Ruby 3.2.4p170 (2024-04-23 revision af471c0e0127eea0cafa6f308c0425bbfab0acf5)
dir.c
1/**********************************************************************
2
3 dir.c -
4
5 $Author$
6 created at: Wed Jan 5 09:51:01 JST 1994
7
8 Copyright (C) 1993-2007 Yukihiro Matsumoto
9 Copyright (C) 2000 Network Applied Communication Laboratory, Inc.
10 Copyright (C) 2000 Information-technology Promotion Agency, Japan
11
12**********************************************************************/
13
14#include "ruby/internal/config.h"
15
16#include <ctype.h>
17#include <errno.h>
18#include <sys/types.h>
19#include <sys/stat.h>
20
21#ifdef HAVE_UNISTD_H
22#include <unistd.h>
23#endif
24
25#ifndef O_CLOEXEC
26# define O_CLOEXEC 0
27#endif
28
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
37
38#if USE_OPENDIR_AT
39# include <fcntl.h>
40#endif
41
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
66
67#ifndef HAVE_STDLIB_H
68char *getenv();
69#endif
70
71#ifndef HAVE_STRING_H
72char *strchr(char*,char);
73#endif
74
75#ifdef HAVE_SYS_ATTR_H
76#include <sys/attr.h>
77#endif
78
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 */
83
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
95
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
104
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"
122
123#ifndef AT_FDCWD
124# define AT_FDCWD -1
125#endif
126
127#define vm_initialized rb_cThread
128
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
144
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
151static inline int
152need_normalization(DIR *dirp, const char *path)
153{
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;
168 }
169 }
170# endif
171 return FALSE;
172}
173
174static inline int
175has_nonascii(const char *ptr, size_t len)
176{
177 while (len > 0) {
178 if (!ISASCII(*ptr)) return 1;
179 ptr++;
180 --len;
181 }
182 return 0;
183}
184
185# define IF_NORMALIZE_UTF8PATH(something) something
186#else
187# define IF_NORMALIZE_UTF8PATH(something) /* nothing */
188#endif
189
190#if defined(IFTODT) && defined(DT_UNKNOWN)
191# define EMULATE_IFTODT 0
192#else
193# define EMULATE_IFTODT 1
194#endif
195
196#if EMULATE_IFTODT
197# define IFTODT(m) (((m) & S_IFMT) / ((~S_IFMT & (S_IFMT-1)) + 1))
198#endif
199
200typedef 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;
215
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
233
234#define FNM_NOMATCH 1
235#define FNM_ERROR 2
236
237# define Next(p, e, enc) ((p)+ rb_enc_mbclen((p), (e), (enc)))
238# define Inc(p, e, enc) ((p) = Next((p), (e), (enc)))
239
240static char *
241bracket(
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)
248{
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;
254
255 if (p >= pend) return NULL;
256 if (*p == '!' || *p == '^') {
257 not = 1;
258 p++;
259 }
260
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;
282 }
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;
291 }
292 else {
293 if (ok) continue;
294 if (r <= (send-s) && memcmp(t1, s, r) == 0) {
295 ok = 1;
296 continue;
297 }
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;
302 }
303 ok = 1;
304 }
305
306 return ok == not ? NULL : (char *)p + 1;
307}
308
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.
313*/
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);
317
318static int
319fnmatch_helper(
320 const char **pcur, /* pattern */
321 const char **scur, /* string */
322 int flags,
323 rb_encoding *enc)
324{
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;
329
330 const char *ptmp = 0;
331 const char *stmp = 0;
332
333 const char *p = *pcur;
334 const char *pend = p + strlen(p);
335 const char *s = *scur;
336 const char *send = s + strlen(s);
337
338 int r;
339
340 if (period && *s == '.' && *UNESCAPE(p) != '.') /* leading period */
341 RETURN(FNM_NOMATCH);
342
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);
350 }
351 if (ISEND(s))
352 RETURN(FNM_NOMATCH);
353 ptmp = p;
354 stmp = s;
355 continue;
356
357 case '?':
358 if (ISEND(s))
359 RETURN(FNM_NOMATCH);
360 p++;
361 Inc(s, send, enc);
362 continue;
363
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;
372 }
373 goto failed;
374 }
375 }
376
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;
390 }
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;
398
399 failed: /* try next '*' position */
400 if (ptmp && stmp) {
401 p = ptmp;
402 Inc(stmp, send, enc); /* !ISEND(*stmp) */
403 s = stmp;
404 continue;
405 }
406 RETURN(FNM_NOMATCH);
407 }
408}
409
410static int
411fnmatch(
412 const char *pattern,
413 rb_encoding *enc,
414 const char *string,
415 int flags)
416{
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;
422
423 const char *ptmp = 0;
424 const char *stmp = 0;
425
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;
432 }
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;
439 }
440 if (!*p && !*s)
441 return 0;
442 }
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;
451 }
452 }
453 return FNM_NOMATCH;
454 }
455 }
456 else
457 return fnmatch_helper(&p, &s, flags, enc);
459
461
462struct dir_data {
463 DIR *dir;
464 const VALUE path;
465 rb_encoding *enc;
466};
467
468static void
469dir_mark(void *ptr)
470{
471 struct dir_data *dir = ptr;
472 rb_gc_mark(dir->path);
473}
474
475static void
476dir_free(void *ptr)
477{
478 struct dir_data *dir = ptr;
479
480 if (dir->dir) closedir(dir->dir);
481 xfree(dir);
482}
483
484static size_t
485dir_memsize(const void *ptr)
486{
487 return sizeof(struct dir_data);
488}
489
490static const rb_data_type_t dir_data_type = {
491 "dir",
492 {dir_mark, dir_free, dir_memsize,},
493 0, 0, RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_FREE_IMMEDIATELY
494};
495
496static VALUE dir_close(VALUE);
497
498static VALUE
499dir_s_alloc(VALUE klass)
500{
501 struct dir_data *dirp;
502 VALUE obj = TypedData_Make_Struct(klass, struct dir_data, &dir_data_type, dirp);
503
504 dirp->dir = NULL;
505 RB_OBJ_WRITE(obj, &dirp->path, Qnil);
506 dirp->enc = NULL;
507
508 return obj;
509}
510
511static void *
512nogvl_opendir(void *ptr)
513{
514 const char *path = ptr;
515
516 return (void *)opendir(path);
517}
518
519static DIR *
520opendir_without_gvl(const char *path)
521{
522 if (vm_initialized) {
523 union { const void *in; void *out; } u;
524
525 u.in = path;
526
527 return rb_thread_call_without_gvl(nogvl_opendir, u.out, RUBY_UBF_IO, 0);
528 }
529 else
530 return opendir(path);
531}
532
533static VALUE
534dir_initialize(rb_execution_context_t *ec, VALUE dir, VALUE dirname, VALUE enc)
535{
536 struct dir_data *dp;
537 VALUE orig;
538 const char *path;
539 rb_encoding *fsenc = NIL_P(enc) ? rb_filesystem_encoding() : rb_to_encoding(enc);
540
541 FilePathValue(dirname);
542 orig = rb_str_dup_frozen(dirname);
543 dirname = rb_str_encode_ospath(dirname);
544 dirname = rb_str_dup_frozen(dirname);
545
546 TypedData_Get_Struct(dir, struct dir_data, &dir_data_type, dp);
547 if (dp->dir) closedir(dp->dir);
548 dp->dir = NULL;
549 RB_OBJ_WRITE(dir, &dp->path, Qnil);
550 dp->enc = fsenc;
551 path = RSTRING_PTR(dirname);
552 dp->dir = opendir_without_gvl(path);
553 if (dp->dir == NULL) {
554 int e = errno;
555 if (rb_gc_for_fd(e)) {
556 dp->dir = opendir_without_gvl(path);
557 }
558#ifdef HAVE_GETATTRLIST
559 else if (e == EIO) {
560 u_int32_t attrbuf[1];
561 struct attrlist al = {ATTR_BIT_MAP_COUNT, 0};
562 if (getattrlist(path, &al, attrbuf, sizeof(attrbuf), FSOPT_NOFOLLOW) == 0) {
563 dp->dir = opendir_without_gvl(path);
564 }
565 }
566#endif
567 if (dp->dir == NULL) {
568 RB_GC_GUARD(dirname);
569 rb_syserr_fail_path(e, orig);
570 }
571 }
572 RB_OBJ_WRITE(dir, &dp->path, orig);
573
574 return dir;
575}
576
577static VALUE
578dir_s_open(rb_execution_context_t *ec, VALUE klass, VALUE dirname, VALUE enc)
579{
580 struct dir_data *dp;
581 VALUE dir = TypedData_Make_Struct(klass, struct dir_data, &dir_data_type, dp);
582
583 dir_initialize(ec, dir, dirname, enc);
584
585 return dir;
586}
587
588static VALUE
589dir_s_close(rb_execution_context_t *ec, VALUE klass, VALUE dir)
590{
591 return dir_close(dir);
592}
593
594NORETURN(static void dir_closed(void));
595
596static void
597dir_closed(void)
598{
599 rb_raise(rb_eIOError, "closed directory");
600}
601
602static struct dir_data *
603dir_get(VALUE dir)
604{
605 rb_check_frozen(dir);
606 return rb_check_typeddata(dir, &dir_data_type);
607}
608
609static struct dir_data *
610dir_check(VALUE dir)
611{
612 struct dir_data *dirp = dir_get(dir);
613 if (!dirp->dir) dir_closed();
614 return dirp;
615}
616
617#define GetDIR(obj, dirp) ((dirp) = dir_check(obj))
618
619
620/*
621 * call-seq:
622 * dir.inspect -> string
623 *
624 * Return a string describing this Dir object.
625 */
626static VALUE
627dir_inspect(VALUE dir)
628{
629 struct dir_data *dirp;
630
631 TypedData_Get_Struct(dir, struct dir_data, &dir_data_type, dirp);
632 if (!NIL_P(dirp->path)) {
633 VALUE str = rb_str_new_cstr("#<");
635 rb_str_cat2(str, ":");
636 rb_str_append(str, dirp->path);
637 rb_str_cat2(str, ">");
638 return str;
639 }
640 return rb_funcallv(dir, idTo_s, 0, 0);
641}
642
643/* Workaround for Solaris 10 that does not have dirfd.
644 Note: Solaris 11 (POSIX.1-2008 compliant) has dirfd(3C).
645 */
646#if defined(__sun) && !defined(HAVE_DIRFD)
647# if defined(HAVE_DIR_D_FD)
648# define dirfd(x) ((x)->d_fd)
649# define HAVE_DIRFD 1
650# elif defined(HAVE_DIR_DD_FD)
651# define dirfd(x) ((x)->dd_fd)
652# define HAVE_DIRFD 1
653# endif
654#endif
655
656#ifdef HAVE_DIRFD
657/*
658 * call-seq:
659 * dir.fileno -> integer
660 *
661 * Returns the file descriptor used in <em>dir</em>.
662 *
663 * d = Dir.new("..")
664 * d.fileno #=> 8
665 *
666 * This method uses dirfd() function defined by POSIX 2008.
667 * NotImplementedError is raised on other platforms, such as Windows,
668 * which doesn't provide the function.
669 *
670 */
671static VALUE
672dir_fileno(VALUE dir)
673{
674 struct dir_data *dirp;
675 int fd;
676
677 GetDIR(dir, dirp);
678 fd = dirfd(dirp->dir);
679 if (fd == -1)
680 rb_sys_fail("dirfd");
681 return INT2NUM(fd);
682}
683#else
684#define dir_fileno rb_f_notimplement
685#endif
686
687/*
688 * call-seq:
689 * dir.path -> string or nil
690 * dir.to_path -> string or nil
691 *
692 * Returns the path parameter passed to <em>dir</em>'s constructor.
693 *
694 * d = Dir.new("..")
695 * d.path #=> ".."
696 */
697static VALUE
698dir_path(VALUE dir)
699{
700 struct dir_data *dirp;
701
702 TypedData_Get_Struct(dir, struct dir_data, &dir_data_type, dirp);
703 if (NIL_P(dirp->path)) return Qnil;
704 return rb_str_dup(dirp->path);
705}
706
707#if defined _WIN32
708static int
709fundamental_encoding_p(rb_encoding *enc)
710{
711 switch (rb_enc_to_index(enc)) {
712 case ENCINDEX_ASCII_8BIT:
713 case ENCINDEX_US_ASCII:
714 case ENCINDEX_UTF_8:
715 return TRUE;
716 default:
717 return FALSE;
718 }
719}
720# define READDIR(dir, enc) rb_w32_readdir((dir), (enc))
721#else
722# define READDIR(dir, enc) readdir((dir))
723#endif
724
725/* safe to use without GVL */
726static int
727to_be_skipped(const struct dirent *dp)
728{
729 const char *name = dp->d_name;
730 if (name[0] != '.') return FALSE;
731#ifdef HAVE_DIRENT_NAMLEN
732 switch (NAMLEN(dp)) {
733 case 2:
734 if (name[1] != '.') return FALSE;
735 case 1:
736 return TRUE;
737 default:
738 break;
739 }
740#else
741 if (!name[1]) return TRUE;
742 if (name[1] != '.') return FALSE;
743 if (!name[2]) return TRUE;
744#endif
745 return FALSE;
746}
747
748/*
749 * call-seq:
750 * dir.read -> string or nil
751 *
752 * Reads the next entry from <em>dir</em> and returns it as a string.
753 * Returns <code>nil</code> at the end of the stream.
754 *
755 * d = Dir.new("testdir")
756 * d.read #=> "."
757 * d.read #=> ".."
758 * d.read #=> "config.h"
759 */
760static VALUE
761dir_read(VALUE dir)
762{
763 struct dir_data *dirp;
764 struct dirent *dp;
765
766 GetDIR(dir, dirp);
767 errno = 0;
768 if ((dp = READDIR(dirp->dir, dirp->enc)) != NULL) {
769 return rb_external_str_new_with_enc(dp->d_name, NAMLEN(dp), dirp->enc);
770 }
771 else {
772 int e = errno;
773 if (e != 0) rb_syserr_fail(e, 0);
774 return Qnil; /* end of stream */
775 }
776}
777
778static VALUE dir_each_entry(VALUE, VALUE (*)(VALUE, VALUE), VALUE, int);
779
780static VALUE
781dir_yield(VALUE arg, VALUE path)
782{
783 return rb_yield(path);
784}
785
786/*
787 * call-seq:
788 * dir.each { |filename| block } -> dir
789 * dir.each -> an_enumerator
790 *
791 * Calls the block once for each entry in this directory, passing the
792 * filename of each entry as a parameter to the block.
793 *
794 * If no block is given, an enumerator is returned instead.
795 *
796 * d = Dir.new("testdir")
797 * d.each {|x| puts "Got #{x}" }
798 *
799 * <em>produces:</em>
800 *
801 * Got .
802 * Got ..
803 * Got config.h
804 * Got main.rb
805 */
806static VALUE
807dir_each(VALUE dir)
808{
809 RETURN_ENUMERATOR(dir, 0, 0);
810 return dir_each_entry(dir, dir_yield, Qnil, FALSE);
811}
812
813static VALUE
814dir_each_entry(VALUE dir, VALUE (*each)(VALUE, VALUE), VALUE arg, int children_only)
815{
816 struct dir_data *dirp;
817 struct dirent *dp;
818 IF_NORMALIZE_UTF8PATH(int norm_p);
819
820 GetDIR(dir, dirp);
821 rewinddir(dirp->dir);
822 IF_NORMALIZE_UTF8PATH(norm_p = need_normalization(dirp->dir, RSTRING_PTR(dirp->path)));
823 while ((dp = READDIR(dirp->dir, dirp->enc)) != NULL) {
824 const char *name = dp->d_name;
825 size_t namlen = NAMLEN(dp);
826 VALUE path;
827
828 if (children_only && name[0] == '.') {
829 if (namlen == 1) continue; /* current directory */
830 if (namlen == 2 && name[1] == '.') continue; /* parent directory */
831 }
832#if NORMALIZE_UTF8PATH
833 if (norm_p && has_nonascii(name, namlen) &&
834 !NIL_P(path = rb_str_normalize_ospath(name, namlen))) {
835 path = rb_external_str_with_enc(path, dirp->enc);
836 }
837 else
838#endif
839 path = rb_external_str_new_with_enc(name, namlen, dirp->enc);
840 (*each)(arg, path);
841 }
842 return dir;
843}
844
845#ifdef HAVE_TELLDIR
846/*
847 * call-seq:
848 * dir.pos -> integer
849 * dir.tell -> integer
850 *
851 * Returns the current position in <em>dir</em>. See also Dir#seek.
852 *
853 * d = Dir.new("testdir")
854 * d.tell #=> 0
855 * d.read #=> "."
856 * d.tell #=> 12
857 */
858static VALUE
859dir_tell(VALUE dir)
860{
861 struct dir_data *dirp;
862 long pos;
863
864 GetDIR(dir, dirp);
865 pos = telldir(dirp->dir);
866 return rb_int2inum(pos);
867}
868#else
869#define dir_tell rb_f_notimplement
870#endif
871
872#ifdef HAVE_SEEKDIR
873/*
874 * call-seq:
875 * dir.seek( integer ) -> dir
876 *
877 * Seeks to a particular location in <em>dir</em>. <i>integer</i>
878 * must be a value returned by Dir#tell.
879 *
880 * d = Dir.new("testdir") #=> #<Dir:0x401b3c40>
881 * d.read #=> "."
882 * i = d.tell #=> 12
883 * d.read #=> ".."
884 * d.seek(i) #=> #<Dir:0x401b3c40>
885 * d.read #=> ".."
886 */
887static VALUE
888dir_seek(VALUE dir, VALUE pos)
889{
890 struct dir_data *dirp;
891 long p = NUM2LONG(pos);
892
893 GetDIR(dir, dirp);
894 seekdir(dirp->dir, p);
895 return dir;
896}
897#else
898#define dir_seek rb_f_notimplement
899#endif
900
901#ifdef HAVE_SEEKDIR
902/*
903 * call-seq:
904 * dir.pos = integer -> integer
905 *
906 * Synonym for Dir#seek, but returns the position parameter.
907 *
908 * d = Dir.new("testdir") #=> #<Dir:0x401b3c40>
909 * d.read #=> "."
910 * i = d.pos #=> 12
911 * d.read #=> ".."
912 * d.pos = i #=> 12
913 * d.read #=> ".."
914 */
915static VALUE
916dir_set_pos(VALUE dir, VALUE pos)
917{
918 dir_seek(dir, pos);
919 return pos;
920}
921#else
922#define dir_set_pos rb_f_notimplement
923#endif
924
925/*
926 * call-seq:
927 * dir.rewind -> dir
928 *
929 * Repositions <em>dir</em> to the first entry.
930 *
931 * d = Dir.new("testdir")
932 * d.read #=> "."
933 * d.rewind #=> #<Dir:0x401b3fb0>
934 * d.read #=> "."
935 */
936static VALUE
937dir_rewind(VALUE dir)
938{
939 struct dir_data *dirp;
940
941 GetDIR(dir, dirp);
942 rewinddir(dirp->dir);
943 return dir;
944}
945
946/*
947 * call-seq:
948 * dir.close -> nil
949 *
950 * Closes the directory stream.
951 * Calling this method on closed Dir object is ignored since Ruby 2.3.
952 *
953 * d = Dir.new("testdir")
954 * d.close #=> nil
955 */
956static VALUE
957dir_close(VALUE dir)
958{
959 struct dir_data *dirp;
960
961 dirp = dir_get(dir);
962 if (!dirp->dir) return Qnil;
963 closedir(dirp->dir);
964 dirp->dir = NULL;
965
966 return Qnil;
967}
968
969static void *
970nogvl_chdir(void *ptr)
971{
972 const char *path = ptr;
973
974 return (void *)(VALUE)chdir(path);
975}
976
977static void
978dir_chdir(VALUE path)
979{
980 if (chdir(RSTRING_PTR(path)) < 0)
981 rb_sys_fail_path(path);
982}
983
984static int chdir_blocking = 0;
985static VALUE chdir_thread = Qnil;
986
987struct chdir_data {
988 VALUE old_path, new_path;
989 int done;
990};
991
992static VALUE
993chdir_yield(VALUE v)
994{
995 struct chdir_data *args = (void *)v;
996 dir_chdir(args->new_path);
997 args->done = TRUE;
998 chdir_blocking++;
999 if (NIL_P(chdir_thread))
1000 chdir_thread = rb_thread_current();
1001 return rb_yield(args->new_path);
1002}
1003
1004static VALUE
1005chdir_restore(VALUE v)
1006{
1007 struct chdir_data *args = (void *)v;
1008 if (args->done) {
1009 chdir_blocking--;
1010 if (chdir_blocking == 0)
1011 chdir_thread = Qnil;
1012 dir_chdir(args->old_path);
1013 }
1014 return Qnil;
1015}
1016
1017/*
1018 * call-seq:
1019 * Dir.chdir( [ string] ) -> 0
1020 * Dir.chdir( [ string] ) {| path | block } -> anObject
1021 *
1022 * Changes the current working directory of the process to the given
1023 * string. When called without an argument, changes the directory to
1024 * the value of the environment variable <code>HOME</code>, or
1025 * <code>LOGDIR</code>. SystemCallError (probably Errno::ENOENT) if
1026 * the target directory does not exist.
1027 *
1028 * If a block is given, it is passed the name of the new current
1029 * directory, and the block is executed with that as the current
1030 * directory. The original working directory is restored when the block
1031 * exits. The return value of <code>chdir</code> is the value of the
1032 * block. <code>chdir</code> blocks can be nested, but in a
1033 * multi-threaded program an error will be raised if a thread attempts
1034 * to open a <code>chdir</code> block while another thread has one
1035 * open or a call to <code>chdir</code> without a block occurs inside
1036 * a block passed to <code>chdir</code> (even in the same thread).
1037 *
1038 * Dir.chdir("/var/spool/mail")
1039 * puts Dir.pwd
1040 * Dir.chdir("/tmp") do
1041 * puts Dir.pwd
1042 * Dir.chdir("/usr") do
1043 * puts Dir.pwd
1044 * end
1045 * puts Dir.pwd
1046 * end
1047 * puts Dir.pwd
1048 *
1049 * <em>produces:</em>
1050 *
1051 * /var/spool/mail
1052 * /tmp
1053 * /usr
1054 * /tmp
1055 * /var/spool/mail
1056 */
1057static VALUE
1058dir_s_chdir(int argc, VALUE *argv, VALUE obj)
1059{
1060 VALUE path = Qnil;
1061
1062 if (rb_check_arity(argc, 0, 1) == 1) {
1063 path = rb_str_encode_ospath(rb_get_path(argv[0]));
1064 }
1065 else {
1066 const char *dist = getenv("HOME");
1067 if (!dist) {
1068 dist = getenv("LOGDIR");
1069 if (!dist) rb_raise(rb_eArgError, "HOME/LOGDIR not set");
1070 }
1071 path = rb_str_new2(dist);
1072 }
1073
1074 if (chdir_blocking > 0) {
1075 if (rb_thread_current() != chdir_thread)
1076 rb_raise(rb_eRuntimeError, "conflicting chdir during another chdir block");
1077 if (!rb_block_given_p())
1078 rb_warn("conflicting chdir during another chdir block");
1079 }
1080
1081 if (rb_block_given_p()) {
1082 struct chdir_data args;
1083
1084 args.old_path = rb_str_encode_ospath(rb_dir_getwd());
1085 args.new_path = path;
1086 args.done = FALSE;
1087 return rb_ensure(chdir_yield, (VALUE)&args, chdir_restore, (VALUE)&args);
1088 }
1089 else {
1090 char *p = RSTRING_PTR(path);
1091 int r = (int)(VALUE)rb_thread_call_without_gvl(nogvl_chdir, p,
1092 RUBY_UBF_IO, 0);
1093 if (r < 0)
1094 rb_sys_fail_path(path);
1095 }
1096
1097 return INT2FIX(0);
1098}
1099
1100#ifndef _WIN32
1101VALUE
1102rb_dir_getwd_ospath(void)
1103{
1104 char *path;
1105 VALUE cwd;
1106 VALUE path_guard;
1107
1108#undef RUBY_UNTYPED_DATA_WARNING
1109#define RUBY_UNTYPED_DATA_WARNING 0
1110 path_guard = Data_Wrap_Struct((VALUE)0, NULL, RUBY_DEFAULT_FREE, NULL);
1111 path = ruby_getcwd();
1112 DATA_PTR(path_guard) = path;
1113#ifdef __APPLE__
1114 cwd = rb_str_normalize_ospath(path, strlen(path));
1115#else
1116 cwd = rb_str_new2(path);
1117#endif
1118 DATA_PTR(path_guard) = 0;
1119
1120 xfree(path);
1121 return cwd;
1122}
1123#endif
1125VALUE
1126rb_dir_getwd(void)
1127{
1128 rb_encoding *fs = rb_filesystem_encoding();
1129 int fsenc = rb_enc_to_index(fs);
1130 VALUE cwd = rb_dir_getwd_ospath();
1131
1132 switch (fsenc) {
1133 case ENCINDEX_US_ASCII:
1134 fsenc = ENCINDEX_ASCII_8BIT;
1135 case ENCINDEX_ASCII_8BIT:
1136 break;
1137#if defined _WIN32 || defined __APPLE__
1138 default:
1139 return rb_str_conv_enc(cwd, NULL, fs);
1140#endif
1141 }
1142 return rb_enc_associate_index(cwd, fsenc);
1143}
1144
1145/*
1146 * call-seq:
1147 * Dir.getwd -> string
1148 * Dir.pwd -> string
1149 *
1150 * Returns the path to the current working directory of this process as
1151 * a string.
1152 *
1153 * Dir.chdir("/tmp") #=> 0
1154 * Dir.getwd #=> "/tmp"
1155 * Dir.pwd #=> "/tmp"
1156 */
1157static VALUE
1158dir_s_getwd(VALUE dir)
1159{
1160 return rb_dir_getwd();
1161}
1162
1163static VALUE
1164check_dirname(VALUE dir)
1165{
1166 VALUE d = dir;
1167 char *path, *pend;
1168 long len;
1169 rb_encoding *enc;
1170
1171 FilePathValue(d);
1172 enc = rb_enc_get(d);
1173 RSTRING_GETMEM(d, path, len);
1174 pend = path + len;
1175 pend = rb_enc_path_end(rb_enc_path_skip_prefix(path, pend, enc), pend, enc);
1176 if (pend - path < len) {
1177 d = rb_str_subseq(d, 0, pend - path);
1178 StringValueCStr(d);
1179 }
1180 return rb_str_encode_ospath(d);
1181}
1182
1183#if defined(HAVE_CHROOT)
1184/*
1185 * call-seq:
1186 * Dir.chroot( string ) -> 0
1187 *
1188 * Changes this process's idea of the file system root. Only a
1189 * privileged process may make this call. Not available on all
1190 * platforms. On Unix systems, see <code>chroot(2)</code> for more
1191 * information.
1192 */
1193static VALUE
1194dir_s_chroot(VALUE dir, VALUE path)
1195{
1196 path = check_dirname(path);
1197 if (chroot(RSTRING_PTR(path)) == -1)
1198 rb_sys_fail_path(path);
1199
1200 return INT2FIX(0);
1201}
1202#else
1203#define dir_s_chroot rb_f_notimplement
1204#endif
1205
1206struct mkdir_arg {
1207 const char *path;
1208 mode_t mode;
1209};
1210
1211static void *
1212nogvl_mkdir(void *ptr)
1213{
1214 struct mkdir_arg *m = ptr;
1215
1216 return (void *)(VALUE)mkdir(m->path, m->mode);
1217}
1218
1219/*
1220 * call-seq:
1221 * Dir.mkdir( string [, integer] ) -> 0
1222 *
1223 * Makes a new directory named by <i>string</i>, with permissions
1224 * specified by the optional parameter <i>anInteger</i>. The
1225 * permissions may be modified by the value of File::umask, and are
1226 * ignored on NT. Raises a SystemCallError if the directory cannot be
1227 * created. See also the discussion of permissions in the class
1228 * documentation for File.
1229 *
1230 * Dir.mkdir(File.join(Dir.home, ".foo"), 0700) #=> 0
1231 *
1232 */
1233static VALUE
1234dir_s_mkdir(int argc, VALUE *argv, VALUE obj)
1235{
1236 struct mkdir_arg m;
1237 VALUE path, vmode;
1238 int r;
1239
1240 if (rb_scan_args(argc, argv, "11", &path, &vmode) == 2) {
1241 m.mode = NUM2MODET(vmode);
1242 }
1243 else {
1244 m.mode = 0777;
1245 }
1246
1247 path = check_dirname(path);
1248 m.path = RSTRING_PTR(path);
1249 r = (int)(VALUE)rb_thread_call_without_gvl(nogvl_mkdir, &m, RUBY_UBF_IO, 0);
1250 if (r < 0)
1251 rb_sys_fail_path(path);
1252
1253 return INT2FIX(0);
1254}
1255
1256static void *
1257nogvl_rmdir(void *ptr)
1258{
1259 const char *path = ptr;
1260
1261 return (void *)(VALUE)rmdir(path);
1262}
1263
1264/*
1265 * call-seq:
1266 * Dir.delete( string ) -> 0
1267 * Dir.rmdir( string ) -> 0
1268 * Dir.unlink( string ) -> 0
1269 *
1270 * Deletes the named directory. Raises a subclass of SystemCallError
1271 * if the directory isn't empty.
1272 */
1273static VALUE
1274dir_s_rmdir(VALUE obj, VALUE dir)
1275{
1276 const char *p;
1277 int r;
1278
1279 dir = check_dirname(dir);
1280 p = RSTRING_PTR(dir);
1281 r = (int)(VALUE)rb_thread_call_without_gvl(nogvl_rmdir, (void *)p, RUBY_UBF_IO, 0);
1282 if (r < 0)
1283 rb_sys_fail_path(dir);
1284
1285 return INT2FIX(0);
1287
1288struct warning_args {
1289#ifdef RUBY_FUNCTION_NAME_STRING
1290 const char *func;
1291#endif
1292 const char *mesg;
1293 rb_encoding *enc;
1294};
1295
1296#ifndef RUBY_FUNCTION_NAME_STRING
1297#define sys_enc_warning_in(func, mesg, enc) sys_enc_warning(mesg, enc)
1298#endif
1299
1300static VALUE
1301sys_warning_1(VALUE mesg)
1302{
1303 const struct warning_args *arg = (struct warning_args *)mesg;
1304#ifdef RUBY_FUNCTION_NAME_STRING
1305 rb_sys_enc_warning(arg->enc, "%s: %s", arg->func, arg->mesg);
1306#else
1307 rb_sys_enc_warning(arg->enc, "%s", arg->mesg);
1308#endif
1309 return Qnil;
1310}
1311
1312static void
1313sys_enc_warning_in(const char *func, const char *mesg, rb_encoding *enc)
1314{
1315 struct warning_args arg;
1316#ifdef RUBY_FUNCTION_NAME_STRING
1317 arg.func = func;
1318#endif
1319 arg.mesg = mesg;
1320 arg.enc = enc;
1321 rb_protect(sys_warning_1, (VALUE)&arg, 0);
1322}
1323
1324#define GLOB_VERBOSE (1U << (sizeof(int) * CHAR_BIT - 1))
1325#define sys_warning(val, enc) \
1326 ((flags & GLOB_VERBOSE) ? sys_enc_warning_in(RUBY_FUNCTION_NAME_STRING, (val), (enc)) :(void)0)
1327
1328static inline size_t
1329glob_alloc_size(size_t x, size_t y)
1330{
1331 size_t z;
1332 if (rb_mul_size_overflow(x, y, SSIZE_MAX, &z)) {
1333 rb_memerror(); /* or...? */
1334 }
1335 else {
1336 return z;
1337 }
1338}
1339
1340static inline void *
1341glob_alloc_n(size_t x, size_t y)
1342{
1343 return malloc(glob_alloc_size(x, y));
1344}
1345
1346static inline void *
1347glob_realloc_n(void *p, size_t x, size_t y)
1348{
1349 return realloc(p, glob_alloc_size(x, y));
1350}
1351
1352#define GLOB_ALLOC(type) ((type *)malloc(sizeof(type)))
1353#define GLOB_ALLOC_N(type, n) ((type *)glob_alloc_n(sizeof(type), n))
1354#define GLOB_REALLOC(ptr, size) realloc((ptr), (size))
1355#define GLOB_REALLOC_N(ptr, n) glob_realloc_n(ptr, sizeof(*(ptr)), n)
1356#define GLOB_FREE(ptr) free(ptr)
1357#define GLOB_JUMP_TAG(status) (((status) == -1) ? rb_memerror() : rb_jump_tag(status))
1358
1359/*
1360 * ENOTDIR can be returned by stat(2) if a non-leaf element of the path
1361 * is not a directory.
1362 */
1363ALWAYS_INLINE(static int to_be_ignored(int e));
1364static inline int
1365to_be_ignored(int e)
1366{
1367 return e == ENOENT || e == ENOTDIR;
1368}
1369
1370#ifdef _WIN32
1371#define STAT(p, s) rb_w32_ustati128((p), (s))
1372#undef lstat
1373#define lstat(p, s) rb_w32_ulstati128((p), (s))
1374#else
1375#define STAT(p, s) stat((p), (s))
1376#endif
1378typedef int ruby_glob_errfunc(const char*, VALUE, const void*, int);
1379typedef struct {
1380 ruby_glob_func *match;
1381 ruby_glob_errfunc *error;
1383
1384static const char *
1385at_subpath(int fd, size_t baselen, const char *path)
1386{
1387#if USE_OPENDIR_AT
1388 if (fd != (int)AT_FDCWD && baselen > 0) {
1389 path += baselen;
1390 if (*path == '/') ++path;
1391 }
1392#endif
1393 return *path ? path : ".";
1394}
1395
1396/* System call with warning */
1397static int
1398do_stat(int fd, size_t baselen, const char *path, struct stat *pst, int flags, rb_encoding *enc)
1399{
1400#if USE_OPENDIR_AT
1401 int ret = fstatat(fd, at_subpath(fd, baselen, path), pst, 0);
1402#else
1403 int ret = STAT(path, pst);
1404#endif
1405 if (ret < 0 && !to_be_ignored(errno))
1406 sys_warning(path, enc);
1407
1408 return ret;
1409}
1410
1411#if defined HAVE_LSTAT || defined lstat || USE_OPENDIR_AT
1412static int
1413do_lstat(int fd, size_t baselen, const char *path, struct stat *pst, int flags, rb_encoding *enc)
1414{
1415#if USE_OPENDIR_AT
1416 int ret = fstatat(fd, at_subpath(fd, baselen, path), pst, AT_SYMLINK_NOFOLLOW);
1417#else
1418 int ret = lstat(path, pst);
1419#endif
1420 if (ret < 0 && !to_be_ignored(errno))
1421 sys_warning(path, enc);
1422
1423 return ret;
1424}
1425#else
1426#define do_lstat do_stat
1427#endif
1428
1429struct opendir_at_arg {
1430 int basefd;
1431 const char *path;
1432};
1433
1434static void *
1435with_gvl_gc_for_fd(void *ptr)
1436{
1437 int *e = ptr;
1438
1439 return (void *)RBOOL(rb_gc_for_fd(*e));
1440}
1441
1442static int
1443gc_for_fd_with_gvl(int e)
1444{
1445 if (vm_initialized)
1446 return (int)(VALUE)rb_thread_call_with_gvl(with_gvl_gc_for_fd, &e);
1447 else
1448 return RBOOL(rb_gc_for_fd(e));
1449}
1450
1451static void *
1452nogvl_opendir_at(void *ptr)
1453{
1454 const struct opendir_at_arg *oaa = ptr;
1455 DIR *dirp;
1456
1457#if USE_OPENDIR_AT
1458 const int opendir_flags = (O_RDONLY|O_CLOEXEC|
1459# ifdef O_DIRECTORY
1460 O_DIRECTORY|
1461# endif /* O_DIRECTORY */
1462 0);
1463 int fd = openat(oaa->basefd, oaa->path, opendir_flags);
1464
1465 dirp = fd >= 0 ? fdopendir(fd) : 0;
1466 if (!dirp) {
1467 int e = errno;
1468
1469 switch (gc_for_fd_with_gvl(e)) {
1470 default:
1471 if (fd < 0) fd = openat(oaa->basefd, oaa->path, opendir_flags);
1472 if (fd >= 0) dirp = fdopendir(fd);
1473 if (dirp) return dirp;
1474
1475 e = errno;
1476 /* fallthrough*/
1477 case 0:
1478 if (fd >= 0) close(fd);
1479 errno = e;
1480 }
1481 }
1482#else /* !USE_OPENDIR_AT */
1483 dirp = opendir(oaa->path);
1484 if (!dirp && gc_for_fd_with_gvl(errno))
1485 dirp = opendir(oaa->path);
1486#endif /* !USE_OPENDIR_AT */
1487
1488 return dirp;
1489}
1490
1491static DIR *
1492opendir_at(int basefd, const char *path)
1493{
1494 struct opendir_at_arg oaa;
1495
1496 oaa.basefd = basefd;
1497 oaa.path = path;
1498
1499 if (vm_initialized)
1500 return rb_thread_call_without_gvl(nogvl_opendir_at, &oaa, RUBY_UBF_IO, 0);
1501 else
1502 return nogvl_opendir_at(&oaa);
1503}
1504
1505static DIR *
1506do_opendir(const int basefd, size_t baselen, const char *path, int flags, rb_encoding *enc,
1507 ruby_glob_errfunc *errfunc, VALUE arg, int *status)
1508{
1509 DIR *dirp;
1510#ifdef _WIN32
1511 VALUE tmp = 0;
1512 if (!fundamental_encoding_p(enc)) {
1513 tmp = rb_enc_str_new(path, strlen(path), enc);
1514 tmp = rb_str_encode_ospath(tmp);
1515 path = RSTRING_PTR(tmp);
1516 }
1517#endif
1518 dirp = opendir_at(basefd, at_subpath(basefd, baselen, path));
1519 if (!dirp) {
1520 int e = errno;
1521
1522 *status = 0;
1523 if (!to_be_ignored(e)) {
1524 if (errfunc) {
1525 *status = (*errfunc)(path, arg, enc, e);
1526 }
1527 else {
1528 sys_warning(path, enc);
1529 }
1530 }
1531 }
1532#ifdef _WIN32
1533 if (tmp) rb_str_resize(tmp, 0); /* GC guard */
1534#endif
1535
1536 return dirp;
1537}
1538
1539/* Globing pattern */
1540enum glob_pattern_type { PLAIN, ALPHA, BRACE, MAGICAL, RECURSIVE, MATCH_ALL, MATCH_DIR };
1541
1542/* Return nonzero if S has any special globbing chars in it. */
1543static enum glob_pattern_type
1544has_magic(const char *p, const char *pend, int flags, rb_encoding *enc)
1545{
1546 const int escape = !(flags & FNM_NOESCAPE);
1547 int hasalpha = 0;
1548 int hasmagical = 0;
1549
1550 register char c;
1551
1552 while (p < pend && (c = *p++) != 0) {
1553 switch (c) {
1554 case '{':
1555 return BRACE;
1556
1557 case '*':
1558 case '?':
1559 case '[':
1560 hasmagical = 1;
1561 break;
1562
1563 case '\\':
1564 if (escape && p++ >= pend)
1565 continue;
1566 break;
1567
1568#ifdef _WIN32
1569 case '.':
1570 break;
1571
1572 case '~':
1573 hasalpha = 1;
1574 break;
1575#endif
1576 default:
1577 if (IS_WIN32 || ISALPHA(c)) {
1578 hasalpha = 1;
1579 }
1580 break;
1581 }
1582
1583 p = Next(p-1, pend, enc);
1584 }
1585
1586 return hasmagical ? MAGICAL : hasalpha ? ALPHA : PLAIN;
1587}
1588
1589/* Find separator in globbing pattern. */
1590static char *
1591find_dirsep(const char *p, const char *pend, int flags, rb_encoding *enc)
1592{
1593 const int escape = !(flags & FNM_NOESCAPE);
1594
1595 register char c;
1596 int open = 0;
1597
1598 while ((c = *p++) != 0) {
1599 switch (c) {
1600 case '[':
1601 open = 1;
1602 continue;
1603 case ']':
1604 open = 0;
1605 continue;
1606
1607 case '{':
1608 open = 1;
1609 continue;
1610 case '}':
1611 open = 0;
1612 continue;
1613
1614 case '/':
1615 if (!open)
1616 return (char *)p-1;
1617 continue;
1618
1619 case '\\':
1620 if (escape && !(c = *p++))
1621 return (char *)p-1;
1622 continue;
1623 }
1624
1625 p = Next(p-1, pend, enc);
1626 }
1627
1628 return (char *)p-1;
1629}
1630
1631/* Remove escaping backslashes */
1632static char *
1633remove_backslashes(char *p, register const char *pend, rb_encoding *enc)
1634{
1635 char *t = p;
1636 char *s = p;
1637
1638 while (*p) {
1639 if (*p == '\\') {
1640 if (t != s)
1641 memmove(t, s, p - s);
1642 t += p - s;
1643 s = ++p;
1644 if (!*p) break;
1645 }
1646 Inc(p, pend, enc);
1647 }
1648
1649 while (*p++);
1650
1651 if (t != s)
1652 memmove(t, s, p - s); /* move '\0' too */
1653
1654 return p;
1656
1657struct glob_pattern {
1658 char *str;
1659 enum glob_pattern_type type;
1660 struct glob_pattern *next;
1661};
1662
1663static void glob_free_pattern(struct glob_pattern *list);
1664
1665static struct glob_pattern *
1666glob_make_pattern(const char *p, const char *e, int flags, rb_encoding *enc)
1667{
1668 struct glob_pattern *list, *tmp, **tail = &list;
1669 int dirsep = 0; /* pattern is terminated with '/' */
1670 int recursive = 0;
1671
1672 while (p < e && *p) {
1673 tmp = GLOB_ALLOC(struct glob_pattern);
1674 if (!tmp) goto error;
1675 if (p + 2 < e && p[0] == '*' && p[1] == '*' && p[2] == '/') {
1676 /* fold continuous RECURSIVEs (needed in glob_helper) */
1677 do { p += 3; while (*p == '/') p++; } while (p[0] == '*' && p[1] == '*' && p[2] == '/');
1678 tmp->type = RECURSIVE;
1679 tmp->str = 0;
1680 dirsep = 1;
1681 recursive = 1;
1682 }
1683 else {
1684 const char *m = find_dirsep(p, e, flags, enc);
1685 const enum glob_pattern_type magic = has_magic(p, m, flags, enc);
1686 const enum glob_pattern_type non_magic = (USE_NAME_ON_FS || FNM_SYSCASE) ? PLAIN : ALPHA;
1687 char *buf;
1688
1689 if (!(FNM_SYSCASE || magic > non_magic) && !recursive && *m) {
1690 const char *m2;
1691 while (has_magic(m+1, m2 = find_dirsep(m+1, e, flags, enc), flags, enc) <= non_magic &&
1692 *m2) {
1693 m = m2;
1694 }
1695 }
1696 buf = GLOB_ALLOC_N(char, m-p+1);
1697 if (!buf) {
1698 GLOB_FREE(tmp);
1699 goto error;
1700 }
1701 memcpy(buf, p, m-p);
1702 buf[m-p] = '\0';
1703 tmp->type = magic > MAGICAL ? MAGICAL : magic > non_magic ? magic : PLAIN;
1704 tmp->str = buf;
1705 if (*m) {
1706 dirsep = 1;
1707 p = m + 1;
1708 }
1709 else {
1710 dirsep = 0;
1711 p = m;
1712 }
1713 }
1714 *tail = tmp;
1715 tail = &tmp->next;
1716 }
1717
1718 tmp = GLOB_ALLOC(struct glob_pattern);
1719 if (!tmp) {
1720 goto error;
1721 }
1722 tmp->type = dirsep ? MATCH_DIR : MATCH_ALL;
1723 tmp->str = 0;
1724 *tail = tmp;
1725 tmp->next = 0;
1726
1727 return list;
1728
1729 error:
1730 *tail = 0;
1731 glob_free_pattern(list);
1732 return 0;
1733}
1734
1735static void
1736glob_free_pattern(struct glob_pattern *list)
1737{
1738 while (list) {
1739 struct glob_pattern *tmp = list;
1740 list = list->next;
1741 if (tmp->str)
1742 GLOB_FREE(tmp->str);
1743 GLOB_FREE(tmp);
1744 }
1745}
1746
1747static char *
1748join_path(const char *path, size_t len, int dirsep, const char *name, size_t namlen)
1749{
1750 char *buf = GLOB_ALLOC_N(char, len+namlen+(dirsep?1:0)+1);
1751
1752 if (!buf) return 0;
1753 memcpy(buf, path, len);
1754 if (dirsep) {
1755 buf[len++] = '/';
1756 }
1757 memcpy(buf+len, name, namlen);
1758 buf[len+namlen] = '\0';
1759 return buf;
1760}
1761
1762#ifdef HAVE_GETATTRLIST
1763# if defined HAVE_FGETATTRLIST
1764# define is_case_sensitive(dirp, path) is_case_sensitive(dirp)
1765# else
1766# define is_case_sensitive(dirp, path) is_case_sensitive(path)
1767# endif
1768static int
1769is_case_sensitive(DIR *dirp, const char *path)
1770{
1771 struct {
1772 u_int32_t length;
1773 vol_capabilities_attr_t cap[1];
1774 } __attribute__((aligned(4), packed)) attrbuf[1];
1775 struct attrlist al = {ATTR_BIT_MAP_COUNT, 0, 0, ATTR_VOL_INFO|ATTR_VOL_CAPABILITIES};
1776 const vol_capabilities_attr_t *const cap = attrbuf[0].cap;
1777 const int idx = VOL_CAPABILITIES_FORMAT;
1778 const uint32_t mask = VOL_CAP_FMT_CASE_SENSITIVE;
1779
1780# if defined HAVE_FGETATTRLIST
1781 if (fgetattrlist(dirfd(dirp), &al, attrbuf, sizeof(attrbuf), FSOPT_NOFOLLOW))
1782 return -1;
1783# else
1784 if (getattrlist(path, &al, attrbuf, sizeof(attrbuf), FSOPT_NOFOLLOW))
1785 return -1;
1786# endif
1787 if (!(cap->valid[idx] & mask))
1788 return -1;
1789 return (cap->capabilities[idx] & mask) != 0;
1790}
1791
1792static char *
1793replace_real_basename(char *path, long base, rb_encoding *enc, int norm_p, int flags, rb_pathtype_t *type)
1794{
1795 struct {
1796 u_int32_t length;
1797 attrreference_t ref[1];
1798 fsobj_type_t objtype;
1799 char path[MAXPATHLEN * 3];
1800 } __attribute__((aligned(4), packed)) attrbuf[1];
1801 struct attrlist al = {ATTR_BIT_MAP_COUNT, 0, ATTR_CMN_NAME|ATTR_CMN_OBJTYPE};
1802 const attrreference_t *const ar = attrbuf[0].ref;
1803 const char *name;
1804 long len;
1805 char *tmp;
1806 IF_NORMALIZE_UTF8PATH(VALUE utf8str = Qnil);
1807
1808 *type = path_noent;
1809 if (getattrlist(path, &al, attrbuf, sizeof(attrbuf), FSOPT_NOFOLLOW)) {
1810 if (!to_be_ignored(errno))
1811 sys_warning(path, enc);
1812 return path;
1813 }
1814
1815 switch (attrbuf[0].objtype) {
1816 case VREG: *type = path_regular; break;
1817 case VDIR: *type = path_directory; break;
1818 case VLNK: *type = path_symlink; break;
1819 default: *type = path_exist; break;
1820 }
1821 name = (char *)ar + ar->attr_dataoffset;
1822 len = (long)ar->attr_length - 1;
1823 if (name + len > (char *)attrbuf + sizeof(attrbuf))
1824 return path;
1825
1826# if NORMALIZE_UTF8PATH
1827 if (norm_p && has_nonascii(name, len)) {
1828 if (!NIL_P(utf8str = rb_str_normalize_ospath(name, len))) {
1829 RSTRING_GETMEM(utf8str, name, len);
1830 }
1831 }
1832# endif
1833
1834 tmp = GLOB_REALLOC(path, base + len + 1);
1835 if (tmp) {
1836 path = tmp;
1837 memcpy(path + base, name, len);
1838 path[base + len] = '\0';
1839 }
1840 IF_NORMALIZE_UTF8PATH(if (!NIL_P(utf8str)) rb_str_resize(utf8str, 0));
1841 return path;
1842}
1843#elif defined _WIN32
1844VALUE rb_w32_conv_from_wchar(const WCHAR *wstr, rb_encoding *enc);
1845int rb_w32_reparse_symlink_p(const WCHAR *path);
1846
1847static char *
1848replace_real_basename(char *path, long base, rb_encoding *enc, int norm_p, int flags, rb_pathtype_t *type)
1849{
1850 char *plainname = path;
1851 volatile VALUE tmp = 0;
1852 WIN32_FIND_DATAW fd;
1853 WIN32_FILE_ATTRIBUTE_DATA fa;
1854 WCHAR *wplain;
1855 HANDLE h = INVALID_HANDLE_VALUE;
1856 long wlen;
1857 int e = 0;
1858 if (!fundamental_encoding_p(enc)) {
1859 tmp = rb_enc_str_new_cstr(plainname, enc);
1860 tmp = rb_str_encode_ospath(tmp);
1861 plainname = RSTRING_PTR(tmp);
1862 }
1863 wplain = rb_w32_mbstr_to_wstr(CP_UTF8, plainname, -1, &wlen);
1864 if (tmp) rb_str_resize(tmp, 0);
1865 if (!wplain) return path;
1866 if (GetFileAttributesExW(wplain, GetFileExInfoStandard, &fa)) {
1867 h = FindFirstFileW(wplain, &fd);
1868 e = rb_w32_map_errno(GetLastError());
1869 }
1870 if (fa.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
1871 if (!rb_w32_reparse_symlink_p(wplain))
1872 fa.dwFileAttributes &= ~FILE_ATTRIBUTE_REPARSE_POINT;
1873 }
1874 free(wplain);
1875 if (h == INVALID_HANDLE_VALUE) {
1876 *type = path_noent;
1877 if (e && !to_be_ignored(e)) {
1878 errno = e;
1879 sys_warning(path, enc);
1880 }
1881 return path;
1882 }
1883 FindClose(h);
1884 *type =
1885 (fa.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) ? path_symlink :
1886 (fa.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? path_directory :
1887 path_regular;
1888 if (tmp) {
1889 char *buf;
1890 tmp = rb_w32_conv_from_wchar(fd.cFileName, enc);
1891 wlen = RSTRING_LEN(tmp);
1892 buf = GLOB_REALLOC(path, base + wlen + 1);
1893 if (buf) {
1894 path = buf;
1895 memcpy(path + base, RSTRING_PTR(tmp), wlen);
1896 path[base + wlen] = 0;
1897 }
1898 rb_str_resize(tmp, 0);
1899 }
1900 else {
1901 char *utf8filename;
1902 wlen = WideCharToMultiByte(CP_UTF8, 0, fd.cFileName, -1, NULL, 0, NULL, NULL);
1903 utf8filename = GLOB_REALLOC(0, wlen);
1904 if (utf8filename) {
1905 char *buf;
1906 WideCharToMultiByte(CP_UTF8, 0, fd.cFileName, -1, utf8filename, wlen, NULL, NULL);
1907 buf = GLOB_REALLOC(path, base + wlen + 1);
1908 if (buf) {
1909 path = buf;
1910 memcpy(path + base, utf8filename, wlen);
1911 path[base + wlen] = 0;
1912 }
1913 GLOB_FREE(utf8filename);
1914 }
1915 }
1916 return path;
1917}
1918#elif USE_NAME_ON_FS == USE_NAME_ON_FS_REAL_BASENAME
1919# error not implemented
1920#endif
1921
1922#ifndef S_ISDIR
1923# define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
1924#endif
1925
1926#ifndef S_ISLNK
1927# ifndef S_IFLNK
1928# define S_ISLNK(m) (0)
1929# else
1930# define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK)
1931# endif
1932#endif
1933
1934struct glob_args {
1935 void (*func)(const char *, VALUE, void *);
1936 const char *path;
1937 const char *base;
1938 size_t baselen;
1939 VALUE value;
1940 rb_encoding *enc;
1941};
1942
1943#define glob_call_func(func, path, arg, enc) (*(func))((path), (arg), (void *)(enc))
1944
1945static VALUE
1946glob_func_caller(VALUE val)
1947{
1948 struct glob_args *args = (struct glob_args *)val;
1949
1950 glob_call_func(args->func, args->path, args->value, args->enc);
1951 return Qnil;
1953
1954struct glob_error_args {
1955 const char *path;
1956 rb_encoding *enc;
1957 int error;
1958};
1959
1960static VALUE
1961glob_func_warning(VALUE val)
1962{
1963 struct glob_error_args *arg = (struct glob_error_args *)val;
1964 rb_syserr_enc_warning(arg->error, arg->enc, "%s", arg->path);
1965 return Qnil;
1966}
1967
1968#if 0
1969static int
1970rb_glob_warning(const char *path, VALUE a, const void *enc, int error)
1971{
1972 int status;
1973 struct glob_error_args args;
1974
1975 args.path = path;
1976 args.enc = enc;
1977 args.error = error;
1978 rb_protect(glob_func_warning, (VALUE)&args, &status);
1979 return status;
1980}
1981#endif
1982
1983NORETURN(static VALUE glob_func_error(VALUE val));
1984
1985static VALUE
1986glob_func_error(VALUE val)
1987{
1988 struct glob_error_args *arg = (struct glob_error_args *)val;
1989 VALUE path = rb_enc_str_new_cstr(arg->path, arg->enc);
1990 rb_syserr_fail_str(arg->error, path);
1992}
1993
1994static int
1995rb_glob_error(const char *path, VALUE a, const void *enc, int error)
1996{
1997 int status;
1998 struct glob_error_args args;
1999 VALUE (*errfunc)(VALUE) = glob_func_error;
2000
2001 switch (error) {
2002 case EACCES:
2003#ifdef ENOTCAPABLE
2004 case ENOTCAPABLE:
2005#endif
2006 errfunc = glob_func_warning;
2007 }
2008 args.path = path;
2009 args.enc = enc;
2010 args.error = error;
2011 rb_protect(errfunc, (VALUE)&args, &status);
2012 return status;
2014
2015typedef struct rb_dirent {
2016 long d_namlen;
2017 const char *d_name;
2018#ifdef _WIN32
2019 const char *d_altname;
2020#endif
2021 uint8_t d_type;
2022} rb_dirent_t;
2023
2024static inline int
2025dirent_match(const char *pat, rb_encoding *enc, const char *name, const rb_dirent_t *dp, int flags)
2026{
2027 if (fnmatch(pat, enc, name, flags) == 0) return 1;
2028#ifdef _WIN32
2029 if (dp->d_altname && (flags & FNM_SHORTNAME)) {
2030 if (fnmatch(pat, enc, dp->d_altname, flags) == 0) return 1;
2031 }
2032#endif
2033 return 0;
2035
2036struct push_glob_args {
2037 int fd;
2038 const char *path;
2039 size_t baselen;
2040 size_t namelen;
2041 int dirsep; /* '/' should be placed before appending child entry's name to 'path'. */
2042 rb_pathtype_t pathtype; /* type of 'path' */
2043 int flags;
2044 const ruby_glob_funcs_t *funcs;
2045 VALUE arg;
2047
2048struct dirent_brace_args {
2049 const char *name;
2050 const rb_dirent_t *dp;
2051 int flags;
2052};
2053
2054static int
2055dirent_match_brace(const char *pattern, VALUE val, void *enc)
2056{
2057 struct dirent_brace_args *arg = (struct dirent_brace_args *)val;
2058
2059 return dirent_match(pattern, enc, arg->name, arg->dp, arg->flags);
2060}
2061
2062/* join paths from pattern list of glob_make_pattern() */
2063static char*
2064join_path_from_pattern(struct glob_pattern **beg)
2065{
2066 struct glob_pattern *p;
2067 char *path = NULL;
2068 size_t path_len = 0;
2069
2070 for (p = *beg; p; p = p->next) {
2071 const char *str;
2072 switch (p->type) {
2073 case RECURSIVE:
2074 str = "**";
2075 break;
2076 case MATCH_DIR:
2077 /* append last slash */
2078 str = "";
2079 break;
2080 default:
2081 str = p->str;
2082 if (!str) continue;
2083 }
2084 if (!path) {
2085 path_len = strlen(str);
2086 path = GLOB_ALLOC_N(char, path_len + 1);
2087 if (path) {
2088 memcpy(path, str, path_len);
2089 path[path_len] = '\0';
2090 }
2091 }
2092 else {
2093 size_t len = strlen(str);
2094 char *tmp;
2095 tmp = GLOB_REALLOC(path, path_len + len + 2);
2096 if (tmp) {
2097 path = tmp;
2098 path[path_len++] = '/';
2099 memcpy(path + path_len, str, len);
2100 path_len += len;
2101 path[path_len] = '\0';
2102 }
2103 }
2104 }
2105 return path;
2106}
2107
2108static int push_caller(const char *path, VALUE val, void *enc);
2109
2110static int ruby_brace_expand(const char *str, int flags, ruby_glob_func *func, VALUE arg,
2111 rb_encoding *enc, VALUE var);
2112
2113static const size_t rb_dirent_name_offset =
2114 offsetof(rb_dirent_t, d_type) + sizeof(uint8_t);
2115
2116static rb_dirent_t *
2117dirent_copy(const struct dirent *dp, rb_dirent_t *rdp)
2118{
2119 if (!dp) return NULL;
2120 size_t namlen = NAMLEN(dp);
2121 const size_t altlen =
2122#ifdef _WIN32
2123 dp->d_altlen ? dp->d_altlen + 1 :
2124#endif
2125 0;
2126 rb_dirent_t *newrdp = rdp;
2127 if (!rdp && !(newrdp = malloc(rb_dirent_name_offset + namlen + 1 + altlen)))
2128 return NULL;
2129 newrdp->d_namlen = namlen;
2130 if (!rdp) {
2131 char *name = (char *)newrdp + rb_dirent_name_offset;
2132 memcpy(name, dp->d_name, namlen);
2133 name[namlen] = '\0';
2134#ifdef _WIN32
2135 newrdp->d_altname = NULL;
2136 if (altlen) {
2137 char *const altname = name + namlen + 1;
2138 memcpy(altname, dp->d_altname, altlen - 1);
2139 altname[altlen - 1] = '\0';
2140 newrdp->d_altname = altname;
2141 }
2142#endif
2143 newrdp->d_name = name;
2144 }
2145 else {
2146 newrdp->d_name = dp->d_name;
2147#ifdef _WIN32
2148 newrdp->d_altname = dp->d_altname;
2149#endif
2150 }
2151#if !EMULATE_IFTODT
2152 newrdp->d_type = dp->d_type;
2153#else
2154 newrdp->d_type = 0;
2155#endif
2156 return newrdp;
2158
2159typedef union {
2160 struct {
2161 DIR *dirp;
2162 rb_dirent_t ent;
2163 } nosort;
2164 struct {
2165 size_t count, idx;
2166 rb_dirent_t **entries;
2167 } sort;
2169
2170static int
2171glob_sort_cmp(const void *a, const void *b, void *e)
2172{
2173 const rb_dirent_t *ent1 = *(void **)a;
2174 const rb_dirent_t *ent2 = *(void **)b;
2175 return strcmp(ent1->d_name, ent2->d_name);
2176}
2177
2178static void
2179glob_dir_finish(ruby_glob_entries_t *ent, int flags)
2180{
2181 if (flags & FNM_GLOB_NOSORT) {
2182 closedir(ent->nosort.dirp);
2183 ent->nosort.dirp = NULL;
2184 }
2185 else if (ent->sort.entries) {
2186 for (size_t i = 0, count = ent->sort.count; i < count;) {
2187 GLOB_FREE(ent->sort.entries[i++]);
2188 }
2189 GLOB_FREE(ent->sort.entries);
2190 ent->sort.entries = NULL;
2191 ent->sort.count = ent->sort.idx = 0;
2192 }
2193}
2194
2195static ruby_glob_entries_t *
2196glob_opendir(ruby_glob_entries_t *ent, DIR *dirp, int flags, rb_encoding *enc)
2197{
2199 if (flags & FNM_GLOB_NOSORT) {
2200 ent->nosort.dirp = dirp;
2201 return ent;
2202 }
2203 else {
2204 void *newp;
2205 struct dirent *dp;
2206 size_t count = 0, capacity = 0;
2207 ent->sort.count = 0;
2208 ent->sort.idx = 0;
2209 ent->sort.entries = 0;
2210#ifdef _WIN32
2211 if ((capacity = dirp->nfiles) > 0) {
2212 if (!(newp = GLOB_ALLOC_N(rb_dirent_t, capacity))) {
2213 closedir(dirp);
2214 return NULL;
2215 }
2216 ent->sort.entries = newp;
2217 }
2218#endif
2219 while ((dp = READDIR(dirp, enc)) != NULL) {
2220 rb_dirent_t *rdp = dirent_copy(dp, NULL);
2221 if (!rdp) {
2222 goto nomem;
2223 }
2224 if (count >= capacity) {
2225 capacity += 256;
2226 if (!(newp = GLOB_REALLOC_N(ent->sort.entries, capacity)))
2227 goto nomem;
2228 ent->sort.entries = newp;
2229 }
2230 ent->sort.entries[count++] = rdp;
2231 ent->sort.count = count;
2232 }
2233 closedir(dirp);
2234 if (count < capacity) {
2235 if (!(newp = GLOB_REALLOC_N(ent->sort.entries, count))) {
2236 glob_dir_finish(ent, 0);
2237 return NULL;
2238 }
2239 ent->sort.entries = newp;
2240 }
2241 ruby_qsort(ent->sort.entries, ent->sort.count, sizeof(ent->sort.entries[0]),
2242 glob_sort_cmp, NULL);
2243 return ent;
2244 }
2245
2246 nomem:
2247 glob_dir_finish(ent, 0);
2248 closedir(dirp);
2249 return NULL;
2250}
2251
2252static rb_dirent_t *
2253glob_getent(ruby_glob_entries_t *ent, int flags, rb_encoding *enc)
2254{
2255 if (flags & FNM_GLOB_NOSORT) {
2256 return dirent_copy(READDIR(ent->nosort.dirp, enc), &ent->nosort.ent);
2257 }
2258 else if (ent->sort.idx < ent->sort.count) {
2259 return ent->sort.entries[ent->sort.idx++];
2260 }
2261 else {
2262 return NULL;
2263 }
2264}
2265
2266static int
2267glob_helper(
2268 int fd,
2269 const char *path,
2270 size_t baselen,
2271 size_t namelen,
2272 int dirsep, /* '/' should be placed before appending child entry's name to 'path'. */
2273 rb_pathtype_t pathtype, /* type of 'path' */
2274 struct glob_pattern **beg,
2275 struct glob_pattern **end,
2276 int flags,
2277 const ruby_glob_funcs_t *funcs,
2278 VALUE arg,
2279 rb_encoding *enc)
2280{
2281 struct stat st;
2282 int status = 0;
2283 struct glob_pattern **cur, **new_beg, **new_end;
2284 int plain = 0, brace = 0, magical = 0, recursive = 0, match_all = 0, match_dir = 0;
2285 int escape = !(flags & FNM_NOESCAPE);
2286 size_t pathlen = baselen + namelen;
2287
2288 rb_check_stack_overflow();
2289
2290 for (cur = beg; cur < end; ++cur) {
2291 struct glob_pattern *p = *cur;
2292 if (p->type == RECURSIVE) {
2293 recursive = 1;
2294 p = p->next;
2295 }
2296 switch (p->type) {
2297 case PLAIN:
2298 plain = 1;
2299 break;
2300 case ALPHA:
2301#if USE_NAME_ON_FS == USE_NAME_ON_FS_REAL_BASENAME
2302 plain = 1;
2303#else
2304 magical = 1;
2305#endif
2306 break;
2307 case BRACE:
2308 if (!recursive || strchr(p->str, '/')) {
2309 brace = 1;
2310 }
2311 break;
2312 case MAGICAL:
2313 magical = 2;
2314 break;
2315 case MATCH_ALL:
2316 match_all = 1;
2317 break;
2318 case MATCH_DIR:
2319 match_dir = 1;
2320 break;
2321 case RECURSIVE:
2322 rb_bug("continuous RECURSIVEs");
2323 }
2324 }
2325
2326 if (brace) {
2327 struct push_glob_args args;
2328 char* brace_path = join_path_from_pattern(beg);
2329 if (!brace_path) return -1;
2330 args.fd = fd;
2331 args.path = path;
2332 args.baselen = baselen;
2333 args.namelen = namelen;
2334 args.dirsep = dirsep;
2335 args.pathtype = pathtype;
2336 args.flags = flags;
2337 args.funcs = funcs;
2338 args.arg = arg;
2339 status = ruby_brace_expand(brace_path, flags, push_caller, (VALUE)&args, enc, Qfalse);
2340 GLOB_FREE(brace_path);
2341 return status;
2342 }
2343
2344 if (*path) {
2345 if (match_all && pathtype == path_unknown) {
2346 if (do_lstat(fd, baselen, path, &st, flags, enc) == 0) {
2347 pathtype = IFTODT(st.st_mode);
2348 }
2349 else {
2350 pathtype = path_noent;
2351 }
2352 }
2353 if (match_dir && (pathtype == path_unknown || pathtype == path_symlink)) {
2354 if (do_stat(fd, baselen, path, &st, flags, enc) == 0) {
2355 pathtype = IFTODT(st.st_mode);
2356 }
2357 else {
2358 pathtype = path_noent;
2359 }
2360 }
2361 if (match_all && pathtype > path_noent) {
2362 const char *subpath = path + baselen + (baselen && path[baselen] == '/');
2363 status = glob_call_func(funcs->match, subpath, arg, enc);
2364 if (status) return status;
2365 }
2366 if (match_dir && pathtype == path_directory) {
2367 int seplen = (baselen && path[baselen] == '/');
2368 const char *subpath = path + baselen + seplen;
2369 char *tmp = join_path(subpath, namelen - seplen, dirsep, "", 0);
2370 if (!tmp) return -1;
2371 status = glob_call_func(funcs->match, tmp, arg, enc);
2372 GLOB_FREE(tmp);
2373 if (status) return status;
2374 }
2375 }
2376
2377 if (pathtype == path_noent) return 0;
2378
2379 if (magical || recursive) {
2380 rb_dirent_t *dp;
2381 DIR *dirp;
2382# if USE_NAME_ON_FS == USE_NAME_ON_FS_BY_FNMATCH
2383 char *plainname = 0;
2384# endif
2385 IF_NORMALIZE_UTF8PATH(int norm_p);
2386# if USE_NAME_ON_FS == USE_NAME_ON_FS_BY_FNMATCH
2387 if (cur + 1 == end && (*cur)->type <= ALPHA) {
2388 plainname = join_path(path, pathlen, dirsep, (*cur)->str, strlen((*cur)->str));
2389 if (!plainname) return -1;
2390 dirp = do_opendir(fd, basename, plainname, flags, enc, funcs->error, arg, &status);
2391 GLOB_FREE(plainname);
2392 }
2393 else
2394# else
2395 ;
2396# endif
2397 dirp = do_opendir(fd, baselen, path, flags, enc, funcs->error, arg, &status);
2398 if (dirp == NULL) {
2399# if FNM_SYSCASE || NORMALIZE_UTF8PATH
2400 if ((magical < 2) && !recursive && (errno == EACCES)) {
2401 /* no read permission, fallback */
2402 goto literally;
2403 }
2404# endif
2405 return status;
2406 }
2407 IF_NORMALIZE_UTF8PATH(norm_p = need_normalization(dirp, *path ? path : "."));
2408
2409# if NORMALIZE_UTF8PATH
2410 if (!(norm_p || magical || recursive)) {
2411 closedir(dirp);
2412 goto literally;
2413 }
2414# endif
2415# ifdef HAVE_GETATTRLIST
2416 if (is_case_sensitive(dirp, path) == 0)
2417 flags |= FNM_CASEFOLD;
2418# endif
2419 ruby_glob_entries_t globent;
2420 if (!glob_opendir(&globent, dirp, flags, enc)) {
2421 status = 0;
2422 if (funcs->error) {
2423 status = (*funcs->error)(path, arg, enc, ENOMEM);
2424 }
2425 else {
2426 sys_warning(path, enc);
2427 }
2428 return status;
2429 }
2430
2431 int skipdot = (flags & FNM_GLOB_SKIPDOT);
2432 flags |= FNM_GLOB_SKIPDOT;
2433
2434 while ((dp = glob_getent(&globent, flags, enc)) != NULL) {
2435 char *buf;
2436 rb_pathtype_t new_pathtype = path_unknown;
2437 const char *name;
2438 size_t namlen;
2439 int dotfile = 0;
2440 IF_NORMALIZE_UTF8PATH(VALUE utf8str = Qnil);
2441
2442 name = dp->d_name;
2443 namlen = dp->d_namlen;
2444 if (name[0] == '.') {
2445 ++dotfile;
2446 if (namlen == 1) {
2447 /* unless DOTMATCH, skip current directories not to recurse infinitely */
2448 if (recursive && !(flags & FNM_DOTMATCH)) continue;
2449 if (skipdot) continue;
2450 ++dotfile;
2451 new_pathtype = path_directory; /* force to skip stat/lstat */
2452 }
2453 else if (namlen == 2 && name[1] == '.') {
2454 /* always skip parent directories not to recurse infinitely */
2455 continue;
2456 }
2457 }
2458
2459# if NORMALIZE_UTF8PATH
2460 if (norm_p && has_nonascii(name, namlen)) {
2461 if (!NIL_P(utf8str = rb_str_normalize_ospath(name, namlen))) {
2462 RSTRING_GETMEM(utf8str, name, namlen);
2463 }
2464 }
2465# endif
2466 buf = join_path(path, pathlen, dirsep, name, namlen);
2467 IF_NORMALIZE_UTF8PATH(if (!NIL_P(utf8str)) rb_str_resize(utf8str, 0));
2468 if (!buf) {
2469 status = -1;
2470 break;
2471 }
2472 name = buf + pathlen + (dirsep != 0);
2473#if !EMULATE_IFTODT
2474 if (dp->d_type != DT_UNKNOWN) {
2475 /* Got it. We need no more lstat. */
2476 new_pathtype = dp->d_type;
2477 }
2478#endif
2479 if (recursive && dotfile < ((flags & FNM_DOTMATCH) ? 2 : 1) &&
2480 new_pathtype == path_unknown) {
2481 /* RECURSIVE never match dot files unless FNM_DOTMATCH is set */
2482 if (do_lstat(fd, baselen, buf, &st, flags, enc) == 0)
2483 new_pathtype = IFTODT(st.st_mode);
2484 else
2485 new_pathtype = path_noent;
2486 }
2487
2488 new_beg = new_end = GLOB_ALLOC_N(struct glob_pattern *, (end - beg) * 2);
2489 if (!new_beg) {
2490 GLOB_FREE(buf);
2491 status = -1;
2492 break;
2493 }
2494
2495 for (cur = beg; cur < end; ++cur) {
2496 struct glob_pattern *p = *cur;
2497 struct dirent_brace_args args;
2498 if (p->type == RECURSIVE) {
2499 if (new_pathtype == path_directory || /* not symlink but real directory */
2500 new_pathtype == path_exist) {
2501 if (dotfile < ((flags & FNM_DOTMATCH) ? 2 : 1))
2502 *new_end++ = p; /* append recursive pattern */
2503 }
2504 p = p->next; /* 0 times recursion */
2505 }
2506 switch (p->type) {
2507 case BRACE:
2508 args.name = name;
2509 args.dp = dp;
2510 args.flags = flags;
2511 if (ruby_brace_expand(p->str, flags, dirent_match_brace,
2512 (VALUE)&args, enc, Qfalse) > 0)
2513 *new_end++ = p->next;
2514 break;
2515 case ALPHA:
2516# if USE_NAME_ON_FS == USE_NAME_ON_FS_BY_FNMATCH
2517 if (plainname) {
2518 *new_end++ = p->next;
2519 break;
2520 }
2521# endif
2522 case PLAIN:
2523 case MAGICAL:
2524 if (dirent_match(p->str, enc, name, dp, flags))
2525 *new_end++ = p->next;
2526 default:
2527 break;
2528 }
2529 }
2530
2531 status = glob_helper(fd, buf, baselen, name - buf - baselen + namlen, 1,
2532 new_pathtype, new_beg, new_end,
2533 flags, funcs, arg, enc);
2534 GLOB_FREE(buf);
2535 GLOB_FREE(new_beg);
2536 if (status) break;
2537 }
2538
2539 glob_dir_finish(&globent, flags);
2540 }
2541 else if (plain) {
2542 struct glob_pattern **copy_beg, **copy_end, **cur2;
2543
2544# if FNM_SYSCASE || NORMALIZE_UTF8PATH
2545 literally:
2546# endif
2547 copy_beg = copy_end = GLOB_ALLOC_N(struct glob_pattern *, end - beg);
2548 if (!copy_beg) return -1;
2549 for (cur = beg; cur < end; ++cur)
2550 *copy_end++ = (*cur)->type <= ALPHA ? *cur : 0;
2551
2552 for (cur = copy_beg; cur < copy_end; ++cur) {
2553 if (*cur) {
2554 rb_pathtype_t new_pathtype = path_unknown;
2555 char *buf;
2556 char *name;
2557 size_t len = strlen((*cur)->str) + 1;
2558 name = GLOB_ALLOC_N(char, len);
2559 if (!name) {
2560 status = -1;
2561 break;
2562 }
2563 memcpy(name, (*cur)->str, len);
2564 if (escape)
2565 len = remove_backslashes(name, name+len-1, enc) - name;
2566
2567 new_beg = new_end = GLOB_ALLOC_N(struct glob_pattern *, end - beg);
2568 if (!new_beg) {
2569 GLOB_FREE(name);
2570 status = -1;
2571 break;
2572 }
2573 *new_end++ = (*cur)->next;
2574 for (cur2 = cur + 1; cur2 < copy_end; ++cur2) {
2575 if (*cur2 && fnmatch((*cur2)->str, enc, name, flags) == 0) {
2576 *new_end++ = (*cur2)->next;
2577 *cur2 = 0;
2578 }
2579 }
2580
2581 buf = join_path(path, pathlen, dirsep, name, len);
2582 GLOB_FREE(name);
2583 if (!buf) {
2584 GLOB_FREE(new_beg);
2585 status = -1;
2586 break;
2587 }
2588#if USE_NAME_ON_FS == USE_NAME_ON_FS_REAL_BASENAME
2589 if ((*cur)->type == ALPHA) {
2590 buf = replace_real_basename(buf, pathlen + (dirsep != 0), enc,
2591 IF_NORMALIZE_UTF8PATH(1)+0,
2592 flags, &new_pathtype);
2593 if (!buf) break;
2594 }
2595#endif
2596 status = glob_helper(fd, buf, baselen,
2597 namelen + strlen(buf + pathlen), 1,
2598 new_pathtype, new_beg, new_end,
2599 flags, funcs, arg, enc);
2600 GLOB_FREE(buf);
2601 GLOB_FREE(new_beg);
2602 if (status) break;
2603 }
2604 }
2605
2606 GLOB_FREE(copy_beg);
2607 }
2608
2609 return status;
2610}
2611
2612static int
2613push_caller(const char *path, VALUE val, void *enc)
2614{
2615 struct push_glob_args *arg = (struct push_glob_args *)val;
2616 struct glob_pattern *list;
2617 int status;
2618
2619 list = glob_make_pattern(path, path + strlen(path), arg->flags, enc);
2620 if (!list) {
2621 return -1;
2622 }
2623 status = glob_helper(arg->fd, arg->path, arg->baselen, arg->namelen, arg->dirsep,
2624 arg->pathtype, &list, &list + 1, arg->flags, arg->funcs,
2625 arg->arg, enc);
2626 glob_free_pattern(list);
2627 return status;
2628}
2629
2630static int ruby_glob0(const char *path, int fd, const char *base, int flags,
2631 const ruby_glob_funcs_t *funcs, VALUE arg, rb_encoding *enc);
2632
2633struct push_glob0_args {
2634 int fd;
2635 const char *base;
2636 int flags;
2637 const ruby_glob_funcs_t *funcs;
2638 VALUE arg;
2639};
2640
2641static int
2642push_glob0_caller(const char *path, VALUE val, void *enc)
2643{
2644 struct push_glob0_args *arg = (struct push_glob0_args *)val;
2645 return ruby_glob0(path, arg->fd, arg->base, arg->flags, arg->funcs, arg->arg, enc);
2646}
2647
2648static int
2649ruby_glob0(const char *path, int fd, const char *base, int flags,
2650 const ruby_glob_funcs_t *funcs, VALUE arg,
2651 rb_encoding *enc)
2652{
2653 struct glob_pattern *list;
2654 const char *root, *start;
2655 char *buf;
2656 size_t n, baselen = 0;
2657 int status, dirsep = FALSE;
2658
2659 start = root = path;
2660
2661 if (*root == '{') {
2662 struct push_glob0_args args;
2663 args.fd = fd;
2664 args.base = base;
2665 args.flags = flags;
2666 args.funcs = funcs;
2667 args.arg = arg;
2668 return ruby_brace_expand(path, flags, push_glob0_caller, (VALUE)&args, enc, Qfalse);
2669 }
2670
2671 flags |= FNM_SYSCASE;
2672#if defined DOSISH
2673 root = rb_enc_path_skip_prefix(root, root + strlen(root), enc);
2674#endif
2675
2676 if (*root == '/') root++;
2677
2678 n = root - start;
2679 if (!n && base) {
2680 n = strlen(base);
2681 baselen = n;
2682 start = base;
2683 dirsep = TRUE;
2684 }
2685 buf = GLOB_ALLOC_N(char, n + 1);
2686 if (!buf) return -1;
2687 MEMCPY(buf, start, char, n);
2688 buf[n] = '\0';
2689
2690 list = glob_make_pattern(root, root + strlen(root), flags, enc);
2691 if (!list) {
2692 GLOB_FREE(buf);
2693 return -1;
2694 }
2695 status = glob_helper(fd, buf, baselen, n-baselen, dirsep,
2696 path_unknown, &list, &list + 1,
2697 flags, funcs, arg, enc);
2698 glob_free_pattern(list);
2699 GLOB_FREE(buf);
2700
2701 return status;
2702}
2704int
2705ruby_glob(const char *path, int flags, ruby_glob_func *func, VALUE arg)
2706{
2707 ruby_glob_funcs_t funcs;
2708 funcs.match = func;
2709 funcs.error = 0;
2710 return ruby_glob0(path, AT_FDCWD, 0, flags & ~GLOB_VERBOSE,
2711 &funcs, arg, rb_ascii8bit_encoding());
2712}
2713
2714static int
2715rb_glob_caller(const char *path, VALUE a, void *enc)
2716{
2717 int status;
2718 struct glob_args *args = (struct glob_args *)a;
2719
2720 args->path = path;
2721 rb_protect(glob_func_caller, a, &status);
2722 return status;
2723}
2724
2725static const ruby_glob_funcs_t rb_glob_funcs = {
2726 rb_glob_caller, rb_glob_error,
2727};
2728
2729void
2730rb_glob(const char *path, void (*func)(const char *, VALUE, void *), VALUE arg)
2731{
2732 struct glob_args args;
2733 int status;
2734
2735 args.func = func;
2736 args.value = arg;
2737 args.enc = rb_ascii8bit_encoding();
2738
2739 status = ruby_glob0(path, AT_FDCWD, 0, GLOB_VERBOSE, &rb_glob_funcs,
2740 (VALUE)&args, args.enc);
2741 if (status) GLOB_JUMP_TAG(status);
2742}
2743
2744static void
2745push_pattern(const char *path, VALUE ary, void *enc)
2746{
2747#if defined _WIN32 || defined __APPLE__
2748 VALUE name = rb_utf8_str_new_cstr(path);
2749 rb_encoding *eenc = rb_default_internal_encoding();
2750 name = rb_str_conv_enc(name, NULL, eenc ? eenc : enc);
2751#else
2752 VALUE name = rb_external_str_new_with_enc(path, strlen(path), enc);
2753#endif
2754 rb_ary_push(ary, name);
2755}
2756
2757static int
2758ruby_brace_expand(const char *str, int flags, ruby_glob_func *func, VALUE arg,
2759 rb_encoding *enc, VALUE var)
2760{
2761 const int escape = !(flags & FNM_NOESCAPE);
2762 const char *p = str;
2763 const char *pend = p + strlen(p);
2764 const char *s = p;
2765 const char *lbrace = 0, *rbrace = 0;
2766 int nest = 0, status = 0;
2767
2768 while (*p) {
2769 if (*p == '{' && nest++ == 0) {
2770 lbrace = p;
2771 }
2772 if (*p == '}' && lbrace && --nest == 0) {
2773 rbrace = p;
2774 break;
2775 }
2776 if (*p == '\\' && escape) {
2777 if (!*++p) break;
2778 }
2779 Inc(p, pend, enc);
2780 }
2781
2782 if (lbrace && rbrace) {
2783 size_t len = strlen(s) + 1;
2784 char *buf = GLOB_ALLOC_N(char, len);
2785 long shift;
2786
2787 if (!buf) return -1;
2788 memcpy(buf, s, lbrace-s);
2789 shift = (lbrace-s);
2790 p = lbrace;
2791 while (p < rbrace) {
2792 const char *t = ++p;
2793 nest = 0;
2794 while (p < rbrace && !(*p == ',' && nest == 0)) {
2795 if (*p == '{') nest++;
2796 if (*p == '}') nest--;
2797 if (*p == '\\' && escape) {
2798 if (++p == rbrace) break;
2799 }
2800 Inc(p, pend, enc);
2801 }
2802 memcpy(buf+shift, t, p-t);
2803 strlcpy(buf+shift+(p-t), rbrace+1, len-(shift+(p-t)));
2804 status = ruby_brace_expand(buf, flags, func, arg, enc, var);
2805 if (status) break;
2806 }
2807 GLOB_FREE(buf);
2808 }
2809 else if (!lbrace && !rbrace) {
2810 status = glob_call_func(func, s, arg, enc);
2811 }
2812
2813 RB_GC_GUARD(var);
2814 return status;
2816
2817struct brace_args {
2818 ruby_glob_funcs_t funcs;
2819 VALUE value;
2820 int flags;
2821};
2822
2823static int
2824glob_brace(const char *path, VALUE val, void *enc)
2825{
2826 struct brace_args *arg = (struct brace_args *)val;
2827
2828 return ruby_glob0(path, AT_FDCWD, 0, arg->flags, &arg->funcs, arg->value, enc);
2829}
2830
2831int
2832ruby_brace_glob_with_enc(const char *str, int flags, ruby_glob_func *func, VALUE arg, rb_encoding *enc)
2833{
2834 struct brace_args args;
2835
2836 flags &= ~GLOB_VERBOSE;
2837 args.funcs.match = func;
2838 args.funcs.error = 0;
2839 args.value = arg;
2840 args.flags = flags;
2841 return ruby_brace_expand(str, flags, glob_brace, (VALUE)&args, enc, Qfalse);
2842}
2844int
2845ruby_brace_glob(const char *str, int flags, ruby_glob_func *func, VALUE arg)
2846{
2847 return ruby_brace_glob_with_enc(str, flags, func, arg, rb_ascii8bit_encoding());
2848}
2849
2850static int
2851push_glob(VALUE ary, VALUE str, VALUE base, int flags)
2852{
2853 struct glob_args args;
2854 int fd;
2855 rb_encoding *enc = rb_enc_get(str);
2856
2857#if defined _WIN32 || defined __APPLE__
2858 str = rb_str_encode_ospath(str);
2859#endif
2860 if (rb_enc_to_index(enc) == ENCINDEX_US_ASCII)
2861 enc = rb_filesystem_encoding();
2862 if (rb_enc_to_index(enc) == ENCINDEX_US_ASCII)
2863 enc = rb_ascii8bit_encoding();
2864 flags |= GLOB_VERBOSE;
2865 args.func = push_pattern;
2866 args.value = ary;
2867 args.enc = enc;
2868 args.base = 0;
2869 fd = AT_FDCWD;
2870 if (!NIL_P(base)) {
2871 if (!RB_TYPE_P(base, T_STRING) || !rb_enc_check(str, base)) {
2872 struct dir_data *dirp = DATA_PTR(base);
2873 if (!dirp->dir) dir_closed();
2874#ifdef HAVE_DIRFD
2875 if ((fd = dirfd(dirp->dir)) == -1)
2876 rb_sys_fail_path(dir_inspect(base));
2877#endif
2878 base = dirp->path;
2879 }
2880 args.base = RSTRING_PTR(base);
2881 }
2882#if defined _WIN32 || defined __APPLE__
2883 enc = rb_utf8_encoding();
2884#endif
2885
2886 return ruby_glob0(RSTRING_PTR(str), fd, args.base, flags, &rb_glob_funcs,
2887 (VALUE)&args, enc);
2888}
2889
2890static VALUE
2891rb_push_glob(VALUE str, VALUE base, int flags) /* '\0' is delimiter */
2892{
2893 VALUE ary;
2894 int status;
2895
2896 /* can contain null bytes as separators */
2897 if (!RB_TYPE_P(str, T_STRING)) {
2898 FilePathValue(str);
2899 }
2900 else if (!rb_str_to_cstr(str)) {
2901 rb_raise(rb_eArgError, "nul-separated glob pattern is deprecated");
2902 }
2903 else {
2904 rb_enc_check(str, rb_enc_from_encoding(rb_usascii_encoding()));
2905 }
2906 ary = rb_ary_new();
2907
2908 status = push_glob(ary, str, base, flags);
2909 if (status) GLOB_JUMP_TAG(status);
2910
2911 return ary;
2912}
2913
2914static VALUE
2915dir_globs(VALUE args, VALUE base, int flags)
2916{
2917 VALUE ary = rb_ary_new();
2918 long i;
2919
2920 for (i = 0; i < RARRAY_LEN(args); ++i) {
2921 int status;
2922 VALUE str = RARRAY_AREF(args, i);
2923 FilePathValue(str);
2924 status = push_glob(ary, str, base, flags);
2925 if (status) GLOB_JUMP_TAG(status);
2926 }
2927 RB_GC_GUARD(args);
2928
2929 return ary;
2930}
2931
2932static VALUE
2933dir_glob_option_base(VALUE base)
2934{
2935 if (NIL_OR_UNDEF_P(base)) {
2936 return Qnil;
2937 }
2938#if USE_OPENDIR_AT
2939 if (rb_typeddata_is_kind_of(base, &dir_data_type)) {
2940 return base;
2941 }
2942#endif
2943 FilePathValue(base);
2944 if (!RSTRING_LEN(base)) return Qnil;
2945 return base;
2946}
2947
2948static int
2949dir_glob_option_sort(VALUE sort)
2950{
2951 return (rb_bool_expected(sort, "sort", TRUE) ? 0 : FNM_GLOB_NOSORT);
2952}
2953
2954static VALUE
2955dir_s_aref(rb_execution_context_t *ec, VALUE obj, VALUE args, VALUE base, VALUE sort)
2956{
2957 const int flags = dir_glob_option_sort(sort);
2958 base = dir_glob_option_base(base);
2959 if (RARRAY_LEN(args) == 1) {
2960 return rb_push_glob(RARRAY_AREF(args, 0), base, flags);
2961 }
2962 return dir_globs(args, base, flags);
2963}
2964
2965static VALUE
2966dir_s_glob(rb_execution_context_t *ec, VALUE obj, VALUE str, VALUE rflags, VALUE base, VALUE sort)
2967{
2968 VALUE ary = rb_check_array_type(str);
2969 const int flags = (NUM2INT(rflags) | dir_glob_option_sort(sort)) & ~FNM_CASEFOLD;
2970 base = dir_glob_option_base(base);
2971 if (NIL_P(ary)) {
2972 ary = rb_push_glob(str, base, flags);
2973 }
2974 else {
2975 ary = dir_globs(ary, base, flags);
2976 }
2977
2978 if (rb_block_given_p()) {
2979 rb_ary_each(ary);
2980 return Qnil;
2981 }
2982 return ary;
2983}
2984
2985static VALUE
2986dir_open_dir(int argc, VALUE *argv)
2987{
2988 VALUE dir = rb_funcallv_kw(rb_cDir, rb_intern("open"), argc, argv, RB_PASS_CALLED_KEYWORDS);
2989
2990 rb_check_typeddata(dir, &dir_data_type);
2991 return dir;
2992}
2993
2994
2995/*
2996 * call-seq:
2997 * Dir.foreach( dirname ) {| filename | block } -> nil
2998 * Dir.foreach( dirname, encoding: enc ) {| filename | block } -> nil
2999 * Dir.foreach( dirname ) -> an_enumerator
3000 * Dir.foreach( dirname, encoding: enc ) -> an_enumerator
3001 *
3002 * Calls the block once for each entry in the named directory, passing
3003 * the filename of each entry as a parameter to the block.
3004 *
3005 * If no block is given, an enumerator is returned instead.
3006 *
3007 * Dir.foreach("testdir") {|x| puts "Got #{x}" }
3008 *
3009 * <em>produces:</em>
3010 *
3011 * Got .
3012 * Got ..
3013 * Got config.h
3014 * Got main.rb
3015 *
3016 */
3017static VALUE
3018dir_foreach(int argc, VALUE *argv, VALUE io)
3019{
3020 VALUE dir;
3021
3022 RETURN_ENUMERATOR(io, argc, argv);
3023 dir = dir_open_dir(argc, argv);
3024 rb_ensure(dir_each, dir, dir_close, dir);
3025 return Qnil;
3026}
3027
3028static VALUE
3029dir_collect(VALUE dir)
3030{
3031 VALUE ary = rb_ary_new();
3032 dir_each_entry(dir, rb_ary_push, ary, FALSE);
3033 return ary;
3034}
3035
3036/*
3037 * call-seq:
3038 * Dir.entries( dirname ) -> array
3039 * Dir.entries( dirname, encoding: enc ) -> array
3040 *
3041 * Returns an array containing all of the filenames in the given
3042 * directory. Will raise a SystemCallError if the named directory
3043 * doesn't exist.
3044 *
3045 * The optional <i>encoding</i> keyword argument specifies the encoding of the
3046 * directory. If not specified, the filesystem encoding is used.
3047 *
3048 * Dir.entries("testdir") #=> [".", "..", "config.h", "main.rb"]
3049 *
3050 */
3051static VALUE
3052dir_entries(int argc, VALUE *argv, VALUE io)
3053{
3054 VALUE dir;
3055
3056 dir = dir_open_dir(argc, argv);
3057 return rb_ensure(dir_collect, dir, dir_close, dir);
3058}
3059
3060static VALUE
3061dir_each_child(VALUE dir)
3062{
3063 return dir_each_entry(dir, dir_yield, Qnil, TRUE);
3064}
3065
3066/*
3067 * call-seq:
3068 * Dir.each_child( dirname ) {| filename | block } -> nil
3069 * Dir.each_child( dirname, encoding: enc ) {| filename | block } -> nil
3070 * Dir.each_child( dirname ) -> an_enumerator
3071 * Dir.each_child( dirname, encoding: enc ) -> an_enumerator
3072 *
3073 * Calls the block once for each entry except for "." and ".." in the
3074 * named directory, passing the filename of each entry as a parameter
3075 * to the block.
3076 *
3077 * If no block is given, an enumerator is returned instead.
3078 *
3079 * Dir.each_child("testdir") {|x| puts "Got #{x}" }
3080 *
3081 * <em>produces:</em>
3082 *
3083 * Got config.h
3084 * Got main.rb
3085 *
3086 */
3087static VALUE
3088dir_s_each_child(int argc, VALUE *argv, VALUE io)
3089{
3090 VALUE dir;
3091
3092 RETURN_ENUMERATOR(io, argc, argv);
3093 dir = dir_open_dir(argc, argv);
3094 rb_ensure(dir_each_child, dir, dir_close, dir);
3095 return Qnil;
3096}
3097
3098/*
3099 * call-seq:
3100 * dir.each_child {| filename | block } -> dir
3101 * dir.each_child -> an_enumerator
3102 *
3103 * Calls the block once for each entry except for "." and ".." in
3104 * this directory, passing the filename of each entry as a parameter
3105 * to the block.
3106 *
3107 * If no block is given, an enumerator is returned instead.
3108 *
3109 * d = Dir.new("testdir")
3110 * d.each_child {|x| puts "Got #{x}" }
3111 *
3112 * <em>produces:</em>
3113 *
3114 * Got config.h
3115 * Got main.rb
3116 *
3117 */
3118static VALUE
3119dir_each_child_m(VALUE dir)
3120{
3121 RETURN_ENUMERATOR(dir, 0, 0);
3122 return dir_each_entry(dir, dir_yield, Qnil, TRUE);
3123}
3124
3125/*
3126 * call-seq:
3127 * dir.children -> array
3128 *
3129 * Returns an array containing all of the filenames except for "."
3130 * and ".." in this directory.
3131 *
3132 * d = Dir.new("testdir")
3133 * d.children #=> ["config.h", "main.rb"]
3134 *
3135 */
3136static VALUE
3137dir_collect_children(VALUE dir)
3138{
3139 VALUE ary = rb_ary_new();
3140 dir_each_entry(dir, rb_ary_push, ary, TRUE);
3141 return ary;
3142}
3143
3144/*
3145 * call-seq:
3146 * Dir.children( dirname ) -> array
3147 * Dir.children( dirname, encoding: enc ) -> array
3148 *
3149 * Returns an array containing all of the filenames except for "."
3150 * and ".." in the given directory. Will raise a SystemCallError if
3151 * the named directory doesn't exist.
3152 *
3153 * The optional <i>encoding</i> keyword argument specifies the encoding of the
3154 * directory. If not specified, the filesystem encoding is used.
3155 *
3156 * Dir.children("testdir") #=> ["config.h", "main.rb"]
3157 *
3158 */
3159static VALUE
3160dir_s_children(int argc, VALUE *argv, VALUE io)
3161{
3162 VALUE dir;
3163
3164 dir = dir_open_dir(argc, argv);
3165 return rb_ensure(dir_collect_children, dir, dir_close, dir);
3166}
3167
3168static int
3169fnmatch_brace(const char *pattern, VALUE val, void *enc)
3170{
3171 struct brace_args *arg = (struct brace_args *)val;
3172 VALUE path = arg->value;
3173 rb_encoding *enc_pattern = enc;
3174 rb_encoding *enc_path = rb_enc_get(path);
3175
3176 if (enc_pattern != enc_path) {
3177 if (!rb_enc_asciicompat(enc_pattern))
3178 return FNM_NOMATCH;
3179 if (!rb_enc_asciicompat(enc_path))
3180 return FNM_NOMATCH;
3181 if (!rb_enc_str_asciionly_p(path)) {
3182 int cr = ENC_CODERANGE_7BIT;
3183 long len = strlen(pattern);
3184 if (rb_str_coderange_scan_restartable(pattern, pattern + len,
3185 enc_pattern, &cr) != len)
3186 return FNM_NOMATCH;
3187 if (cr != ENC_CODERANGE_7BIT)
3188 return FNM_NOMATCH;
3189 }
3190 }
3191 return (fnmatch(pattern, enc, RSTRING_PTR(path), arg->flags) == 0);
3192}
3193
3194/* :nodoc: */
3195static VALUE
3196file_s_fnmatch(int argc, VALUE *argv, VALUE obj)
3197{
3198 VALUE pattern, path;
3199 VALUE rflags;
3200 int flags;
3201
3202 if (rb_scan_args(argc, argv, "21", &pattern, &path, &rflags) == 3)
3203 flags = NUM2INT(rflags);
3204 else
3205 flags = 0;
3206
3207 StringValueCStr(pattern);
3208 FilePathStringValue(path);
3209
3210 if (flags & FNM_EXTGLOB) {
3211 struct brace_args args;
3212
3213 args.value = path;
3214 args.flags = flags;
3215 if (ruby_brace_expand(RSTRING_PTR(pattern), flags, fnmatch_brace,
3216 (VALUE)&args, rb_enc_get(pattern), pattern) > 0)
3217 return Qtrue;
3218 }
3219 else {
3220 rb_encoding *enc = rb_enc_compatible(pattern, path);
3221 if (!enc) return Qfalse;
3222 if (fnmatch(RSTRING_PTR(pattern), enc, RSTRING_PTR(path), flags) == 0)
3223 return Qtrue;
3224 }
3225 RB_GC_GUARD(pattern);
3226
3227 return Qfalse;
3228}
3229
3230/*
3231 * call-seq:
3232 * Dir.home() -> "/home/me"
3233 * Dir.home("root") -> "/root"
3234 *
3235 * Returns the home directory of the current user or the named user
3236 * if given.
3237 */
3238static VALUE
3239dir_s_home(int argc, VALUE *argv, VALUE obj)
3240{
3241 VALUE user;
3242 const char *u = 0;
3243
3244 rb_check_arity(argc, 0, 1);
3245 user = (argc > 0) ? argv[0] : Qnil;
3246 if (!NIL_P(user)) {
3247 SafeStringValue(user);
3248 rb_must_asciicompat(user);
3249 u = StringValueCStr(user);
3250 if (*u) {
3251 return rb_home_dir_of(user, rb_str_new(0, 0));
3252 }
3253 }
3254 return rb_default_home_dir(rb_str_new(0, 0));
3255
3256}
3257
3258#if 0
3259/*
3260 * call-seq:
3261 * Dir.exist?(file_name) -> true or false
3262 *
3263 * Returns <code>true</code> if the named file is a directory,
3264 * <code>false</code> otherwise.
3265 *
3266 */
3267VALUE
3268rb_file_directory_p(void)
3269{
3270}
3271#endif
3272
3273static void *
3274nogvl_dir_empty_p(void *ptr)
3275{
3276 const char *path = ptr;
3277 DIR *dir = opendir(path);
3278 struct dirent *dp;
3279 VALUE result = Qtrue;
3280
3281 if (!dir) {
3282 int e = errno;
3283 switch (gc_for_fd_with_gvl(e)) {
3284 default:
3285 dir = opendir(path);
3286 if (dir) break;
3287 e = errno;
3288 /* fall through */
3289 case 0:
3290 if (e == ENOTDIR) return (void *)Qfalse;
3291 errno = e; /* for rb_sys_fail_path */
3292 return (void *)Qundef;
3293 }
3294 }
3295 while ((dp = READDIR(dir, NULL)) != NULL) {
3296 if (!to_be_skipped(dp)) {
3297 result = Qfalse;
3298 break;
3299 }
3300 }
3301 closedir(dir);
3302 return (void *)result;
3303}
3304
3305/*
3306 * call-seq:
3307 * Dir.empty?(path_name) -> true or false
3308 *
3309 * Returns <code>true</code> if the named file is an empty directory,
3310 * <code>false</code> if it is not a directory or non-empty.
3311 */
3312static VALUE
3313rb_dir_s_empty_p(VALUE obj, VALUE dirname)
3314{
3315 VALUE result, orig;
3316 const char *path;
3317 enum {false_on_notdir = 1};
3318
3319 FilePathValue(dirname);
3320 orig = rb_str_dup_frozen(dirname);
3321 dirname = rb_str_encode_ospath(dirname);
3322 dirname = rb_str_dup_frozen(dirname);
3323 path = RSTRING_PTR(dirname);
3324
3325#if defined HAVE_GETATTRLIST && defined ATTR_DIR_ENTRYCOUNT
3326 {
3327 u_int32_t attrbuf[SIZEUP32(fsobj_tag_t)];
3328 struct attrlist al = {ATTR_BIT_MAP_COUNT, 0, ATTR_CMN_OBJTAG,};
3329 if (getattrlist(path, &al, attrbuf, sizeof(attrbuf), 0) != 0)
3330 rb_sys_fail_path(orig);
3331 if (*(const fsobj_tag_t *)(attrbuf+1) == VT_HFS) {
3332 al.commonattr = 0;
3333 al.dirattr = ATTR_DIR_ENTRYCOUNT;
3334 if (getattrlist(path, &al, attrbuf, sizeof(attrbuf), 0) == 0) {
3335 if (attrbuf[0] >= 2 * sizeof(u_int32_t))
3336 return RBOOL(attrbuf[1] == 0);
3337 if (false_on_notdir) return Qfalse;
3338 }
3339 rb_sys_fail_path(orig);
3340 }
3341 }
3342#endif
3343
3344 result = (VALUE)rb_thread_call_without_gvl(nogvl_dir_empty_p, (void *)path,
3345 RUBY_UBF_IO, 0);
3346 if (UNDEF_P(result)) {
3347 rb_sys_fail_path(orig);
3348 }
3349 return result;
3350}
3351
3352void
3353Init_Dir(void)
3354{
3355 rb_cDir = rb_define_class("Dir", rb_cObject);
3356
3358
3359 rb_define_alloc_func(rb_cDir, dir_s_alloc);
3360 rb_define_singleton_method(rb_cDir, "foreach", dir_foreach, -1);
3361 rb_define_singleton_method(rb_cDir, "entries", dir_entries, -1);
3362 rb_define_singleton_method(rb_cDir, "each_child", dir_s_each_child, -1);
3363 rb_define_singleton_method(rb_cDir, "children", dir_s_children, -1);
3364
3365 rb_define_method(rb_cDir,"fileno", dir_fileno, 0);
3366 rb_define_method(rb_cDir,"path", dir_path, 0);
3367 rb_define_method(rb_cDir,"to_path", dir_path, 0);
3368 rb_define_method(rb_cDir,"inspect", dir_inspect, 0);
3369 rb_define_method(rb_cDir,"read", dir_read, 0);
3370 rb_define_method(rb_cDir,"each", dir_each, 0);
3371 rb_define_method(rb_cDir,"each_child", dir_each_child_m, 0);
3372 rb_define_method(rb_cDir,"children", dir_collect_children, 0);
3373 rb_define_method(rb_cDir,"rewind", dir_rewind, 0);
3374 rb_define_method(rb_cDir,"tell", dir_tell, 0);
3375 rb_define_method(rb_cDir,"seek", dir_seek, 1);
3376 rb_define_method(rb_cDir,"pos", dir_tell, 0);
3377 rb_define_method(rb_cDir,"pos=", dir_set_pos, 1);
3378 rb_define_method(rb_cDir,"close", dir_close, 0);
3379
3380 rb_define_singleton_method(rb_cDir,"chdir", dir_s_chdir, -1);
3381 rb_define_singleton_method(rb_cDir,"getwd", dir_s_getwd, 0);
3382 rb_define_singleton_method(rb_cDir,"pwd", dir_s_getwd, 0);
3383 rb_define_singleton_method(rb_cDir,"chroot", dir_s_chroot, 1);
3384 rb_define_singleton_method(rb_cDir,"mkdir", dir_s_mkdir, -1);
3385 rb_define_singleton_method(rb_cDir,"rmdir", dir_s_rmdir, 1);
3386 rb_define_singleton_method(rb_cDir,"delete", dir_s_rmdir, 1);
3387 rb_define_singleton_method(rb_cDir,"unlink", dir_s_rmdir, 1);
3388 rb_define_singleton_method(rb_cDir,"home", dir_s_home, -1);
3389
3390 rb_define_singleton_method(rb_cDir,"exist?", rb_file_directory_p, 1);
3391 rb_define_singleton_method(rb_cDir,"empty?", rb_dir_s_empty_p, 1);
3392
3393 rb_define_singleton_method(rb_cFile,"fnmatch", file_s_fnmatch, -1);
3394 rb_define_singleton_method(rb_cFile,"fnmatch?", file_s_fnmatch, -1);
3395
3396 /* Document-const: File::Constants::FNM_NOESCAPE
3397 *
3398 * Disables escapes in File.fnmatch and Dir.glob patterns
3399 */
3400 rb_file_const("FNM_NOESCAPE", INT2FIX(FNM_NOESCAPE));
3401
3402 /* Document-const: File::Constants::FNM_PATHNAME
3403 *
3404 * Wildcards in File.fnmatch and Dir.glob patterns do not match directory
3405 * separators
3406 */
3407 rb_file_const("FNM_PATHNAME", INT2FIX(FNM_PATHNAME));
3408
3409 /* Document-const: File::Constants::FNM_DOTMATCH
3410 *
3411 * The '*' wildcard matches filenames starting with "." in File.fnmatch
3412 * and Dir.glob patterns
3413 */
3414 rb_file_const("FNM_DOTMATCH", INT2FIX(FNM_DOTMATCH));
3415
3416 /* Document-const: File::Constants::FNM_CASEFOLD
3417 *
3418 * Makes File.fnmatch patterns case insensitive (but not Dir.glob
3419 * patterns).
3420 */
3421 rb_file_const("FNM_CASEFOLD", INT2FIX(FNM_CASEFOLD));
3422
3423 /* Document-const: File::Constants::FNM_EXTGLOB
3424 *
3425 * Allows file globbing through "{a,b}" in File.fnmatch patterns.
3426 */
3427 rb_file_const("FNM_EXTGLOB", INT2FIX(FNM_EXTGLOB));
3428
3429 /* Document-const: File::Constants::FNM_SYSCASE
3430 *
3431 * System default case insensitiveness, equals to FNM_CASEFOLD or
3432 * 0.
3433 */
3434 rb_file_const("FNM_SYSCASE", INT2FIX(FNM_SYSCASE));
3435
3436 /* Document-const: File::Constants::FNM_SHORTNAME
3437 *
3438 * Makes patterns to match short names if existing. Valid only
3439 * on Microsoft Windows.
3440 */
3441 rb_file_const("FNM_SHORTNAME", INT2FIX(FNM_SHORTNAME));
3442}
3443
3444#include "dir.rbinc"
#define rb_define_method(klass, mid, func, arity)
Defines klass#mid.
#define rb_define_singleton_method(klass, mid, func, arity)
Defines klass.mid.
int ruby_glob_func(const char *path, VALUE arg, void *enc)
Type of a glob callback function.
Definition glob.h:49
void rb_include_module(VALUE klass, VALUE module)
Includes a module to a class.
Definition class.c:1125
VALUE rb_define_class(const char *name, VALUE super)
Defines a top-level class.
Definition class.c:923
int rb_scan_args(int argc, const VALUE *argv, const char *fmt,...)
Retrieves argument from argc and argv to given VALUE references according to the format string.
Definition class.c:2574
int rb_block_given_p(void)
Determines if the current method is given a block.
Definition eval.c:868
#define rb_str_new2
Old name of rb_str_new_cstr.
Definition string.h:1675
#define ENC_CODERANGE_7BIT
Old name of RUBY_ENC_CODERANGE_7BIT.
Definition coderange.h:180
#define T_STRING
Old name of RUBY_T_STRING.
Definition value_type.h:78
#define xfree
Old name of ruby_xfree.
Definition xmalloc.h:58
#define Qundef
Old name of RUBY_Qundef.
#define INT2FIX
Old name of RB_INT2FIX.
Definition long.h:48
#define rb_str_cat2
Old name of rb_str_cat_cstr.
Definition string.h:1683
#define UNREACHABLE_RETURN
Old name of RBIMPL_UNREACHABLE_RETURN.
Definition assume.h:29
#define CLASS_OF
Old name of rb_class_of.
Definition globals.h:203
#define ISALPHA
Old name of rb_isalpha.
Definition ctype.h:92
#define ISASCII
Old name of rb_isascii.
Definition ctype.h:85
#define Qtrue
Old name of RUBY_Qtrue.
#define NUM2INT
Old name of RB_NUM2INT.
Definition int.h:44
#define INT2NUM
Old name of RB_INT2NUM.
Definition int.h:43
#define Qnil
Old name of RUBY_Qnil.
#define Qfalse
Old name of RUBY_Qfalse.
#define NIL_P
Old name of RB_NIL_P.
#define MBCLEN_CHARFOUND_P(ret)
Old name of ONIGENC_MBCLEN_CHARFOUND_P.
Definition encoding.h:532
#define NUM2LONG
Old name of RB_NUM2LONG.
Definition long.h:51
void rb_raise(VALUE exc, const char *fmt,...)
Exception entry point.
Definition error.c:3150
int rb_typeddata_is_kind_of(VALUE obj, const rb_data_type_t *data_type)
Checks if the given object is of given kind.
Definition error.c:1041
void rb_syserr_fail(int e, const char *mesg)
Raises appropriate exception that represents a C errno.
Definition error.c:3262
void rb_bug(const char *fmt,...)
Interpreter panic switch.
Definition error.c:794
void rb_sys_fail(const char *mesg)
Converts a C errno into a Ruby exception, then raises it.
Definition error.c:3274
VALUE rb_eIOError
IOError exception.
Definition io.c:182
void rb_syserr_fail_str(int e, VALUE mesg)
Identical to rb_syserr_fail(), except it takes the message in Ruby's String instead of C's.
Definition error.c:3268
VALUE rb_eRuntimeError
RuntimeError exception.
Definition error.c:1089
void * rb_check_typeddata(VALUE obj, const rb_data_type_t *data_type)
Identical to rb_typeddata_is_kind_of(), except it raises exceptions instead of returning false.
Definition error.c:1058
void rb_warn(const char *fmt,...)
Identical to rb_warning(), except it reports always regardless of runtime -W flag.
Definition error.c:411
VALUE rb_eArgError
ArgumentError exception.
Definition error.c:1092
VALUE rb_cDir
Dir class.
Definition dir.c:458
VALUE rb_mEnumerable
Enumerable module.
Definition enum.c:27
VALUE rb_cFile
File class.
Definition file.c:176
#define RB_OBJ_WRITE(old, slot, young)
Declaration of a "back" pointer.
Definition rgengc.h:220
Encoding relates APIs.
static bool rb_enc_asciicompat(rb_encoding *enc)
Queries if the passed encoding is in some sense compatible with ASCII.
Definition encoding.h:784
static unsigned int rb_enc_codepoint(const char *p, const char *e, rb_encoding *enc)
Queries the code point of character pointed by the passed pointer.
Definition encoding.h:587
VALUE rb_str_conv_enc(VALUE str, rb_encoding *from, rb_encoding *to)
Encoding conversion main routine.
Definition string.c:1208
VALUE rb_enc_str_new_cstr(const char *ptr, rb_encoding *enc)
Identical to rb_enc_str_new(), except it assumes the passed pointer is a pointer to a C string.
Definition string.c:1021
VALUE rb_enc_str_new(const char *ptr, long len, rb_encoding *enc)
Identical to rb_enc_str_new(), except it additionally takes an encoding.
Definition string.c:981
VALUE rb_external_str_new_with_enc(const char *ptr, long len, rb_encoding *enc)
Identical to rb_external_str_new(), except it additionally takes an encoding.
Definition string.c:1214
int rb_enc_str_asciionly_p(VALUE str)
Queries if the passed string is "ASCII only".
Definition string.c:833
long rb_str_coderange_scan_restartable(const char *str, const char *end, rb_encoding *enc, int *cr)
Scans the passed string until it finds something odd.
Definition string.c:719
VALUE rb_funcallv_kw(VALUE recv, ID mid, int argc, const VALUE *argv, int kw_splat)
Identical to rb_funcallv(), except you can specify how to handle the last element of the given array.
Definition vm_eval.c:1069
#define RETURN_ENUMERATOR(obj, argc, argv)
Identical to RETURN_SIZED_ENUMERATOR(), except its size is unknown.
Definition enumerator.h:239
#define rb_check_frozen
Just another name of rb_check_frozen.
Definition error.h:264
static int rb_check_arity(int argc, int min, int max)
Ensures that the passed integer is in the passed range.
Definition error.h:280
#define rb_utf8_str_new_cstr(str)
Identical to rb_str_new_cstr, except it generates a string of "UTF-8" encoding.
Definition string.h:1583
VALUE rb_str_append(VALUE dst, VALUE src)
Identical to rb_str_buf_append(), except it converts the right hand side before concatenating.
Definition string.c:3353
VALUE rb_str_subseq(VALUE str, long beg, long len)
Identical to rb_str_substr(), except the numbers are interpreted as byte offsets instead of character...
Definition string.c:2826
#define rb_str_new(str, len)
Allocates an instance of rb_cString.
Definition string.h:1498
VALUE rb_str_dup(VALUE str)
Duplicates a string.
Definition string.c:1834
void rb_must_asciicompat(VALUE obj)
Asserts that the given string's encoding is (Ruby's definition of) ASCII compatible.
Definition string.c:2489
#define rb_str_dup_frozen
Just another name of rb_str_new_frozen.
Definition string.h:631
VALUE rb_str_resize(VALUE str, long len)
Overwrites the length of the string.
Definition string.c:3064
#define rb_str_new_cstr(str)
Identical to rb_str_new, except it assumes the passed pointer is a pointer to a C string.
Definition string.h:1514
#define RUBY_UBF_IO
A special UBF for blocking IO operations.
Definition thread.h:382
VALUE rb_thread_current(void)
Obtains the "current" thread.
Definition thread.c:2793
VALUE rb_class_name(VALUE obj)
Queries the name of the given object's class.
Definition variable.c:310
void rb_define_alloc_func(VALUE klass, rb_alloc_func_t func)
Sets the allocator function of a class.
ID rb_intern(const char *name)
Finds or creates a symbol of the given name.
Definition symbol.c:796
void * rb_thread_call_with_gvl(void *(*func)(void *), void *data1)
(Re-)acquires the GVL.
Definition thread.c:1761
void * rb_thread_call_without_gvl(void *(*func)(void *), void *data1, rb_unblock_function_t *ubf, void *data2)
Allows the passed function to run in parallel with other Ruby threads.
void ruby_qsort(void *, const size_t, const size_t, int(*)(const void *, const void *, void *), void *)
Reentrant implementation of quick sort.
char * ruby_getcwd(void)
This is our own version of getcwd(3) that uses ruby_xmalloc() instead of system malloc (benefits our ...
Definition util.c:550
VALUE rb_yield(VALUE val)
Yields the block.
Definition vm_eval.c:1357
#define MEMCPY(p1, p2, type, n)
Handy macro to call memcpy.
Definition memory.h:366
#define MEMZERO(p, type, n)
Handy macro to erase a region of memory.
Definition memory.h:354
static int rb_mul_size_overflow(size_t a, size_t b, size_t max, size_t *c)
Definition memory.h:525
#define RB_GC_GUARD(v)
Prevents premature destruction of local objects.
Definition memory.h:161
#define NUM2MODET
Converts a C's mode_t into an instance of rb_cInteger.
Definition mode_t.h:28
VALUE type(ANYARGS)
ANYARGS-ed function type.
VALUE rb_ensure(type *q, VALUE w, type *e, VALUE r)
An equivalent of ensure clause.
#define RARRAY_LEN
Just another name of rb_array_len.
Definition rarray.h:68
#define RARRAY_AREF(a, i)
Definition rarray.h:583
#define Data_Wrap_Struct(klass, mark, free, sval)
Converts sval, a pointer to your struct, into a Ruby object.
Definition rdata.h:202
#define DATA_PTR(obj)
Convenient getter macro.
Definition rdata.h:71
#define RUBY_DEFAULT_FREE
This is a value you can set to RData::dfree.
Definition rdata.h:82
#define SafeStringValue(v)
Definition rstring.h:104
#define RSTRING_GETMEM(str, ptrvar, lenvar)
Convenient macro to obtain the contents and length at once.
Definition rstring.h:574
static long RSTRING_LEN(VALUE str)
Queries the length of the string.
Definition rstring.h:484
static char * RSTRING_PTR(VALUE str)
Queries the contents pointer of the string.
Definition rstring.h:498
#define StringValueCStr(v)
Identical to StringValuePtr, except it additionally checks for the contents for viability as a C stri...
Definition rstring.h:95
#define TypedData_Get_Struct(obj, type, data_type, sval)
Obtains a C struct from inside of a wrapper Ruby object.
Definition rtypeddata.h:507
#define TypedData_Make_Struct(klass, type, data_type, sval)
Identical to TypedData_Wrap_Struct, except it allocates a new data region internally instead of takin...
Definition rtypeddata.h:489
#define FilePathValue(v)
Ensures that the parameter object is a path.
Definition ruby.h:91
#define FilePathStringValue(v)
Definition ruby.h:104
#define RB_PASS_CALLED_KEYWORDS
Pass keywords if current method is called with keywords, useful for argument delegation.
Definition scan_args.h:78
Definition dir.h:21
This is the struct that holds necessary info for a struct.
Definition rtypeddata.h:190
uintptr_t VALUE
Type that represents a Ruby object.
Definition value.h:40
static bool RB_TYPE_P(VALUE obj, enum ruby_value_type t)
Queries if the given object is of given type.
Definition value_type.h:375