Ruby  3.4.0dev (2024-12-06 revision 892c46283a5ea4179500d951c9d4866c0051f27b)
dir.c (892c46283a5ea4179500d951c9d4866c0051f27b)
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
68 char *getenv();
69 #endif
70 
71 #ifndef HAVE_STRING_H
72 char *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/imemo.h"
117 #include "internal/vm.h"
118 #include "ruby/encoding.h"
119 #include "ruby/ruby.h"
120 #include "ruby/thread.h"
121 #include "ruby/util.h"
122 #include "builtin.h"
123 
124 #ifndef AT_FDCWD
125 # define AT_FDCWD -1
126 #endif
127 
128 #define vm_initialized rb_cThread
129 
130 /* define system APIs */
131 #ifdef _WIN32
132 # undef chdir
133 # define chdir(p) rb_w32_uchdir(p)
134 # undef mkdir
135 # define mkdir(p, m) rb_w32_umkdir((p), (m))
136 # undef rmdir
137 # define rmdir(p) rb_w32_urmdir(p)
138 # undef opendir
139 # define opendir(p) rb_w32_uopendir(p)
140 # define ruby_getcwd() rb_w32_ugetcwd(NULL, 0)
141 # define IS_WIN32 1
142 #else
143 # define IS_WIN32 0
144 #endif
145 
146 #ifdef HAVE_GETATTRLIST
147 struct getattrlist_args {
148  const char *path;
149  int fd;
150  struct attrlist *list;
151  void *buf;
152  size_t size;
153  unsigned int options;
154 };
155 
156 # define GETATTRLIST_ARGS(list_, buf_, options_) (struct getattrlist_args) \
157  {.list = list_, .buf = buf_, .size = sizeof(buf_), .options = options_}
158 
159 static void *
160 nogvl_getattrlist(void *args)
161 {
162  struct getattrlist_args *arg = args;
163  return (void *)(VALUE)getattrlist(arg->path, arg->list, arg->buf, arg->size, arg->options);
164 }
165 
166 static int
167 gvl_getattrlist(struct getattrlist_args *args, const char *path)
168 {
169  args->path = path;
170  return IO_WITHOUT_GVL_INT(nogvl_getattrlist, args);
171 }
172 
173 # ifdef HAVE_FGETATTRLIST
174 static void *
175 nogvl_fgetattrlist(void *args)
176 {
177  struct getattrlist_args *arg = args;
178  return (void *)(VALUE)fgetattrlist(arg->fd, arg->list, arg->buf, arg->size, arg->options);
179 }
180 
181 static int
182 gvl_fgetattrlist(struct getattrlist_args *args, int fd)
183 {
184  args->fd = fd;
185  return IO_WITHOUT_GVL_INT(nogvl_fgetattrlist, args);
186 }
187 # endif
188 #endif
189 
190 #if NORMALIZE_UTF8PATH
191 # if defined HAVE_FGETATTRLIST || !defined HAVE_GETATTRLIST
192 # define need_normalization(dirp, path) need_normalization(dirp)
193 # else
194 # define need_normalization(dirp, path) need_normalization(path)
195 # endif
196 static inline int
197 need_normalization(DIR *dirp, const char *path)
198 {
199 # if defined HAVE_FGETATTRLIST || defined HAVE_GETATTRLIST
200  u_int32_t attrbuf[SIZEUP32(fsobj_tag_t)];
201  struct attrlist al = {ATTR_BIT_MAP_COUNT, 0, ATTR_CMN_OBJTAG,};
202  struct getattrlist_args args = GETATTRLIST_ARGS(&al, attrbuf, 0);
203 # if defined HAVE_FGETATTRLIST
204  int ret = gvl_fgetattrlist(&args, dirfd(dirp));
205 # else
206  int ret = gvl_getattrlist(&args, path);
207 # endif
208  if (!ret) {
209  const fsobj_tag_t *tag = (void *)(attrbuf+1);
210  switch (*tag) {
211  case VT_HFS:
212  case VT_CIFS:
213  return TRUE;
214  }
215  }
216 # endif
217  return FALSE;
218 }
219 
220 static inline int
221 has_nonascii(const char *ptr, size_t len)
222 {
223  while (len > 0) {
224  if (!ISASCII(*ptr)) return 1;
225  ptr++;
226  --len;
227  }
228  return 0;
229 }
230 
231 # define IF_NORMALIZE_UTF8PATH(something) something
232 #else
233 # define IF_NORMALIZE_UTF8PATH(something) /* nothing */
234 #endif
235 
236 #if defined(IFTODT) && defined(DT_UNKNOWN)
237 # define EMULATE_IFTODT 0
238 #else
239 # define EMULATE_IFTODT 1
240 #endif
241 
242 #if EMULATE_IFTODT
243 # define IFTODT(m) (((m) & S_IFMT) / ((~S_IFMT & (S_IFMT-1)) + 1))
244 #endif
245 
246 typedef enum {
247 #if !EMULATE_IFTODT
248  path_exist = DT_UNKNOWN,
249  path_directory = DT_DIR,
250  path_regular = DT_REG,
251  path_symlink = DT_LNK,
252 #else
253  path_exist,
254  path_directory = IFTODT(S_IFDIR),
255  path_regular = IFTODT(S_IFREG),
256  path_symlink = IFTODT(S_IFLNK),
257 #endif
258  path_noent = -1,
259  path_unknown = -2
260 } rb_pathtype_t;
261 
262 #define FNM_NOESCAPE 0x01
263 #define FNM_PATHNAME 0x02
264 #define FNM_DOTMATCH 0x04
265 #define FNM_CASEFOLD 0x08
266 #define FNM_EXTGLOB 0x10
267 #if CASEFOLD_FILESYSTEM
268 #define FNM_SYSCASE FNM_CASEFOLD
269 #else
270 #define FNM_SYSCASE 0
271 #endif
272 #ifdef _WIN32
273 #define FNM_SHORTNAME 0x20
274 #else
275 #define FNM_SHORTNAME 0
276 #endif
277 #define FNM_GLOB_NOSORT 0x40
278 #define FNM_GLOB_SKIPDOT 0x80
279 
280 #define FNM_NOMATCH 1
281 #define FNM_ERROR 2
282 
283 # define Next(p, e, enc) ((p)+ rb_enc_mbclen((p), (e), (enc)))
284 # define Inc(p, e, enc) ((p) = Next((p), (e), (enc)))
285 
286 static char *
287 bracket(
288  const char *p, /* pattern (next to '[') */
289  const char *pend,
290  const char *s, /* string */
291  const char *send,
292  int flags,
293  rb_encoding *enc)
294 {
295  const int nocase = flags & FNM_CASEFOLD;
296  const int escape = !(flags & FNM_NOESCAPE);
297  unsigned int c1, c2;
298  int r;
299  int ok = 0, not = 0;
300 
301  if (p >= pend) return NULL;
302  if (*p == '!' || *p == '^') {
303  not = 1;
304  p++;
305  }
306 
307  while (*p != ']') {
308  const char *t1 = p;
309  if (escape && *t1 == '\\')
310  t1++;
311  if (!*t1)
312  return NULL;
313  p = t1 + (r = rb_enc_mbclen(t1, pend, enc));
314  if (p >= pend) return NULL;
315  if (p[0] == '-' && p[1] != ']') {
316  const char *t2 = p + 1;
317  int r2;
318  if (escape && *t2 == '\\')
319  t2++;
320  if (!*t2)
321  return NULL;
322  p = t2 + (r2 = rb_enc_mbclen(t2, pend, enc));
323  if (ok) continue;
324  if ((r <= (send-s) && memcmp(t1, s, r) == 0) ||
325  (r2 <= (send-s) && memcmp(t2, s, r2) == 0)) {
326  ok = 1;
327  continue;
328  }
329  c1 = rb_enc_codepoint(s, send, enc);
330  if (nocase) c1 = rb_enc_toupper(c1, enc);
331  c2 = rb_enc_codepoint(t1, pend, enc);
332  if (nocase) c2 = rb_enc_toupper(c2, enc);
333  if (c1 < c2) continue;
334  c2 = rb_enc_codepoint(t2, pend, enc);
335  if (nocase) c2 = rb_enc_toupper(c2, enc);
336  if (c1 > c2) continue;
337  }
338  else {
339  if (ok) continue;
340  if (r <= (send-s) && memcmp(t1, s, r) == 0) {
341  ok = 1;
342  continue;
343  }
344  if (!nocase) continue;
345  c1 = rb_enc_toupper(rb_enc_codepoint(s, send, enc), enc);
346  c2 = rb_enc_toupper(rb_enc_codepoint(p, pend, enc), enc);
347  if (c1 != c2) continue;
348  }
349  ok = 1;
350  }
351 
352  return ok == not ? NULL : (char *)p + 1;
353 }
354 
355 /* If FNM_PATHNAME is set, only path element will be matched. (up to '/' or '\0')
356  Otherwise, entire string will be matched.
357  End marker itself won't be compared.
358  And if function succeeds, *pcur reaches end marker.
359 */
360 #define UNESCAPE(p) (escape && *(p) == '\\' ? (p) + 1 : (p))
361 #define ISEND(p) (!*(p) || (pathname && *(p) == '/'))
362 #define RETURN(val) return *pcur = p, *scur = s, (val);
363 
364 static int
365 fnmatch_helper(
366  const char **pcur, /* pattern */
367  const char **scur, /* string */
368  int flags,
369  rb_encoding *enc)
370 {
371  const int period = !(flags & FNM_DOTMATCH);
372  const int pathname = flags & FNM_PATHNAME;
373  const int escape = !(flags & FNM_NOESCAPE);
374  const int nocase = flags & FNM_CASEFOLD;
375 
376  const char *ptmp = 0;
377  const char *stmp = 0;
378 
379  const char *p = *pcur;
380  const char *pend = p + strlen(p);
381  const char *s = *scur;
382  const char *send = s + strlen(s);
383 
384  int r;
385 
386  if (period && *s == '.' && *UNESCAPE(p) != '.') /* leading period */
387  RETURN(FNM_NOMATCH);
388 
389  while (1) {
390  switch (*p) {
391  case '*':
392  do { p++; } while (*p == '*');
393  if (ISEND(UNESCAPE(p))) {
394  p = UNESCAPE(p);
395  RETURN(0);
396  }
397  if (ISEND(s))
398  RETURN(FNM_NOMATCH);
399  ptmp = p;
400  stmp = s;
401  continue;
402 
403  case '?':
404  if (ISEND(s))
405  RETURN(FNM_NOMATCH);
406  p++;
407  Inc(s, send, enc);
408  continue;
409 
410  case '[': {
411  const char *t;
412  if (ISEND(s))
413  RETURN(FNM_NOMATCH);
414  if ((t = bracket(p + 1, pend, s, send, flags, enc)) != 0) {
415  p = t;
416  Inc(s, send, enc);
417  continue;
418  }
419  goto failed;
420  }
421  }
422 
423  /* ordinary */
424  p = UNESCAPE(p);
425  if (ISEND(s))
426  RETURN(ISEND(p) ? 0 : FNM_NOMATCH);
427  if (ISEND(p))
428  goto failed;
429  r = rb_enc_precise_mbclen(p, pend, enc);
430  if (!MBCLEN_CHARFOUND_P(r))
431  goto failed;
432  if (r <= (send-s) && memcmp(p, s, r) == 0) {
433  p += r;
434  s += r;
435  continue;
436  }
437  if (!nocase) goto failed;
438  if (rb_enc_toupper(rb_enc_codepoint(p, pend, enc), enc) !=
439  rb_enc_toupper(rb_enc_codepoint(s, send, enc), enc))
440  goto failed;
441  p += r;
442  Inc(s, send, enc);
443  continue;
444 
445  failed: /* try next '*' position */
446  if (ptmp && stmp) {
447  p = ptmp;
448  Inc(stmp, send, enc); /* !ISEND(*stmp) */
449  s = stmp;
450  continue;
451  }
452  RETURN(FNM_NOMATCH);
453  }
454 }
455 
456 static int
457 fnmatch(
458  const char *pattern,
459  rb_encoding *enc,
460  const char *string,
461  int flags)
462 {
463  const char *p = pattern;
464  const char *s = string;
465  const char *send = s + strlen(string);
466  const int period = !(flags & FNM_DOTMATCH);
467  const int pathname = flags & FNM_PATHNAME;
468 
469  const char *ptmp = 0;
470  const char *stmp = 0;
471 
472  if (pathname) {
473  while (1) {
474  if (p[0] == '*' && p[1] == '*' && p[2] == '/') {
475  do { p += 3; } while (p[0] == '*' && p[1] == '*' && p[2] == '/');
476  ptmp = p;
477  stmp = s;
478  }
479  if (fnmatch_helper(&p, &s, flags, enc) == 0) {
480  while (*s && *s != '/') Inc(s, send, enc);
481  if (*p && *s) {
482  p++;
483  s++;
484  continue;
485  }
486  if (!*p && !*s)
487  return 0;
488  }
489  /* failed : try next recursion */
490  if (ptmp && stmp && !(period && *stmp == '.')) {
491  while (*stmp && *stmp != '/') Inc(stmp, send, enc);
492  if (*stmp) {
493  p = ptmp;
494  stmp++;
495  s = stmp;
496  continue;
497  }
498  }
499  return FNM_NOMATCH;
500  }
501  }
502  else
503  return fnmatch_helper(&p, &s, flags, enc);
504 }
505 
507 
508 struct dir_data {
509  DIR *dir;
510  const VALUE path;
511  rb_encoding *enc;
512 };
513 
514 static void
515 dir_free(void *ptr)
516 {
517  struct dir_data *dir = ptr;
518 
519  if (dir->dir) closedir(dir->dir);
520 }
521 
522 RUBY_REFERENCES(dir_refs) = {
523  RUBY_REF_EDGE(struct dir_data, path),
524  RUBY_REF_END
525 };
526 
527 static const rb_data_type_t dir_data_type = {
528  "dir",
529  {
530  RUBY_REFS_LIST_PTR(dir_refs),
531  dir_free,
532  NULL, // Nothing allocated externally, so don't need a memsize function
533  },
534  0, NULL, RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_DECL_MARKING | RUBY_TYPED_EMBEDDABLE
535 };
536 
537 static VALUE dir_close(VALUE);
538 
539 static VALUE
540 dir_s_alloc(VALUE klass)
541 {
542  struct dir_data *dirp;
543  VALUE obj = TypedData_Make_Struct(klass, struct dir_data, &dir_data_type, dirp);
544 
545  dirp->dir = NULL;
546  RB_OBJ_WRITE(obj, &dirp->path, Qnil);
547  dirp->enc = NULL;
548 
549  return obj;
550 }
551 
552 static void *
553 nogvl_opendir(void *ptr)
554 {
555  const char *path = ptr;
556 
557  return opendir(path);
558 }
559 
560 static DIR *
561 opendir_without_gvl(const char *path)
562 {
563  if (vm_initialized) {
564  union { const void *in; void *out; } u;
565 
566  u.in = path;
567 
568  return IO_WITHOUT_GVL(nogvl_opendir, u.out);
569  }
570  else
571  return opendir(path);
572 }
573 
574 static void
575 close_dir_data(struct dir_data *dp)
576 {
577  if (dp->dir) {
578  if (closedir(dp->dir) < 0) {
579  dp->dir = NULL;
580  rb_sys_fail("closedir");
581  }
582  dp->dir = NULL;
583  }
584 }
585 
586 static void
587 check_closedir(DIR *dirp)
588 {
589  if (closedir(dirp) < 0)
590  rb_sys_fail("closedir");
591 }
592 
593 static VALUE
594 dir_initialize(rb_execution_context_t *ec, VALUE dir, VALUE dirname, VALUE enc)
595 {
596  struct dir_data *dp;
597  VALUE orig;
598  const char *path;
599  rb_encoding *fsenc = NIL_P(enc) ? rb_filesystem_encoding() : rb_to_encoding(enc);
600 
601  FilePathValue(dirname);
602  orig = rb_str_dup_frozen(dirname);
603  dirname = rb_str_encode_ospath(dirname);
604  dirname = rb_str_dup_frozen(dirname);
605 
606  TypedData_Get_Struct(dir, struct dir_data, &dir_data_type, dp);
607  close_dir_data(dp);
608  RB_OBJ_WRITE(dir, &dp->path, Qnil);
609  dp->enc = fsenc;
610  path = RSTRING_PTR(dirname);
611  dp->dir = opendir_without_gvl(path);
612  if (dp->dir == NULL) {
613  int e = errno;
614  if (rb_gc_for_fd(e)) {
615  dp->dir = opendir_without_gvl(path);
616  }
617 #ifdef HAVE_GETATTRLIST
618  else if (e == EIO) {
619  u_int32_t attrbuf[1];
620  struct attrlist al = {ATTR_BIT_MAP_COUNT, 0};
621  struct getattrlist_args args = GETATTRLIST_ARGS(&al, attrbuf, FSOPT_NOFOLLOW);
622  if (gvl_getattrlist(&args, path) == 0) {
623  dp->dir = opendir_without_gvl(path);
624  }
625  }
626 #endif
627  if (dp->dir == NULL) {
628  RB_GC_GUARD(dirname);
629  rb_syserr_fail_path(e, orig);
630  }
631  }
632  RB_OBJ_WRITE(dir, &dp->path, orig);
633 
634  return dir;
635 }
636 
637 static VALUE
638 dir_s_open(rb_execution_context_t *ec, VALUE klass, VALUE dirname, VALUE enc)
639 {
640  struct dir_data *dp;
641  VALUE dir = TypedData_Make_Struct(klass, struct dir_data, &dir_data_type, dp);
642 
643  dir_initialize(ec, dir, dirname, enc);
644 
645  return dir;
646 }
647 
648 static VALUE
649 dir_s_close(rb_execution_context_t *ec, VALUE klass, VALUE dir)
650 {
651  return dir_close(dir);
652 }
653 
654 # if defined(HAVE_FDOPENDIR) && defined(HAVE_DIRFD)
655 static void *
656 nogvl_fdopendir(void *fd)
657 {
658  return fdopendir((int)(VALUE)fd);
659 }
660 
661 /*
662  * call-seq:
663  * Dir.for_fd(fd) -> dir
664  *
665  * Returns a new \Dir object representing the directory specified by the given
666  * integer directory file descriptor +fd+:
667  *
668  * d0 = Dir.new('..')
669  * d1 = Dir.for_fd(d0.fileno)
670  *
671  * Note that the returned +d1+ does not have an associated path:
672  *
673  * d0.path # => '..'
674  * d1.path # => nil
675  *
676  * This method uses the
677  * {fdopendir()}[https://www.man7.org/linux/man-pages/man3/fdopendir.3p.html]
678  * function defined by POSIX 2008;
679  * the method is not implemented on non-POSIX platforms (raises NotImplementedError).
680  */
681 static VALUE
682 dir_s_for_fd(VALUE klass, VALUE fd)
683 {
684  struct dir_data *dp;
685  VALUE dir = TypedData_Make_Struct(klass, struct dir_data, &dir_data_type, dp);
686 
687  if (!(dp->dir = IO_WITHOUT_GVL(nogvl_fdopendir, (void *)(VALUE)NUM2INT(fd)))) {
688  rb_sys_fail("fdopendir");
690  }
691 
692  RB_OBJ_WRITE(dir, &dp->path, Qnil);
693  return dir;
694 }
695 #else
696 #define dir_s_for_fd rb_f_notimplement
697 #endif
698 
699 NORETURN(static void dir_closed(void));
700 
701 static void
702 dir_closed(void)
703 {
704  rb_raise(rb_eIOError, "closed directory");
705 }
706 
707 static struct dir_data *
708 dir_get(VALUE dir)
709 {
710  rb_check_frozen(dir);
711  return rb_check_typeddata(dir, &dir_data_type);
712 }
713 
714 static struct dir_data *
715 dir_check(VALUE dir)
716 {
717  struct dir_data *dirp = dir_get(dir);
718  if (!dirp->dir) dir_closed();
719  return dirp;
720 }
721 
722 #define GetDIR(obj, dirp) ((dirp) = dir_check(obj))
723 
724 
725 /*
726  * call-seq:
727  * inspect -> string
728  *
729  * Returns a string description of +self+:
730  *
731  * Dir.new('example').inspect # => "#<Dir:example>"
732  *
733  */
734 static VALUE
735 dir_inspect(VALUE dir)
736 {
737  struct dir_data *dirp;
738 
739  TypedData_Get_Struct(dir, struct dir_data, &dir_data_type, dirp);
740  if (!NIL_P(dirp->path)) {
741  VALUE str = rb_str_new_cstr("#<");
743  rb_str_cat2(str, ":");
744  rb_str_append(str, dirp->path);
745  rb_str_cat2(str, ">");
746  return str;
747  }
748  return rb_funcallv(dir, idTo_s, 0, 0);
749 }
750 
751 /* Workaround for Solaris 10 that does not have dirfd.
752  Note: Solaris 11 (POSIX.1-2008 compliant) has dirfd(3C).
753  */
754 #if defined(__sun) && !defined(HAVE_DIRFD)
755 # if defined(HAVE_DIR_D_FD)
756 # define dirfd(x) ((x)->d_fd)
757 # define HAVE_DIRFD 1
758 # elif defined(HAVE_DIR_DD_FD)
759 # define dirfd(x) ((x)->dd_fd)
760 # define HAVE_DIRFD 1
761 # endif
762 #endif
763 
764 #ifdef HAVE_DIRFD
765 /*
766  * call-seq:
767  * fileno -> integer
768  *
769  * Returns the file descriptor used in <em>dir</em>.
770  *
771  * d = Dir.new('..')
772  * d.fileno # => 8
773  *
774  * This method uses the
775  * {dirfd()}[https://www.man7.org/linux/man-pages/man3/dirfd.3.html]
776  * function defined by POSIX 2008;
777  * the method is not implemented on non-POSIX platforms (raises NotImplementedError).
778  */
779 static VALUE
780 dir_fileno(VALUE dir)
781 {
782  struct dir_data *dirp;
783  int fd;
784 
785  GetDIR(dir, dirp);
786  fd = dirfd(dirp->dir);
787  if (fd == -1)
788  rb_sys_fail("dirfd");
789  return INT2NUM(fd);
790 }
791 #else
792 #define dir_fileno rb_f_notimplement
793 #endif
794 
795 /*
796  * call-seq:
797  * path -> string or nil
798  *
799  * Returns the +dirpath+ string that was used to create +self+
800  * (or +nil+ if created by method Dir.for_fd):
801  *
802  * Dir.new('example').path # => "example"
803  *
804  */
805 static VALUE
806 dir_path(VALUE dir)
807 {
808  struct dir_data *dirp;
809 
810  TypedData_Get_Struct(dir, struct dir_data, &dir_data_type, dirp);
811  if (NIL_P(dirp->path)) return Qnil;
812  return rb_str_dup(dirp->path);
813 }
814 
815 #if defined _WIN32
816 static int
817 fundamental_encoding_p(rb_encoding *enc)
818 {
819  switch (rb_enc_to_index(enc)) {
820  case ENCINDEX_ASCII_8BIT:
821  case ENCINDEX_US_ASCII:
822  case ENCINDEX_UTF_8:
823  return TRUE;
824  default:
825  return FALSE;
826  }
827 }
828 # define READDIR(dir, enc) rb_w32_readdir((dir), (enc))
829 # define READDIR_NOGVL READDIR
830 #else
831 NORETURN(static void *sys_failure(void *function));
832 static void *
833 sys_failure(void *function)
834 {
835  rb_sys_fail(function);
836 }
837 
838 static void *
839 nogvl_readdir(void *dir)
840 {
841  rb_errno_set(0);
842  if ((dir = readdir(dir)) == NULL) {
843  if (rb_errno())
844  rb_thread_call_with_gvl(sys_failure, (void *)"readdir");
845  }
846  return dir;
847 }
848 
849 # define READDIR(dir, enc) IO_WITHOUT_GVL(nogvl_readdir, (void *)(dir))
850 # define READDIR_NOGVL(dir, enc) nogvl_readdir((dir))
851 #endif
852 
853 /* safe to use without GVL */
854 static int
855 to_be_skipped(const struct dirent *dp)
856 {
857  const char *name = dp->d_name;
858  if (name[0] != '.') return FALSE;
859 #ifdef HAVE_DIRENT_NAMLEN
860  switch (NAMLEN(dp)) {
861  case 2:
862  if (name[1] != '.') return FALSE;
863  case 1:
864  return TRUE;
865  default:
866  break;
867  }
868 #else
869  if (!name[1]) return TRUE;
870  if (name[1] != '.') return FALSE;
871  if (!name[2]) return TRUE;
872 #endif
873  return FALSE;
874 }
875 
876 /*
877  * call-seq:
878  * read -> string or nil
879  *
880  * Reads and returns the next entry name from +self+;
881  * returns +nil+ if at end-of-stream;
882  * see {Dir As Stream-Like}[rdoc-ref:Dir@Dir+As+Stream-Like]:
883  *
884  * dir = Dir.new('example')
885  * dir.read # => "."
886  * dir.read # => ".."
887  * dir.read # => "config.h"
888  *
889  */
890 static VALUE
891 dir_read(VALUE dir)
892 {
893  struct dir_data *dirp;
894  struct dirent *dp;
895 
896  GetDIR(dir, dirp);
897  rb_errno_set(0);
898  if ((dp = READDIR(dirp->dir, dirp->enc)) != NULL) {
899  return rb_external_str_new_with_enc(dp->d_name, NAMLEN(dp), dirp->enc);
900  }
901  else {
902  int e = errno;
903  if (e != 0) rb_syserr_fail(e, 0);
904  return Qnil; /* end of stream */
905  }
906 }
907 
908 static VALUE dir_each_entry(VALUE, VALUE (*)(VALUE, VALUE), VALUE, int);
909 
910 static VALUE
911 dir_yield(VALUE arg, VALUE path)
912 {
913  return rb_yield(path);
914 }
915 
916 /*
917  * call-seq:
918  * each {|entry_name| ... } -> self
919  *
920  * Calls the block with each entry name in +self+:
921  *
922  * Dir.new('example').each {|entry_name| p entry_name }
923  *
924  * Output:
925 
926  * "."
927  * ".."
928  * "config.h"
929  * "lib"
930  * "main.rb"
931  *
932  * With no block given, returns an Enumerator.
933  *
934  */
935 static VALUE
936 dir_each(VALUE dir)
937 {
938  RETURN_ENUMERATOR(dir, 0, 0);
939  return dir_each_entry(dir, dir_yield, Qnil, FALSE);
940 }
941 
942 static VALUE
943 dir_each_entry(VALUE dir, VALUE (*each)(VALUE, VALUE), VALUE arg, int children_only)
944 {
945  struct dir_data *dirp;
946  struct dirent *dp;
947  IF_NORMALIZE_UTF8PATH(int norm_p);
948 
949  GetDIR(dir, dirp);
950  rewinddir(dirp->dir);
951  IF_NORMALIZE_UTF8PATH(norm_p = need_normalization(dirp->dir, RSTRING_PTR(dirp->path)));
952  while ((dp = READDIR(dirp->dir, dirp->enc)) != NULL) {
953  const char *name = dp->d_name;
954  size_t namlen = NAMLEN(dp);
955  VALUE path;
956 
957  if (children_only && name[0] == '.') {
958  if (namlen == 1) continue; /* current directory */
959  if (namlen == 2 && name[1] == '.') continue; /* parent directory */
960  }
961 #if NORMALIZE_UTF8PATH
962  if (norm_p && has_nonascii(name, namlen) &&
963  !NIL_P(path = rb_str_normalize_ospath(name, namlen))) {
964  path = rb_external_str_with_enc(path, dirp->enc);
965  }
966  else
967 #endif
968  path = rb_external_str_new_with_enc(name, namlen, dirp->enc);
969  (*each)(arg, path);
970  }
971  return dir;
972 }
973 
974 #ifdef HAVE_TELLDIR
975 /*
976  * call-seq:
977  * tell -> integer
978  *
979  * Returns the current position of +self+;
980  * see {Dir As Stream-Like}[rdoc-ref:Dir@Dir+As+Stream-Like]:
981  *
982  * dir = Dir.new('example')
983  * dir.tell # => 0
984  * dir.read # => "."
985  * dir.tell # => 1
986  *
987  */
988 static VALUE
989 dir_tell(VALUE dir)
990 {
991  struct dir_data *dirp;
992  long pos;
993 
994  GetDIR(dir, dirp);
995  if((pos = telldir(dirp->dir)) < 0)
996  rb_sys_fail("telldir");
997  return rb_int2inum(pos);
998 }
999 #else
1000 #define dir_tell rb_f_notimplement
1001 #endif
1002 
1003 #ifdef HAVE_SEEKDIR
1004 /*
1005  * call-seq:
1006  * seek(position) -> self
1007  *
1008  * Sets the position in +self+ and returns +self+.
1009  * The value of +position+ should have been returned from an earlier call to #tell;
1010  * if not, the return values from subsequent calls to #read are unspecified.
1011  *
1012  * See {Dir As Stream-Like}[rdoc-ref:Dir@Dir+As+Stream-Like].
1013  *
1014  * Examples:
1015  *
1016  * dir = Dir.new('example')
1017  * dir.pos # => 0
1018  * dir.seek(3) # => #<Dir:example>
1019  * dir.pos # => 3
1020  * dir.seek(30) # => #<Dir:example>
1021  * dir.pos # => 5
1022  *
1023  */
1024 static VALUE
1025 dir_seek(VALUE dir, VALUE pos)
1026 {
1027  struct dir_data *dirp;
1028  long p = NUM2LONG(pos);
1029 
1030  GetDIR(dir, dirp);
1031  seekdir(dirp->dir, p);
1032  return dir;
1033 }
1034 #else
1035 #define dir_seek rb_f_notimplement
1036 #endif
1037 
1038 #ifdef HAVE_SEEKDIR
1039 /*
1040  * call-seq:
1041  * pos = position -> integer
1042  *
1043  * Sets the position in +self+ and returns +position+.
1044  * The value of +position+ should have been returned from an earlier call to #tell;
1045  * if not, the return values from subsequent calls to #read are unspecified.
1046  *
1047  * See {Dir As Stream-Like}[rdoc-ref:Dir@Dir+As+Stream-Like].
1048  *
1049  * Examples:
1050  *
1051  * dir = Dir.new('example')
1052  * dir.pos # => 0
1053  * dir.pos = 3 # => 3
1054  * dir.pos # => 3
1055  * dir.pos = 30 # => 30
1056  * dir.pos # => 5
1057  *
1058  */
1059 static VALUE
1060 dir_set_pos(VALUE dir, VALUE pos)
1061 {
1062  dir_seek(dir, pos);
1063  return pos;
1064 }
1065 #else
1066 #define dir_set_pos rb_f_notimplement
1067 #endif
1068 
1069 /*
1070  * call-seq:
1071  * rewind -> self
1072  *
1073  * Sets the position in +self+ to zero;
1074  * see {Dir As Stream-Like}[rdoc-ref:Dir@Dir+As+Stream-Like]:
1075  *
1076  * dir = Dir.new('example')
1077  * dir.read # => "."
1078  * dir.read # => ".."
1079  * dir.pos # => 2
1080  * dir.rewind # => #<Dir:example>
1081  * dir.pos # => 0
1082  *
1083  */
1084 static VALUE
1085 dir_rewind(VALUE dir)
1086 {
1087  struct dir_data *dirp;
1088 
1089  GetDIR(dir, dirp);
1090  rewinddir(dirp->dir);
1091  return dir;
1092 }
1093 
1094 /*
1095  * call-seq:
1096  * close -> nil
1097  *
1098  * Closes the stream in +self+, if it is open, and returns +nil+;
1099  * ignored if +self+ is already closed:
1100  *
1101  * dir = Dir.new('example')
1102  * dir.read # => "."
1103  * dir.close # => nil
1104  * dir.close # => nil
1105  * dir.read # Raises IOError.
1106  *
1107  */
1108 static VALUE
1109 dir_close(VALUE dir)
1110 {
1111  struct dir_data *dirp;
1112 
1113  dirp = dir_get(dir);
1114  if (!dirp->dir) return Qnil;
1115  close_dir_data(dirp);
1116 
1117  return Qnil;
1118 }
1119 
1120 static void *
1121 nogvl_chdir(void *ptr)
1122 {
1123  const char *path = ptr;
1124 
1125  return (void *)(VALUE)chdir(path);
1126 }
1127 
1128 static void
1129 dir_chdir0(VALUE path)
1130 {
1131  if (IO_WITHOUT_GVL_INT(nogvl_chdir, (void*)RSTRING_PTR(path)) < 0)
1132  rb_sys_fail_path(path);
1133 }
1134 
1135 static struct {
1136  VALUE thread;
1137  VALUE path;
1138  int line;
1139  int blocking;
1140 } chdir_lock = {
1141  .blocking = 0, .thread = Qnil,
1142  .path = Qnil, .line = 0,
1143 };
1144 
1145 static void
1146 chdir_enter(void)
1147 {
1148  if (chdir_lock.blocking == 0) {
1149  chdir_lock.path = rb_source_location(&chdir_lock.line);
1150  }
1151  chdir_lock.blocking++;
1152  if (NIL_P(chdir_lock.thread)) {
1153  chdir_lock.thread = rb_thread_current();
1154  }
1155 }
1156 
1157 static void
1158 chdir_leave(void)
1159 {
1160  chdir_lock.blocking--;
1161  if (chdir_lock.blocking == 0) {
1162  chdir_lock.thread = Qnil;
1163  chdir_lock.path = Qnil;
1164  chdir_lock.line = 0;
1165  }
1166 }
1167 
1168 static int
1169 chdir_alone_block_p(void)
1170 {
1171  int block_given = rb_block_given_p();
1172  if (chdir_lock.blocking > 0) {
1173  if (rb_thread_current() != chdir_lock.thread)
1174  rb_raise(rb_eRuntimeError, "conflicting chdir during another chdir block");
1175  if (!block_given) {
1176  if (!NIL_P(chdir_lock.path)) {
1177  rb_warn("conflicting chdir during another chdir block\n"
1178  "%" PRIsVALUE ":%d: note: previous chdir was here",
1179  chdir_lock.path, chdir_lock.line);
1180  }
1181  else {
1182  rb_warn("conflicting chdir during another chdir block");
1183  }
1184  }
1185  }
1186  return block_given;
1188 
1189 struct chdir_data {
1190  VALUE old_path, new_path;
1191  int done;
1192  bool yield_path;
1193 };
1194 
1195 static VALUE
1196 chdir_yield(VALUE v)
1197 {
1198  struct chdir_data *args = (void *)v;
1199  dir_chdir0(args->new_path);
1200  args->done = TRUE;
1201  chdir_enter();
1202  return args->yield_path ? rb_yield(args->new_path) : rb_yield_values2(0, NULL);
1203 }
1204 
1205 static VALUE
1206 chdir_restore(VALUE v)
1207 {
1208  struct chdir_data *args = (void *)v;
1209  if (args->done) {
1210  chdir_leave();
1211  dir_chdir0(args->old_path);
1212  }
1213  return Qnil;
1214 }
1215 
1216 static VALUE
1217 chdir_path(VALUE path, bool yield_path)
1218 {
1219  if (chdir_alone_block_p()) {
1220  struct chdir_data args;
1221 
1222  args.old_path = rb_str_encode_ospath(rb_dir_getwd());
1223  args.new_path = path;
1224  args.done = FALSE;
1225  args.yield_path = yield_path;
1226  return rb_ensure(chdir_yield, (VALUE)&args, chdir_restore, (VALUE)&args);
1227  }
1228  else {
1229  char *p = RSTRING_PTR(path);
1230  int r = IO_WITHOUT_GVL_INT(nogvl_chdir, p);
1231  if (r < 0)
1232  rb_sys_fail_path(path);
1233  }
1234 
1235  return INT2FIX(0);
1236 }
1237 
1238 /*
1239  * call-seq:
1240  * Dir.chdir(new_dirpath) -> 0
1241  * Dir.chdir -> 0
1242  * Dir.chdir(new_dirpath) {|new_dirpath| ... } -> object
1243  * Dir.chdir {|cur_dirpath| ... } -> object
1244  *
1245  * Changes the current working directory.
1246  *
1247  * With argument +new_dirpath+ and no block,
1248  * changes to the given +dirpath+:
1249  *
1250  * Dir.pwd # => "/example"
1251  * Dir.chdir('..') # => 0
1252  * Dir.pwd # => "/"
1253  *
1254  * With no argument and no block:
1255  *
1256  * - Changes to the value of environment variable +HOME+ if defined.
1257  * - Otherwise changes to the value of environment variable +LOGDIR+ if defined.
1258  * - Otherwise makes no change.
1259  *
1260  * With argument +new_dirpath+ and a block, temporarily changes the working directory:
1261  *
1262  * - Calls the block with the argument.
1263  * - Changes to the given directory.
1264  * - Executes the block (yielding the new path).
1265  * - Restores the previous working directory.
1266  * - Returns the block's return value.
1267  *
1268  * Example:
1269  *
1270  * Dir.chdir('/var/spool/mail')
1271  * Dir.pwd # => "/var/spool/mail"
1272  * Dir.chdir('/tmp') do
1273  * Dir.pwd # => "/tmp"
1274  * end
1275  * Dir.pwd # => "/var/spool/mail"
1276  *
1277  * With no argument and a block,
1278  * calls the block with the current working directory (string)
1279  * and returns the block's return value.
1280  *
1281  * Calls to \Dir.chdir with blocks may be nested:
1282  *
1283  * Dir.chdir('/var/spool/mail')
1284  * Dir.pwd # => "/var/spool/mail"
1285  * Dir.chdir('/tmp') do
1286  * Dir.pwd # => "/tmp"
1287  * Dir.chdir('/usr') do
1288  * Dir.pwd # => "/usr"
1289  * end
1290  * Dir.pwd # => "/tmp"
1291  * end
1292  * Dir.pwd # => "/var/spool/mail"
1293  *
1294  * In a multi-threaded program an error is raised if a thread attempts
1295  * to open a +chdir+ block while another thread has one open,
1296  * or a call to +chdir+ without a block occurs inside
1297  * a block passed to +chdir+ (even in the same thread).
1298  *
1299  * Raises an exception if the target directory does not exist.
1300  */
1301 static VALUE
1302 dir_s_chdir(int argc, VALUE *argv, VALUE obj)
1303 {
1304  VALUE path = Qnil;
1305 
1306  if (rb_check_arity(argc, 0, 1) == 1) {
1307  path = rb_str_encode_ospath(rb_get_path(argv[0]));
1308  }
1309  else {
1310  const char *dist = getenv("HOME");
1311  if (!dist) {
1312  dist = getenv("LOGDIR");
1313  if (!dist) rb_raise(rb_eArgError, "HOME/LOGDIR not set");
1314  }
1315  path = rb_str_new2(dist);
1316  }
1317 
1318  return chdir_path(path, true);
1319 }
1320 
1321 #if defined(HAVE_FCHDIR) && defined(HAVE_DIRFD) && HAVE_FCHDIR && HAVE_DIRFD
1322 static void *
1323 nogvl_fchdir(void *ptr)
1324 {
1325  const int *fd = ptr;
1326 
1327  return (void *)(VALUE)fchdir(*fd);
1328 }
1329 
1330 static void
1331 dir_fchdir(int fd)
1332 {
1333  if (IO_WITHOUT_GVL_INT(nogvl_fchdir, (void *)&fd) < 0)
1334  rb_sys_fail("fchdir");
1335 }
1336 
1337 struct fchdir_data {
1338  VALUE old_dir;
1339  int fd;
1340  int done;
1341 };
1342 
1343 static VALUE
1344 fchdir_yield(VALUE v)
1345 {
1346  struct fchdir_data *args = (void *)v;
1347  dir_fchdir(args->fd);
1348  args->done = TRUE;
1349  chdir_enter();
1350  return rb_yield_values(0);
1351 }
1352 
1353 static VALUE
1354 fchdir_restore(VALUE v)
1355 {
1356  struct fchdir_data *args = (void *)v;
1357  if (args->done) {
1358  chdir_leave();
1359  dir_fchdir(RB_NUM2INT(dir_fileno(args->old_dir)));
1360  }
1361  dir_close(args->old_dir);
1362  return Qnil;
1363 }
1364 
1365 /*
1366  * call-seq:
1367  * Dir.fchdir(fd) -> 0
1368  * Dir.fchdir(fd) { ... } -> object
1369  *
1370  * Changes the current working directory to the directory
1371  * specified by the integer file descriptor +fd+.
1372  *
1373  * When passing a file descriptor over a UNIX socket or to a child process,
1374  * using +fchdir+ instead of +chdir+ avoids the
1375  * {time-of-check to time-of-use vulnerability}[https://en.wikipedia.org/wiki/Time-of-check_to_time-of-use]
1376  *
1377  * With no block, changes to the directory given by +fd+:
1378  *
1379  * Dir.chdir('/var/spool/mail')
1380  * Dir.pwd # => "/var/spool/mail"
1381  * dir = Dir.new('/usr')
1382  * fd = dir.fileno
1383  * Dir.fchdir(fd)
1384  * Dir.pwd # => "/usr"
1385  *
1386  * With a block, temporarily changes the working directory:
1387  *
1388  * - Calls the block with the argument.
1389  * - Changes to the given directory.
1390  * - Executes the block (yields no args).
1391  * - Restores the previous working directory.
1392  * - Returns the block's return value.
1393  *
1394  * Example:
1395  *
1396  * Dir.chdir('/var/spool/mail')
1397  * Dir.pwd # => "/var/spool/mail"
1398  * dir = Dir.new('/tmp')
1399  * fd = dir.fileno
1400  * Dir.fchdir(fd) do
1401  * Dir.pwd # => "/tmp"
1402  * end
1403  * Dir.pwd # => "/var/spool/mail"
1404  *
1405  * This method uses the
1406  * {fchdir()}[https://www.man7.org/linux/man-pages/man3/fchdir.3p.html]
1407  * function defined by POSIX 2008;
1408  * the method is not implemented on non-POSIX platforms (raises NotImplementedError).
1409  *
1410  * Raises an exception if the file descriptor is not valid.
1411  *
1412  * In a multi-threaded program an error is raised if a thread attempts
1413  * to open a +chdir+ block while another thread has one open,
1414  * or a call to +chdir+ without a block occurs inside
1415  * a block passed to +chdir+ (even in the same thread).
1416  */
1417 static VALUE
1418 dir_s_fchdir(VALUE klass, VALUE fd_value)
1419 {
1420  int fd = RB_NUM2INT(fd_value);
1421 
1422  if (chdir_alone_block_p()) {
1423  struct fchdir_data args;
1424  args.old_dir = dir_s_alloc(klass);
1425  dir_initialize(NULL, args.old_dir, rb_fstring_cstr("."), Qnil);
1426  args.fd = fd;
1427  args.done = FALSE;
1428  return rb_ensure(fchdir_yield, (VALUE)&args, fchdir_restore, (VALUE)&args);
1429  }
1430  else {
1431  int r = IO_WITHOUT_GVL_INT(nogvl_fchdir, &fd);
1432  if (r < 0)
1433  rb_sys_fail("fchdir");
1434  }
1435 
1436  return INT2FIX(0);
1437 }
1438 #else
1439 #define dir_s_fchdir rb_f_notimplement
1440 #endif
1441 
1442 /*
1443  * call-seq:
1444  * chdir -> 0
1445  * chdir { ... } -> object
1446  *
1447  * Changes the current working directory to +self+:
1448  *
1449  * Dir.pwd # => "/"
1450  * dir = Dir.new('example')
1451  * dir.chdir
1452  * Dir.pwd # => "/example"
1453  *
1454  * With a block, temporarily changes the working directory:
1455  *
1456  * - Calls the block.
1457  * - Changes to the given directory.
1458  * - Executes the block (yields no args).
1459  * - Restores the previous working directory.
1460  * - Returns the block's return value.
1461  *
1462  * Uses Dir.fchdir if available, and Dir.chdir if not, see those
1463  * methods for caveats.
1464  */
1465 static VALUE
1466 dir_chdir(VALUE dir)
1467 {
1468 #if defined(HAVE_FCHDIR) && defined(HAVE_DIRFD) && HAVE_FCHDIR && HAVE_DIRFD
1469  return dir_s_fchdir(rb_cDir, dir_fileno(dir));
1470 #else
1471  return chdir_path(dir_get(dir)->path, false);
1472 #endif
1473 }
1474 
1475 #ifndef _WIN32
1476 VALUE
1477 rb_dir_getwd_ospath(void)
1478 {
1479  char *path;
1480  VALUE cwd;
1481  VALUE path_guard;
1482 
1483  path_guard = rb_imemo_tmpbuf_auto_free_pointer();
1484  path = ruby_getcwd();
1485  rb_imemo_tmpbuf_set_ptr(path_guard, path);
1486 #ifdef __APPLE__
1487  cwd = rb_str_normalize_ospath(path, strlen(path));
1488 #else
1489  cwd = rb_str_new2(path);
1490 #endif
1491  rb_free_tmp_buffer(&path_guard);
1492  return cwd;
1493 }
1494 #endif
1496 VALUE
1497 rb_dir_getwd(void)
1498 {
1500  int fsenc = rb_enc_to_index(fs);
1501  VALUE cwd = rb_dir_getwd_ospath();
1502 
1503  switch (fsenc) {
1504  case ENCINDEX_US_ASCII:
1505  fsenc = ENCINDEX_ASCII_8BIT;
1506  case ENCINDEX_ASCII_8BIT:
1507  break;
1508 #if defined _WIN32 || defined __APPLE__
1509  default:
1510  return rb_str_conv_enc(cwd, NULL, fs);
1511 #endif
1512  }
1513  return rb_enc_associate_index(cwd, fsenc);
1514 }
1515 
1516 /*
1517  * call-seq:
1518  * Dir.pwd -> string
1519  *
1520  * Returns the path to the current working directory:
1521  *
1522  * Dir.chdir("/tmp") # => 0
1523  * Dir.pwd # => "/tmp"
1524  *
1525  */
1526 static VALUE
1527 dir_s_getwd(VALUE dir)
1528 {
1529  return rb_dir_getwd();
1530 }
1531 
1532 static VALUE
1533 check_dirname(VALUE dir)
1534 {
1535  VALUE d = dir;
1536  char *path, *pend;
1537  long len;
1538  rb_encoding *enc;
1539 
1540  FilePathValue(d);
1541  enc = rb_enc_get(d);
1542  RSTRING_GETMEM(d, path, len);
1543  pend = path + len;
1544  pend = rb_enc_path_end(rb_enc_path_skip_prefix(path, pend, enc), pend, enc);
1545  if (pend - path < len) {
1546  d = rb_str_subseq(d, 0, pend - path);
1547  StringValueCStr(d);
1548  }
1549  return rb_str_encode_ospath(d);
1550 }
1551 
1552 #if defined(HAVE_CHROOT)
1553 static void *
1554 nogvl_chroot(void *dirname)
1555 {
1556  return (void *)(VALUE)chroot((const char *)dirname);
1557 }
1558 
1559 /*
1560  * call-seq:
1561  * Dir.chroot(dirpath) -> 0
1562  *
1563  * Changes the root directory of the calling process to that specified in +dirpath+.
1564  * The new root directory is used for pathnames beginning with <tt>'/'</tt>.
1565  * The root directory is inherited by all children of the calling process.
1566  *
1567  * Only a privileged process may call +chroot+.
1568  *
1569  * See {Linux chroot}[https://man7.org/linux/man-pages/man2/chroot.2.html].
1570  */
1571 static VALUE
1572 dir_s_chroot(VALUE dir, VALUE path)
1573 {
1574  path = check_dirname(path);
1575  if (IO_WITHOUT_GVL_INT(nogvl_chroot, (void *)RSTRING_PTR(path)) == -1)
1576  rb_sys_fail_path(path);
1577 
1578  return INT2FIX(0);
1579 }
1580 #else
1581 #define dir_s_chroot rb_f_notimplement
1582 #endif
1583 
1584 struct mkdir_arg {
1585  const char *path;
1586  mode_t mode;
1587 };
1588 
1589 static void *
1590 nogvl_mkdir(void *ptr)
1591 {
1592  struct mkdir_arg *m = ptr;
1593 
1594  return (void *)(VALUE)mkdir(m->path, m->mode);
1595 }
1596 
1597 /*
1598  * call-seq:
1599  * Dir.mkdir(dirpath, permissions = 0775) -> 0
1600  *
1601  * Creates a directory in the underlying file system
1602  * at +dirpath+ with the given +permissions+;
1603  * returns zero:
1604  *
1605  * Dir.mkdir('foo')
1606  * File.stat(Dir.new('foo')).mode.to_s(8)[1..4] # => "0755"
1607  * Dir.mkdir('bar', 0644)
1608  * File.stat(Dir.new('bar')).mode.to_s(8)[1..4] # => "0644"
1609  *
1610  * See {File Permissions}[rdoc-ref:File@File+Permissions].
1611  * Note that argument +permissions+ is ignored on Windows.
1612  */
1613 static VALUE
1614 dir_s_mkdir(int argc, VALUE *argv, VALUE obj)
1615 {
1616  struct mkdir_arg m;
1617  VALUE path, vmode;
1618  int r;
1619 
1620  if (rb_scan_args(argc, argv, "11", &path, &vmode) == 2) {
1621  m.mode = NUM2MODET(vmode);
1622  }
1623  else {
1624  m.mode = 0777;
1625  }
1626 
1627  path = check_dirname(path);
1628  m.path = RSTRING_PTR(path);
1629  r = IO_WITHOUT_GVL_INT(nogvl_mkdir, &m);
1630  if (r < 0)
1631  rb_sys_fail_path(path);
1632 
1633  return INT2FIX(0);
1634 }
1635 
1636 static void *
1637 nogvl_rmdir(void *ptr)
1638 {
1639  const char *path = ptr;
1640 
1641  return (void *)(VALUE)rmdir(path);
1642 }
1643 
1644 /*
1645  * call-seq:
1646  * Dir.rmdir(dirpath) -> 0
1647  *
1648  * Removes the directory at +dirpath+ from the underlying file system:
1649  *
1650  * Dir.rmdir('foo') # => 0
1651  *
1652  * Raises an exception if the directory is not empty.
1653  */
1654 static VALUE
1655 dir_s_rmdir(VALUE obj, VALUE dir)
1656 {
1657  const char *p;
1658  int r;
1659 
1660  dir = check_dirname(dir);
1661  p = RSTRING_PTR(dir);
1662  r = IO_WITHOUT_GVL_INT(nogvl_rmdir, (void *)p);
1663  if (r < 0)
1664  rb_sys_fail_path(dir);
1665 
1666  return INT2FIX(0);
1668 
1669 struct warning_args {
1670 #ifdef RUBY_FUNCTION_NAME_STRING
1671  const char *func;
1672 #endif
1673  const char *mesg;
1674  rb_encoding *enc;
1675 };
1676 
1677 #ifndef RUBY_FUNCTION_NAME_STRING
1678 #define sys_enc_warning_in(func, mesg, enc) sys_enc_warning(mesg, enc)
1679 #endif
1680 
1681 static VALUE
1682 sys_warning_1(VALUE mesg)
1683 {
1684  const struct warning_args *arg = (struct warning_args *)mesg;
1685 #ifdef RUBY_FUNCTION_NAME_STRING
1686  rb_sys_enc_warning(arg->enc, "%s: %s", arg->func, arg->mesg);
1687 #else
1688  rb_sys_enc_warning(arg->enc, "%s", arg->mesg);
1689 #endif
1690  return Qnil;
1691 }
1692 
1693 static void
1694 sys_enc_warning_in(const char *func, const char *mesg, rb_encoding *enc)
1695 {
1696  struct warning_args arg;
1697 #ifdef RUBY_FUNCTION_NAME_STRING
1698  arg.func = func;
1699 #endif
1700  arg.mesg = mesg;
1701  arg.enc = enc;
1702  rb_protect(sys_warning_1, (VALUE)&arg, 0);
1703 }
1704 
1705 #define GLOB_VERBOSE (1U << (sizeof(int) * CHAR_BIT - 1))
1706 #define sys_warning(val, enc) \
1707  ((flags & GLOB_VERBOSE) ? sys_enc_warning_in(RUBY_FUNCTION_NAME_STRING, (val), (enc)) :(void)0)
1708 
1709 static inline size_t
1710 glob_alloc_size(size_t x, size_t y)
1711 {
1712  size_t z;
1713  if (rb_mul_size_overflow(x, y, SSIZE_MAX, &z)) {
1714  rb_memerror(); /* or...? */
1715  }
1716  else {
1717  return z;
1718  }
1719 }
1720 
1721 static inline void *
1722 glob_alloc_n(size_t x, size_t y)
1723 {
1724  return malloc(glob_alloc_size(x, y));
1725 }
1726 
1727 static inline void *
1728 glob_realloc_n(void *p, size_t x, size_t y)
1729 {
1730  return realloc(p, glob_alloc_size(x, y));
1731 }
1732 
1733 #define GLOB_ALLOC(type) ((type *)malloc(sizeof(type)))
1734 #define GLOB_ALLOC_N(type, n) ((type *)glob_alloc_n(sizeof(type), n))
1735 #define GLOB_REALLOC(ptr, size) realloc((ptr), (size))
1736 #define GLOB_REALLOC_N(ptr, n) glob_realloc_n(ptr, sizeof(*(ptr)), n)
1737 #define GLOB_FREE(ptr) free(ptr)
1738 #define GLOB_JUMP_TAG(status) (((status) == -1) ? rb_memerror() : rb_jump_tag(status))
1739 
1740 /*
1741  * ENOTDIR can be returned by stat(2) if a non-leaf element of the path
1742  * is not a directory.
1743  */
1744 ALWAYS_INLINE(static int to_be_ignored(int e));
1745 static inline int
1746 to_be_ignored(int e)
1747 {
1748  return e == ENOENT || e == ENOTDIR;
1749 }
1750 
1751 #ifdef _WIN32
1752 #define STAT(args) (int)(VALUE)nogvl_stat(&(args))
1753 #define LSTAT(args) (int)(VALUE)nogvl_lstat(&(args))
1754 #else
1755 #define STAT(args) IO_WITHOUT_GVL_INT(nogvl_stat, (void *)&(args))
1756 #define LSTAT(args) IO_WITHOUT_GVL_INT(nogvl_lstat, (void *)&(args))
1757 #endif
1759 typedef int ruby_glob_errfunc(const char*, VALUE, const void*, int);
1760 typedef struct {
1761  ruby_glob_func *match;
1762  ruby_glob_errfunc *error;
1764 
1765 static const char *
1766 at_subpath(int fd, size_t baselen, const char *path)
1767 {
1768 #if USE_OPENDIR_AT
1769  if (fd != (int)AT_FDCWD && baselen > 0) {
1770  path += baselen;
1771  if (*path == '/') ++path;
1772  }
1773 #endif
1774  return *path ? path : ".";
1775 }
1776 
1777 #if USE_OPENDIR_AT
1778 struct fstatat_args {
1779  int fd;
1780  int flag;
1781  const char *path;
1782  struct stat *pst;
1783 };
1784 
1785 static void *
1786 nogvl_fstatat(void *args)
1787 {
1788  struct fstatat_args *arg = (struct fstatat_args *)args;
1789  return (void *)(VALUE)fstatat(arg->fd, arg->path, arg->pst, arg->flag);
1791 #else
1792 struct stat_args {
1793  const char *path;
1794  struct stat *pst;
1795 };
1796 
1797 static void *
1798 nogvl_stat(void *args)
1799 {
1800  struct stat_args *arg = (struct stat_args *)args;
1801  return (void *)(VALUE)stat(arg->path, arg->pst);
1802 }
1803 #endif
1804 
1805 /* System call with warning */
1806 static int
1807 do_stat(int fd, size_t baselen, const char *path, struct stat *pst, int flags, rb_encoding *enc)
1808 {
1809 #if USE_OPENDIR_AT
1810  struct fstatat_args args;
1811  args.fd = fd;
1812  args.path = path;
1813  args.pst = pst;
1814  args.flag = 0;
1815  int ret = IO_WITHOUT_GVL_INT(nogvl_fstatat, (void *)&args);
1816 #else
1817  struct stat_args args;
1818  args.path = path;
1819  args.pst = pst;
1820  int ret = STAT(args);
1821 #endif
1822  if (ret < 0 && !to_be_ignored(errno))
1823  sys_warning(path, enc);
1824 
1825  return ret;
1826 }
1827 
1828 #if defined HAVE_LSTAT || defined lstat || USE_OPENDIR_AT
1829 #if !USE_OPENDIR_AT
1830 static void *
1831 nogvl_lstat(void *args)
1832 {
1833  struct stat_args *arg = (struct stat_args *)args;
1834  return (void *)(VALUE)lstat(arg->path, arg->pst);
1835 }
1836 #endif
1837 
1838 static int
1839 do_lstat(int fd, size_t baselen, const char *path, struct stat *pst, int flags, rb_encoding *enc)
1840 {
1841 #if USE_OPENDIR_AT
1842  struct fstatat_args args;
1843  args.fd = fd;
1844  args.path = path;
1845  args.pst = pst;
1846  args.flag = AT_SYMLINK_NOFOLLOW;
1847  int ret = IO_WITHOUT_GVL_INT(nogvl_fstatat, (void *)&args);
1848 #else
1849  struct stat_args args;
1850  args.path = path;
1851  args.pst = pst;
1852  int ret = LSTAT(args);
1853 #endif
1854  if (ret < 0 && !to_be_ignored(errno))
1855  sys_warning(path, enc);
1856 
1857  return ret;
1858 }
1859 #else
1860 #define do_lstat do_stat
1861 #endif
1862 
1863 struct opendir_at_arg {
1864  int basefd;
1865  const char *path;
1866 };
1867 
1868 static void *
1869 with_gvl_gc_for_fd(void *ptr)
1870 {
1871  int *e = ptr;
1872 
1873  return (void *)RBOOL(rb_gc_for_fd(*e));
1874 }
1875 
1876 static int
1877 gc_for_fd_with_gvl(int e)
1878 {
1879  if (vm_initialized)
1880  return (int)(VALUE)rb_thread_call_with_gvl(with_gvl_gc_for_fd, &e);
1881  else
1882  return RBOOL(rb_gc_for_fd(e));
1883 }
1884 
1885 static void *
1886 nogvl_opendir_at(void *ptr)
1887 {
1888  const struct opendir_at_arg *oaa = ptr;
1889  DIR *dirp;
1890 
1891 #if USE_OPENDIR_AT
1892  const int opendir_flags = (O_RDONLY|O_CLOEXEC|
1893 # ifdef O_DIRECTORY
1894  O_DIRECTORY|
1895 # endif /* O_DIRECTORY */
1896  0);
1897  int fd = openat(oaa->basefd, oaa->path, opendir_flags);
1898 
1899  dirp = fd >= 0 ? fdopendir(fd) : 0;
1900  if (!dirp) {
1901  int e = errno;
1902 
1903  switch (gc_for_fd_with_gvl(e)) {
1904  default:
1905  if (fd < 0) fd = openat(oaa->basefd, oaa->path, opendir_flags);
1906  if (fd >= 0) dirp = fdopendir(fd);
1907  if (dirp) return dirp;
1908 
1909  e = errno;
1910  /* fallthrough*/
1911  case 0:
1912  if (fd >= 0) close(fd);
1913  rb_errno_set(e);
1914  }
1915  }
1916 #else /* !USE_OPENDIR_AT */
1917  dirp = opendir(oaa->path);
1918  if (!dirp && gc_for_fd_with_gvl(errno))
1919  dirp = opendir(oaa->path);
1920 #endif /* !USE_OPENDIR_AT */
1921 
1922  return dirp;
1923 }
1924 
1925 static DIR *
1926 opendir_at(int basefd, const char *path)
1927 {
1928  struct opendir_at_arg oaa;
1929 
1930  oaa.basefd = basefd;
1931  oaa.path = path;
1932 
1933  if (vm_initialized)
1934  return IO_WITHOUT_GVL(nogvl_opendir_at, &oaa);
1935  else
1936  return nogvl_opendir_at(&oaa);
1937 }
1938 
1939 static DIR *
1940 do_opendir(const int basefd, size_t baselen, const char *path, int flags, rb_encoding *enc,
1941  ruby_glob_errfunc *errfunc, VALUE arg, int *status)
1942 {
1943  DIR *dirp;
1944 #ifdef _WIN32
1945  VALUE tmp = 0;
1946  if (!fundamental_encoding_p(enc)) {
1947  tmp = rb_enc_str_new(path, strlen(path), enc);
1948  tmp = rb_str_encode_ospath(tmp);
1949  path = RSTRING_PTR(tmp);
1950  }
1951 #endif
1952  dirp = opendir_at(basefd, at_subpath(basefd, baselen, path));
1953  if (!dirp) {
1954  int e = errno;
1955 
1956  *status = 0;
1957  if (!to_be_ignored(e)) {
1958  if (errfunc) {
1959  *status = (*errfunc)(path, arg, enc, e);
1960  }
1961  else {
1962  sys_warning(path, enc);
1963  }
1964  }
1965  }
1966 #ifdef _WIN32
1967  if (tmp) rb_str_resize(tmp, 0); /* GC guard */
1968 #endif
1969 
1970  return dirp;
1971 }
1972 
1973 /* Globing pattern */
1974 enum glob_pattern_type { PLAIN, ALPHA, BRACE, MAGICAL, RECURSIVE, MATCH_ALL, MATCH_DIR };
1975 
1976 /* Return nonzero if S has any special globbing chars in it. */
1977 static enum glob_pattern_type
1978 has_magic(const char *p, const char *pend, int flags, rb_encoding *enc)
1979 {
1980  const int escape = !(flags & FNM_NOESCAPE);
1981  int hasalpha = 0;
1982  int hasmagical = 0;
1983 
1984  register char c;
1985 
1986  while (p < pend && (c = *p++) != 0) {
1987  switch (c) {
1988  case '{':
1989  return BRACE;
1990 
1991  case '*':
1992  case '?':
1993  case '[':
1994  hasmagical = 1;
1995  break;
1996 
1997  case '\\':
1998  if (escape && p++ >= pend)
1999  continue;
2000  break;
2001 
2002 #ifdef _WIN32
2003  case '.':
2004  break;
2005 
2006  case '~':
2007  hasalpha = 1;
2008  break;
2009 #endif
2010  default:
2011  if (IS_WIN32 || ISALPHA(c)) {
2012  hasalpha = 1;
2013  }
2014  break;
2015  }
2016 
2017  p = Next(p-1, pend, enc);
2018  }
2019 
2020  return hasmagical ? MAGICAL : hasalpha ? ALPHA : PLAIN;
2021 }
2022 
2023 /* Find separator in globbing pattern. */
2024 static char *
2025 find_dirsep(const char *p, const char *pend, int flags, rb_encoding *enc)
2026 {
2027  const int escape = !(flags & FNM_NOESCAPE);
2028 
2029  register char c;
2030  int open = 0;
2031 
2032  while ((c = *p++) != 0) {
2033  switch (c) {
2034  case '[':
2035  open = 1;
2036  continue;
2037  case ']':
2038  open = 0;
2039  continue;
2040 
2041  case '{':
2042  open = 1;
2043  continue;
2044  case '}':
2045  open = 0;
2046  continue;
2047 
2048  case '/':
2049  if (!open)
2050  return (char *)p-1;
2051  continue;
2052 
2053  case '\\':
2054  if (escape && !(c = *p++))
2055  return (char *)p-1;
2056  continue;
2057  }
2058 
2059  p = Next(p-1, pend, enc);
2060  }
2061 
2062  return (char *)p-1;
2063 }
2064 
2065 /* Remove escaping backslashes */
2066 static char *
2067 remove_backslashes(char *p, register const char *pend, rb_encoding *enc)
2068 {
2069  char *t = p;
2070  char *s = p;
2071 
2072  while (*p) {
2073  if (*p == '\\') {
2074  if (t != s)
2075  memmove(t, s, p - s);
2076  t += p - s;
2077  s = ++p;
2078  if (!*p) break;
2079  }
2080  Inc(p, pend, enc);
2081  }
2082 
2083  while (*p++);
2084 
2085  if (t != s)
2086  memmove(t, s, p - s); /* move '\0' too */
2087 
2088  return p;
2090 
2091 struct glob_pattern {
2092  char *str;
2093  enum glob_pattern_type type;
2094  struct glob_pattern *next;
2095 };
2096 
2097 static void glob_free_pattern(struct glob_pattern *list);
2098 
2099 static struct glob_pattern *
2100 glob_make_pattern(const char *p, const char *e, int flags, rb_encoding *enc)
2101 {
2102  struct glob_pattern *list, *tmp, **tail = &list;
2103  int dirsep = 0; /* pattern is terminated with '/' */
2104  int recursive = 0;
2105 
2106  while (p < e && *p) {
2107  tmp = GLOB_ALLOC(struct glob_pattern);
2108  if (!tmp) goto error;
2109  if (p + 2 < e && p[0] == '*' && p[1] == '*' && p[2] == '/') {
2110  /* fold continuous RECURSIVEs (needed in glob_helper) */
2111  do { p += 3; while (*p == '/') p++; } while (p[0] == '*' && p[1] == '*' && p[2] == '/');
2112  tmp->type = RECURSIVE;
2113  tmp->str = 0;
2114  dirsep = 1;
2115  recursive = 1;
2116  }
2117  else {
2118  const char *m = find_dirsep(p, e, flags, enc);
2119  const enum glob_pattern_type magic = has_magic(p, m, flags, enc);
2120  const enum glob_pattern_type non_magic = (USE_NAME_ON_FS || FNM_SYSCASE) ? PLAIN : ALPHA;
2121  char *buf;
2122 
2123  if (!(FNM_SYSCASE || magic > non_magic) && !recursive && *m) {
2124  const char *m2;
2125  while (has_magic(m+1, m2 = find_dirsep(m+1, e, flags, enc), flags, enc) <= non_magic &&
2126  *m2) {
2127  m = m2;
2128  }
2129  }
2130  buf = GLOB_ALLOC_N(char, m-p+1);
2131  if (!buf) {
2132  GLOB_FREE(tmp);
2133  goto error;
2134  }
2135  memcpy(buf, p, m-p);
2136  buf[m-p] = '\0';
2137  tmp->type = magic > MAGICAL ? MAGICAL : magic > non_magic ? magic : PLAIN;
2138  tmp->str = buf;
2139  if (*m) {
2140  dirsep = 1;
2141  p = m + 1;
2142  }
2143  else {
2144  dirsep = 0;
2145  p = m;
2146  }
2147  }
2148  *tail = tmp;
2149  tail = &tmp->next;
2150  }
2151 
2152  tmp = GLOB_ALLOC(struct glob_pattern);
2153  if (!tmp) {
2154  goto error;
2155  }
2156  tmp->type = dirsep ? MATCH_DIR : MATCH_ALL;
2157  tmp->str = 0;
2158  *tail = tmp;
2159  tmp->next = 0;
2160 
2161  return list;
2162 
2163  error:
2164  *tail = 0;
2165  glob_free_pattern(list);
2166  return 0;
2167 }
2168 
2169 static void
2170 glob_free_pattern(struct glob_pattern *list)
2171 {
2172  while (list) {
2173  struct glob_pattern *tmp = list;
2174  list = list->next;
2175  if (tmp->str)
2176  GLOB_FREE(tmp->str);
2177  GLOB_FREE(tmp);
2178  }
2179 }
2180 
2181 static char *
2182 join_path(const char *path, size_t len, int dirsep, const char *name, size_t namlen)
2183 {
2184  char *buf = GLOB_ALLOC_N(char, len+namlen+(dirsep?1:0)+1);
2185 
2186  if (!buf) return 0;
2187  memcpy(buf, path, len);
2188  if (dirsep) {
2189  buf[len++] = '/';
2190  }
2191  memcpy(buf+len, name, namlen);
2192  buf[len+namlen] = '\0';
2193  return buf;
2194 }
2195 
2196 #ifdef HAVE_GETATTRLIST
2197 # if defined HAVE_FGETATTRLIST
2198 # define is_case_sensitive(dirp, path) is_case_sensitive(dirp)
2199 # else
2200 # define is_case_sensitive(dirp, path) is_case_sensitive(path)
2201 # endif
2202 static int
2203 is_case_sensitive(DIR *dirp, const char *path)
2204 {
2205  struct {
2206  u_int32_t length;
2207  vol_capabilities_attr_t cap[1];
2208  } __attribute__((aligned(4), packed)) attrbuf[1];
2209  struct attrlist al = {ATTR_BIT_MAP_COUNT, 0, 0, ATTR_VOL_INFO|ATTR_VOL_CAPABILITIES};
2210  const vol_capabilities_attr_t *const cap = attrbuf[0].cap;
2211  const int idx = VOL_CAPABILITIES_FORMAT;
2212  const uint32_t mask = VOL_CAP_FMT_CASE_SENSITIVE;
2213  struct getattrlist_args args = GETATTRLIST_ARGS(&al, attrbuf, FSOPT_NOFOLLOW);
2214 # if defined HAVE_FGETATTRLIST
2215  int ret = gvl_fgetattrlist(&args, dirfd(dirp));
2216 # else
2217  int ret = gvl_getattrlist(&args, path);
2218 # endif
2219  if (ret)
2220  return -1;
2221 
2222  if (!(cap->valid[idx] & mask))
2223  return -1;
2224  return (cap->capabilities[idx] & mask) != 0;
2225 }
2226 
2227 static char *
2228 replace_real_basename(char *path, long base, rb_encoding *enc, int norm_p, int flags, rb_pathtype_t *type)
2229 {
2230  struct {
2231  u_int32_t length;
2232  attrreference_t ref[1];
2233  fsobj_type_t objtype;
2234  char path[MAXPATHLEN * 3];
2235  } __attribute__((aligned(4), packed)) attrbuf[1];
2236  struct attrlist al = {ATTR_BIT_MAP_COUNT, 0, ATTR_CMN_NAME|ATTR_CMN_OBJTYPE};
2237  const attrreference_t *const ar = attrbuf[0].ref;
2238  const char *name;
2239  long len;
2240  char *tmp;
2241  IF_NORMALIZE_UTF8PATH(VALUE utf8str = Qnil);
2242 
2243  *type = path_noent;
2244  struct getattrlist_args args = GETATTRLIST_ARGS(&al, attrbuf, FSOPT_NOFOLLOW);
2245  if (gvl_getattrlist(&args, path)) {
2246  if (!to_be_ignored(errno))
2247  sys_warning(path, enc);
2248  return path;
2249  }
2250 
2251  switch (attrbuf[0].objtype) {
2252  case VREG: *type = path_regular; break;
2253  case VDIR: *type = path_directory; break;
2254  case VLNK: *type = path_symlink; break;
2255  default: *type = path_exist; break;
2256  }
2257  name = (char *)ar + ar->attr_dataoffset;
2258  len = (long)ar->attr_length - 1;
2259  if (name + len > (char *)attrbuf + sizeof(attrbuf))
2260  return path;
2261 
2262 # if NORMALIZE_UTF8PATH
2263  if (norm_p && has_nonascii(name, len)) {
2264  if (!NIL_P(utf8str = rb_str_normalize_ospath(name, len))) {
2265  RSTRING_GETMEM(utf8str, name, len);
2266  }
2267  }
2268 # endif
2269 
2270  tmp = GLOB_REALLOC(path, base + len + 1);
2271  if (tmp) {
2272  path = tmp;
2273  memcpy(path + base, name, len);
2274  path[base + len] = '\0';
2275  }
2276  IF_NORMALIZE_UTF8PATH(if (!NIL_P(utf8str)) rb_str_resize(utf8str, 0));
2277  return path;
2278 }
2279 #elif defined _WIN32
2280 VALUE rb_w32_conv_from_wchar(const WCHAR *wstr, rb_encoding *enc);
2281 int rb_w32_reparse_symlink_p(const WCHAR *path);
2282 
2283 static char *
2284 replace_real_basename(char *path, long base, rb_encoding *enc, int norm_p, int flags, rb_pathtype_t *type)
2285 {
2286  char *plainname = path;
2287  volatile VALUE tmp = 0;
2288  WIN32_FIND_DATAW fd;
2289  WIN32_FILE_ATTRIBUTE_DATA fa;
2290  WCHAR *wplain;
2291  HANDLE h = INVALID_HANDLE_VALUE;
2292  long wlen;
2293  int e = 0;
2294  if (!fundamental_encoding_p(enc)) {
2295  tmp = rb_enc_str_new_cstr(plainname, enc);
2296  tmp = rb_str_encode_ospath(tmp);
2297  plainname = RSTRING_PTR(tmp);
2298  }
2299  wplain = rb_w32_mbstr_to_wstr(CP_UTF8, plainname, -1, &wlen);
2300  if (tmp) rb_str_resize(tmp, 0);
2301  if (!wplain) return path;
2302  if (GetFileAttributesExW(wplain, GetFileExInfoStandard, &fa)) {
2303  h = FindFirstFileW(wplain, &fd);
2304  e = rb_w32_map_errno(GetLastError());
2305  }
2306  if (fa.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
2307  if (!rb_w32_reparse_symlink_p(wplain))
2308  fa.dwFileAttributes &= ~FILE_ATTRIBUTE_REPARSE_POINT;
2309  }
2310  free(wplain);
2311  if (h == INVALID_HANDLE_VALUE) {
2312  *type = path_noent;
2313  if (e && !to_be_ignored(e)) {
2314  errno = e;
2315  sys_warning(path, enc);
2316  }
2317  return path;
2318  }
2319  FindClose(h);
2320  *type =
2321  (fa.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) ? path_symlink :
2322  (fa.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? path_directory :
2323  path_regular;
2324  if (tmp) {
2325  char *buf;
2326  tmp = rb_w32_conv_from_wchar(fd.cFileName, enc);
2327  wlen = RSTRING_LEN(tmp);
2328  buf = GLOB_REALLOC(path, base + wlen + 1);
2329  if (buf) {
2330  path = buf;
2331  memcpy(path + base, RSTRING_PTR(tmp), wlen);
2332  path[base + wlen] = 0;
2333  }
2334  rb_str_resize(tmp, 0);
2335  }
2336  else {
2337  char *utf8filename;
2338  wlen = WideCharToMultiByte(CP_UTF8, 0, fd.cFileName, -1, NULL, 0, NULL, NULL);
2339  utf8filename = GLOB_REALLOC(0, wlen);
2340  if (utf8filename) {
2341  char *buf;
2342  WideCharToMultiByte(CP_UTF8, 0, fd.cFileName, -1, utf8filename, wlen, NULL, NULL);
2343  buf = GLOB_REALLOC(path, base + wlen + 1);
2344  if (buf) {
2345  path = buf;
2346  memcpy(path + base, utf8filename, wlen);
2347  path[base + wlen] = 0;
2348  }
2349  GLOB_FREE(utf8filename);
2350  }
2351  }
2352  return path;
2353 }
2354 #elif USE_NAME_ON_FS == USE_NAME_ON_FS_REAL_BASENAME
2355 # error not implemented
2356 #endif
2357 
2358 #ifndef S_ISDIR
2359 # define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
2360 #endif
2361 
2362 #ifndef S_ISLNK
2363 # ifndef S_IFLNK
2364 # define S_ISLNK(m) (0)
2365 # else
2366 # define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK)
2367 # endif
2368 #endif
2369 
2370 struct glob_args {
2371  void (*func)(const char *, VALUE, void *);
2372  const char *path;
2373  const char *base;
2374  size_t baselen;
2375  VALUE value;
2376  rb_encoding *enc;
2377 };
2378 
2379 #define glob_call_func(func, path, arg, enc) (*(func))((path), (arg), (void *)(enc))
2380 
2381 static VALUE
2382 glob_func_caller(VALUE val)
2383 {
2384  struct glob_args *args = (struct glob_args *)val;
2385 
2386  glob_call_func(args->func, args->path, args->value, args->enc);
2387  return Qnil;
2389 
2390 struct glob_error_args {
2391  const char *path;
2392  rb_encoding *enc;
2393  int error;
2394 };
2395 
2396 static VALUE
2397 glob_func_warning(VALUE val)
2398 {
2399  struct glob_error_args *arg = (struct glob_error_args *)val;
2400  rb_syserr_enc_warning(arg->error, arg->enc, "%s", arg->path);
2401  return Qnil;
2402 }
2403 
2404 #if 0
2405 static int
2406 rb_glob_warning(const char *path, VALUE a, const void *enc, int error)
2407 {
2408  int status;
2409  struct glob_error_args args;
2410 
2411  args.path = path;
2412  args.enc = enc;
2413  args.error = error;
2414  rb_protect(glob_func_warning, (VALUE)&args, &status);
2415  return status;
2416 }
2417 #endif
2418 
2419 NORETURN(static VALUE glob_func_error(VALUE val));
2420 
2421 static VALUE
2422 glob_func_error(VALUE val)
2423 {
2424  struct glob_error_args *arg = (struct glob_error_args *)val;
2425  VALUE path = rb_enc_str_new_cstr(arg->path, arg->enc);
2426  rb_syserr_fail_str(arg->error, path);
2428 }
2429 
2430 static int
2431 rb_glob_error(const char *path, VALUE a, const void *enc, int error)
2432 {
2433  int status;
2434  struct glob_error_args args;
2435  VALUE (*errfunc)(VALUE) = glob_func_error;
2436 
2437  switch (error) {
2438  case EACCES:
2439 #ifdef ENOTCAPABLE
2440  case ENOTCAPABLE:
2441 #endif
2442  errfunc = glob_func_warning;
2443  }
2444  args.path = path;
2445  args.enc = enc;
2446  args.error = error;
2447  rb_protect(errfunc, (VALUE)&args, &status);
2448  return status;
2450 
2451 typedef struct rb_dirent {
2452  long d_namlen;
2453  const char *d_name;
2454 #ifdef _WIN32
2455  const char *d_altname;
2456 #endif
2457  uint8_t d_type;
2458 } rb_dirent_t;
2459 
2460 static inline int
2461 dirent_match(const char *pat, rb_encoding *enc, const char *name, const rb_dirent_t *dp, int flags)
2462 {
2463  if (fnmatch(pat, enc, name, flags) == 0) return 1;
2464 #ifdef _WIN32
2465  if (dp->d_altname && (flags & FNM_SHORTNAME)) {
2466  if (fnmatch(pat, enc, dp->d_altname, flags) == 0) return 1;
2467  }
2468 #endif
2469  return 0;
2471 
2472 struct push_glob_args {
2473  int fd;
2474  const char *path;
2475  size_t baselen;
2476  size_t namelen;
2477  int dirsep; /* '/' should be placed before appending child entry's name to 'path'. */
2478  rb_pathtype_t pathtype; /* type of 'path' */
2479  int flags;
2480  const ruby_glob_funcs_t *funcs;
2481  VALUE arg;
2482 };
2483 
2484 struct dirent_brace_args {
2485  const char *name;
2486  const rb_dirent_t *dp;
2487  int flags;
2488 };
2489 
2490 static int
2491 dirent_match_brace(const char *pattern, VALUE val, void *enc)
2492 {
2493  struct dirent_brace_args *arg = (struct dirent_brace_args *)val;
2494 
2495  return dirent_match(pattern, enc, arg->name, arg->dp, arg->flags);
2496 }
2497 
2498 /* join paths from pattern list of glob_make_pattern() */
2499 static char*
2500 join_path_from_pattern(struct glob_pattern **beg)
2501 {
2502  struct glob_pattern *p;
2503  char *path = NULL;
2504  size_t path_len = 0;
2505 
2506  for (p = *beg; p; p = p->next) {
2507  const char *str;
2508  switch (p->type) {
2509  case RECURSIVE:
2510  str = "**";
2511  break;
2512  case MATCH_DIR:
2513  /* append last slash */
2514  str = "";
2515  break;
2516  default:
2517  str = p->str;
2518  if (!str) continue;
2519  }
2520  if (!path) {
2521  path_len = strlen(str);
2522  path = GLOB_ALLOC_N(char, path_len + 1);
2523  if (path) {
2524  memcpy(path, str, path_len);
2525  path[path_len] = '\0';
2526  }
2527  }
2528  else {
2529  size_t len = strlen(str);
2530  char *tmp;
2531  tmp = GLOB_REALLOC(path, path_len + len + 2);
2532  if (tmp) {
2533  path = tmp;
2534  path[path_len++] = '/';
2535  memcpy(path + path_len, str, len);
2536  path_len += len;
2537  path[path_len] = '\0';
2538  }
2539  }
2540  }
2541  return path;
2542 }
2543 
2544 static int push_caller(const char *path, VALUE val, void *enc);
2545 
2546 static int ruby_brace_expand(const char *str, int flags, ruby_glob_func *func, VALUE arg,
2547  rb_encoding *enc, VALUE var);
2548 
2549 static const size_t rb_dirent_name_offset =
2550  offsetof(rb_dirent_t, d_type) + sizeof(uint8_t);
2551 
2552 static rb_dirent_t *
2553 dirent_copy(const struct dirent *dp, rb_dirent_t *rdp)
2554 {
2555  if (!dp) return NULL;
2556  size_t namlen = NAMLEN(dp);
2557  const size_t altlen =
2558 #ifdef _WIN32
2559  dp->d_altlen ? dp->d_altlen + 1 :
2560 #endif
2561  0;
2562  rb_dirent_t *newrdp = rdp;
2563  if (!rdp && !(newrdp = malloc(rb_dirent_name_offset + namlen + 1 + altlen)))
2564  return NULL;
2565  newrdp->d_namlen = namlen;
2566  if (!rdp) {
2567  char *name = (char *)newrdp + rb_dirent_name_offset;
2568  memcpy(name, dp->d_name, namlen);
2569  name[namlen] = '\0';
2570 #ifdef _WIN32
2571  newrdp->d_altname = NULL;
2572  if (altlen) {
2573  char *const altname = name + namlen + 1;
2574  memcpy(altname, dp->d_altname, altlen - 1);
2575  altname[altlen - 1] = '\0';
2576  newrdp->d_altname = altname;
2577  }
2578 #endif
2579  newrdp->d_name = name;
2580  }
2581  else {
2582  newrdp->d_name = dp->d_name;
2583 #ifdef _WIN32
2584  newrdp->d_altname = dp->d_altname;
2585 #endif
2586  }
2587 #if !EMULATE_IFTODT
2588  newrdp->d_type = dp->d_type;
2589 #else
2590  newrdp->d_type = 0;
2591 #endif
2592  return newrdp;
2594 
2595 typedef union {
2596  struct {
2597  DIR *dirp;
2598  rb_dirent_t ent;
2599  } nosort;
2600  struct {
2601  size_t count, idx;
2602  rb_dirent_t **entries;
2603  } sort;
2605 
2606 static int
2607 glob_sort_cmp(const void *a, const void *b, void *e)
2608 {
2609  const rb_dirent_t *ent1 = *(void **)a;
2610  const rb_dirent_t *ent2 = *(void **)b;
2611  return strcmp(ent1->d_name, ent2->d_name);
2612 }
2613 
2614 static void
2615 glob_dir_finish(ruby_glob_entries_t *ent, int flags)
2616 {
2617  if (flags & FNM_GLOB_NOSORT) {
2618  check_closedir(ent->nosort.dirp);
2619  ent->nosort.dirp = NULL;
2620  }
2621  else if (ent->sort.entries) {
2622  for (size_t i = 0, count = ent->sort.count; i < count;) {
2623  GLOB_FREE(ent->sort.entries[i++]);
2624  }
2625  GLOB_FREE(ent->sort.entries);
2626  ent->sort.entries = NULL;
2627  ent->sort.count = ent->sort.idx = 0;
2628  }
2629 }
2630 
2631 static ruby_glob_entries_t *
2632 glob_opendir(ruby_glob_entries_t *ent, DIR *dirp, int flags, rb_encoding *enc)
2633 {
2634  MEMZERO(ent, ruby_glob_entries_t, 1);
2635  if (flags & FNM_GLOB_NOSORT) {
2636  ent->nosort.dirp = dirp;
2637  return ent;
2638  }
2639  else {
2640  void *newp;
2641  struct dirent *dp;
2642  size_t count = 0, capacity = 0;
2643  ent->sort.count = 0;
2644  ent->sort.idx = 0;
2645  ent->sort.entries = 0;
2646 #ifdef _WIN32
2647  if ((capacity = dirp->nfiles) > 0) {
2648  if (!(newp = GLOB_ALLOC_N(rb_dirent_t, capacity))) {
2649  check_closedir(dirp);
2650  return NULL;
2651  }
2652  ent->sort.entries = newp;
2653  }
2654 #endif
2655  while ((dp = READDIR(dirp, enc)) != NULL) {
2656  rb_dirent_t *rdp = dirent_copy(dp, NULL);
2657  if (!rdp) {
2658  goto nomem;
2659  }
2660  if (count >= capacity) {
2661  capacity += 256;
2662  if (!(newp = GLOB_REALLOC_N(ent->sort.entries, capacity)))
2663  goto nomem;
2664  ent->sort.entries = newp;
2665  }
2666  ent->sort.entries[count++] = rdp;
2667  ent->sort.count = count;
2668  }
2669  check_closedir(dirp);
2670  if (count < capacity) {
2671  if (!(newp = GLOB_REALLOC_N(ent->sort.entries, count))) {
2672  glob_dir_finish(ent, 0);
2673  return NULL;
2674  }
2675  ent->sort.entries = newp;
2676  }
2677  ruby_qsort(ent->sort.entries, ent->sort.count, sizeof(ent->sort.entries[0]),
2678  glob_sort_cmp, NULL);
2679  return ent;
2680  }
2681 
2682  nomem:
2683  glob_dir_finish(ent, 0);
2684  check_closedir(dirp);
2685  return NULL;
2686 }
2687 
2688 static rb_dirent_t *
2689 glob_getent(ruby_glob_entries_t *ent, int flags, rb_encoding *enc)
2690 {
2691  if (flags & FNM_GLOB_NOSORT) {
2692  return dirent_copy(READDIR(ent->nosort.dirp, enc), &ent->nosort.ent);
2693  }
2694  else if (ent->sort.idx < ent->sort.count) {
2695  return ent->sort.entries[ent->sort.idx++];
2696  }
2697  else {
2698  return NULL;
2699  }
2700 }
2701 
2702 static int
2703 glob_helper(
2704  int fd,
2705  const char *path,
2706  size_t baselen,
2707  size_t namelen,
2708  int dirsep, /* '/' should be placed before appending child entry's name to 'path'. */
2709  rb_pathtype_t pathtype, /* type of 'path' */
2710  struct glob_pattern **beg,
2711  struct glob_pattern **end,
2712  int flags,
2713  const ruby_glob_funcs_t *funcs,
2714  VALUE arg,
2715  rb_encoding *enc)
2716 {
2717  struct stat st;
2718  int status = 0;
2719  struct glob_pattern **cur, **new_beg, **new_end;
2720  int plain = 0, brace = 0, magical = 0, recursive = 0, match_all = 0, match_dir = 0;
2721  int escape = !(flags & FNM_NOESCAPE);
2722  size_t pathlen = baselen + namelen;
2723 
2724  rb_check_stack_overflow();
2725 
2726  for (cur = beg; cur < end; ++cur) {
2727  struct glob_pattern *p = *cur;
2728  if (p->type == RECURSIVE) {
2729  recursive = 1;
2730  p = p->next;
2731  }
2732  switch (p->type) {
2733  case PLAIN:
2734  plain = 1;
2735  break;
2736  case ALPHA:
2737 #if USE_NAME_ON_FS == USE_NAME_ON_FS_REAL_BASENAME
2738  plain = 1;
2739 #else
2740  magical = 1;
2741 #endif
2742  break;
2743  case BRACE:
2744  if (!recursive || strchr(p->str, '/')) {
2745  brace = 1;
2746  }
2747  break;
2748  case MAGICAL:
2749  magical = 2;
2750  break;
2751  case MATCH_ALL:
2752  match_all = 1;
2753  break;
2754  case MATCH_DIR:
2755  match_dir = 1;
2756  break;
2757  case RECURSIVE:
2758  rb_bug("continuous RECURSIVEs");
2759  }
2760  }
2761 
2762  if (brace) {
2763  struct push_glob_args args;
2764  char* brace_path = join_path_from_pattern(beg);
2765  if (!brace_path) return -1;
2766  args.fd = fd;
2767  args.path = path;
2768  args.baselen = baselen;
2769  args.namelen = namelen;
2770  args.dirsep = dirsep;
2771  args.pathtype = pathtype;
2772  args.flags = flags;
2773  args.funcs = funcs;
2774  args.arg = arg;
2775  status = ruby_brace_expand(brace_path, flags, push_caller, (VALUE)&args, enc, Qfalse);
2776  GLOB_FREE(brace_path);
2777  return status;
2778  }
2779 
2780  if (*path) {
2781  if (match_all && pathtype == path_unknown) {
2782  if (do_lstat(fd, baselen, path, &st, flags, enc) == 0) {
2783  pathtype = IFTODT(st.st_mode);
2784  }
2785  else {
2786  pathtype = path_noent;
2787  }
2788  }
2789  if (match_dir && (pathtype == path_unknown || pathtype == path_symlink)) {
2790  if (do_stat(fd, baselen, path, &st, flags, enc) == 0) {
2791  pathtype = IFTODT(st.st_mode);
2792  }
2793  else {
2794  pathtype = path_noent;
2795  }
2796  }
2797  if (match_all && pathtype > path_noent) {
2798  const char *subpath = path + baselen + (baselen && path[baselen] == '/');
2799  status = glob_call_func(funcs->match, subpath, arg, enc);
2800  if (status) return status;
2801  }
2802  if (match_dir && pathtype == path_directory) {
2803  int seplen = (baselen && path[baselen] == '/');
2804  const char *subpath = path + baselen + seplen;
2805  char *tmp = join_path(subpath, namelen - seplen, dirsep, "", 0);
2806  if (!tmp) return -1;
2807  status = glob_call_func(funcs->match, tmp, arg, enc);
2808  GLOB_FREE(tmp);
2809  if (status) return status;
2810  }
2811  }
2812 
2813  if (pathtype == path_noent) return 0;
2814 
2815  if (magical || recursive) {
2816  rb_dirent_t *dp;
2817  DIR *dirp;
2818 # if USE_NAME_ON_FS == USE_NAME_ON_FS_BY_FNMATCH
2819  char *plainname = 0;
2820 # endif
2821  IF_NORMALIZE_UTF8PATH(int norm_p);
2822 # if USE_NAME_ON_FS == USE_NAME_ON_FS_BY_FNMATCH
2823  if (cur + 1 == end && (*cur)->type <= ALPHA) {
2824  plainname = join_path(path, pathlen, dirsep, (*cur)->str, strlen((*cur)->str));
2825  if (!plainname) return -1;
2826  dirp = do_opendir(fd, basename, plainname, flags, enc, funcs->error, arg, &status);
2827  GLOB_FREE(plainname);
2828  }
2829  else
2830 # else
2831  ;
2832 # endif
2833  dirp = do_opendir(fd, baselen, path, flags, enc, funcs->error, arg, &status);
2834  if (dirp == NULL) {
2835 # if FNM_SYSCASE || NORMALIZE_UTF8PATH
2836  if ((magical < 2) && !recursive && (errno == EACCES)) {
2837  /* no read permission, fallback */
2838  goto literally;
2839  }
2840 # endif
2841  return status;
2842  }
2843  IF_NORMALIZE_UTF8PATH(norm_p = need_normalization(dirp, *path ? path : "."));
2844 
2845 # if NORMALIZE_UTF8PATH
2846  if (!(norm_p || magical || recursive)) {
2847  check_closedir(dirp);
2848  goto literally;
2849  }
2850 # endif
2851 # ifdef HAVE_GETATTRLIST
2852  if (is_case_sensitive(dirp, path) == 0)
2853  flags |= FNM_CASEFOLD;
2854 # endif
2855  ruby_glob_entries_t globent;
2856  if (!glob_opendir(&globent, dirp, flags, enc)) {
2857  status = 0;
2858  if (funcs->error) {
2859  status = (*funcs->error)(path, arg, enc, ENOMEM);
2860  }
2861  else {
2862  sys_warning(path, enc);
2863  }
2864  return status;
2865  }
2866 
2867  int skipdot = (flags & FNM_GLOB_SKIPDOT);
2868  flags |= FNM_GLOB_SKIPDOT;
2869 
2870  while ((dp = glob_getent(&globent, flags, enc)) != NULL) {
2871  char *buf;
2872  rb_pathtype_t new_pathtype = path_unknown;
2873  const char *name;
2874  size_t namlen;
2875  int dotfile = 0;
2876  IF_NORMALIZE_UTF8PATH(VALUE utf8str = Qnil);
2877 
2878  name = dp->d_name;
2879  namlen = dp->d_namlen;
2880  if (name[0] == '.') {
2881  ++dotfile;
2882  if (namlen == 1) {
2883  /* unless DOTMATCH, skip current directories not to recurse infinitely */
2884  if (recursive && !(flags & FNM_DOTMATCH)) continue;
2885  if (skipdot) continue;
2886  ++dotfile;
2887  new_pathtype = path_directory; /* force to skip stat/lstat */
2888  }
2889  else if (namlen == 2 && name[1] == '.') {
2890  /* always skip parent directories not to recurse infinitely */
2891  continue;
2892  }
2893  }
2894 
2895 # if NORMALIZE_UTF8PATH
2896  if (norm_p && has_nonascii(name, namlen)) {
2897  if (!NIL_P(utf8str = rb_str_normalize_ospath(name, namlen))) {
2898  RSTRING_GETMEM(utf8str, name, namlen);
2899  }
2900  }
2901 # endif
2902  buf = join_path(path, pathlen, dirsep, name, namlen);
2903  IF_NORMALIZE_UTF8PATH(if (!NIL_P(utf8str)) rb_str_resize(utf8str, 0));
2904  if (!buf) {
2905  status = -1;
2906  break;
2907  }
2908  name = buf + pathlen + (dirsep != 0);
2909 #if !EMULATE_IFTODT
2910  if (dp->d_type != DT_UNKNOWN) {
2911  /* Got it. We need no more lstat. */
2912  new_pathtype = dp->d_type;
2913  }
2914 #endif
2915  if (recursive && dotfile < ((flags & FNM_DOTMATCH) ? 2 : 1) &&
2916  new_pathtype == path_unknown) {
2917  /* RECURSIVE never match dot files unless FNM_DOTMATCH is set */
2918  if (do_lstat(fd, baselen, buf, &st, flags, enc) == 0)
2919  new_pathtype = IFTODT(st.st_mode);
2920  else
2921  new_pathtype = path_noent;
2922  }
2923 
2924  new_beg = new_end = GLOB_ALLOC_N(struct glob_pattern *, (end - beg) * 2);
2925  if (!new_beg) {
2926  GLOB_FREE(buf);
2927  status = -1;
2928  break;
2929  }
2930 
2931  for (cur = beg; cur < end; ++cur) {
2932  struct glob_pattern *p = *cur;
2933  struct dirent_brace_args args;
2934  if (p->type == RECURSIVE) {
2935  if (new_pathtype == path_directory || /* not symlink but real directory */
2936  new_pathtype == path_exist) {
2937  if (dotfile < ((flags & FNM_DOTMATCH) ? 2 : 1))
2938  *new_end++ = p; /* append recursive pattern */
2939  }
2940  p = p->next; /* 0 times recursion */
2941  }
2942  switch (p->type) {
2943  case BRACE:
2944  args.name = name;
2945  args.dp = dp;
2946  args.flags = flags;
2947  if (ruby_brace_expand(p->str, flags, dirent_match_brace,
2948  (VALUE)&args, enc, Qfalse) > 0)
2949  *new_end++ = p->next;
2950  break;
2951  case ALPHA:
2952 # if USE_NAME_ON_FS == USE_NAME_ON_FS_BY_FNMATCH
2953  if (plainname) {
2954  *new_end++ = p->next;
2955  break;
2956  }
2957 # endif
2958  case PLAIN:
2959  case MAGICAL:
2960  if (dirent_match(p->str, enc, name, dp, flags))
2961  *new_end++ = p->next;
2962  default:
2963  break;
2964  }
2965  }
2966 
2967  status = glob_helper(fd, buf, baselen, name - buf - baselen + namlen, 1,
2968  new_pathtype, new_beg, new_end,
2969  flags, funcs, arg, enc);
2970  GLOB_FREE(buf);
2971  GLOB_FREE(new_beg);
2972  if (status) break;
2973  }
2974 
2975  glob_dir_finish(&globent, flags);
2976  }
2977  else if (plain) {
2978  struct glob_pattern **copy_beg, **copy_end, **cur2;
2979 
2980 # if FNM_SYSCASE || NORMALIZE_UTF8PATH
2981  literally:
2982 # endif
2983  copy_beg = copy_end = GLOB_ALLOC_N(struct glob_pattern *, end - beg);
2984  if (!copy_beg) return -1;
2985  for (cur = beg; cur < end; ++cur)
2986  *copy_end++ = (*cur)->type <= ALPHA ? *cur : 0;
2987 
2988  for (cur = copy_beg; cur < copy_end; ++cur) {
2989  if (*cur) {
2990  rb_pathtype_t new_pathtype = path_unknown;
2991  char *buf;
2992  char *name;
2993  size_t len = strlen((*cur)->str) + 1;
2994  name = GLOB_ALLOC_N(char, len);
2995  if (!name) {
2996  status = -1;
2997  break;
2998  }
2999  memcpy(name, (*cur)->str, len);
3000  if (escape)
3001  len = remove_backslashes(name, name+len-1, enc) - name;
3002 
3003  new_beg = new_end = GLOB_ALLOC_N(struct glob_pattern *, end - beg);
3004  if (!new_beg) {
3005  GLOB_FREE(name);
3006  status = -1;
3007  break;
3008  }
3009  *new_end++ = (*cur)->next;
3010  for (cur2 = cur + 1; cur2 < copy_end; ++cur2) {
3011  if (*cur2 && fnmatch((*cur2)->str, enc, name, flags) == 0) {
3012  *new_end++ = (*cur2)->next;
3013  *cur2 = 0;
3014  }
3015  }
3016 
3017  buf = join_path(path, pathlen, dirsep, name, len);
3018  GLOB_FREE(name);
3019  if (!buf) {
3020  GLOB_FREE(new_beg);
3021  status = -1;
3022  break;
3023  }
3024 #if USE_NAME_ON_FS == USE_NAME_ON_FS_REAL_BASENAME
3025  if ((*cur)->type == ALPHA) {
3026  buf = replace_real_basename(buf, pathlen + (dirsep != 0), enc,
3027  IF_NORMALIZE_UTF8PATH(1)+0,
3028  flags, &new_pathtype);
3029  if (!buf) break;
3030  }
3031 #endif
3032  status = glob_helper(fd, buf, baselen,
3033  namelen + strlen(buf + pathlen), 1,
3034  new_pathtype, new_beg, new_end,
3035  flags, funcs, arg, enc);
3036  GLOB_FREE(buf);
3037  GLOB_FREE(new_beg);
3038  if (status) break;
3039  }
3040  }
3041 
3042  GLOB_FREE(copy_beg);
3043  }
3044 
3045  return status;
3046 }
3047 
3048 static int
3049 push_caller(const char *path, VALUE val, void *enc)
3050 {
3051  struct push_glob_args *arg = (struct push_glob_args *)val;
3052  struct glob_pattern *list;
3053  int status;
3054 
3055  list = glob_make_pattern(path, path + strlen(path), arg->flags, enc);
3056  if (!list) {
3057  return -1;
3058  }
3059  status = glob_helper(arg->fd, arg->path, arg->baselen, arg->namelen, arg->dirsep,
3060  arg->pathtype, &list, &list + 1, arg->flags, arg->funcs,
3061  arg->arg, enc);
3062  glob_free_pattern(list);
3063  return status;
3064 }
3065 
3066 static int ruby_glob0(const char *path, int fd, const char *base, int flags,
3067  const ruby_glob_funcs_t *funcs, VALUE arg, rb_encoding *enc);
3068 
3069 struct push_glob0_args {
3070  int fd;
3071  const char *base;
3072  int flags;
3073  const ruby_glob_funcs_t *funcs;
3074  VALUE arg;
3075 };
3076 
3077 static int
3078 push_glob0_caller(const char *path, VALUE val, void *enc)
3079 {
3080  struct push_glob0_args *arg = (struct push_glob0_args *)val;
3081  return ruby_glob0(path, arg->fd, arg->base, arg->flags, arg->funcs, arg->arg, enc);
3082 }
3083 
3084 static int
3085 ruby_glob0(const char *path, int fd, const char *base, int flags,
3086  const ruby_glob_funcs_t *funcs, VALUE arg,
3087  rb_encoding *enc)
3088 {
3089  struct glob_pattern *list;
3090  const char *root, *start;
3091  char *buf;
3092  size_t n, baselen = 0;
3093  int status, dirsep = FALSE;
3094 
3095  start = root = path;
3096 
3097  if (*root == '{') {
3098  struct push_glob0_args args;
3099  args.fd = fd;
3100  args.base = base;
3101  args.flags = flags;
3102  args.funcs = funcs;
3103  args.arg = arg;
3104  return ruby_brace_expand(path, flags, push_glob0_caller, (VALUE)&args, enc, Qfalse);
3105  }
3106 
3107  flags |= FNM_SYSCASE;
3108 #if defined DOSISH
3109  root = rb_enc_path_skip_prefix(root, root + strlen(root), enc);
3110 #endif
3111 
3112  if (*root == '/') root++;
3113 
3114  n = root - start;
3115  if (!n && base) {
3116  n = strlen(base);
3117  baselen = n;
3118  start = base;
3119  dirsep = TRUE;
3120  }
3121  buf = GLOB_ALLOC_N(char, n + 1);
3122  if (!buf) return -1;
3123  MEMCPY(buf, start, char, n);
3124  buf[n] = '\0';
3125 
3126  list = glob_make_pattern(root, root + strlen(root), flags, enc);
3127  if (!list) {
3128  GLOB_FREE(buf);
3129  return -1;
3130  }
3131  status = glob_helper(fd, buf, baselen, n-baselen, dirsep,
3132  path_unknown, &list, &list + 1,
3133  flags, funcs, arg, enc);
3134  glob_free_pattern(list);
3135  GLOB_FREE(buf);
3136 
3137  return status;
3138 }
3140 int
3141 ruby_glob(const char *path, int flags, ruby_glob_func *func, VALUE arg)
3142 {
3143  ruby_glob_funcs_t funcs;
3144  funcs.match = func;
3145  funcs.error = 0;
3146  return ruby_glob0(path, AT_FDCWD, 0, flags & ~GLOB_VERBOSE,
3147  &funcs, arg, rb_ascii8bit_encoding());
3148 }
3149 
3150 static int
3151 rb_glob_caller(const char *path, VALUE a, void *enc)
3152 {
3153  int status;
3154  struct glob_args *args = (struct glob_args *)a;
3155 
3156  args->path = path;
3157  rb_protect(glob_func_caller, a, &status);
3158  return status;
3159 }
3160 
3161 static const ruby_glob_funcs_t rb_glob_funcs = {
3162  rb_glob_caller, rb_glob_error,
3163 };
3164 
3165 void
3166 rb_glob(const char *path, void (*func)(const char *, VALUE, void *), VALUE arg)
3167 {
3168  struct glob_args args;
3169  int status;
3170 
3171  args.func = func;
3172  args.value = arg;
3173  args.enc = rb_ascii8bit_encoding();
3174 
3175  status = ruby_glob0(path, AT_FDCWD, 0, GLOB_VERBOSE, &rb_glob_funcs,
3176  (VALUE)&args, args.enc);
3177  if (status) GLOB_JUMP_TAG(status);
3178 }
3179 
3180 static void
3181 push_pattern(const char *path, VALUE ary, void *enc)
3182 {
3183 #if defined _WIN32 || defined __APPLE__
3184  VALUE name = rb_utf8_str_new_cstr(path);
3186  name = rb_str_conv_enc(name, NULL, eenc ? eenc : enc);
3187 #else
3188  VALUE name = rb_external_str_new_with_enc(path, strlen(path), enc);
3189 #endif
3190  rb_ary_push(ary, name);
3191 }
3192 
3193 static int
3194 ruby_brace_expand(const char *str, int flags, ruby_glob_func *func, VALUE arg,
3195  rb_encoding *enc, VALUE var)
3196 {
3197  const int escape = !(flags & FNM_NOESCAPE);
3198  const char *p = str;
3199  const char *pend = p + strlen(p);
3200  const char *s = p;
3201  const char *lbrace = 0, *rbrace = 0;
3202  int nest = 0, status = 0;
3203 
3204  while (*p) {
3205  if (*p == '{' && nest++ == 0) {
3206  lbrace = p;
3207  }
3208  if (*p == '}' && lbrace && --nest == 0) {
3209  rbrace = p;
3210  break;
3211  }
3212  if (*p == '\\' && escape) {
3213  if (!*++p) break;
3214  }
3215  Inc(p, pend, enc);
3216  }
3217 
3218  if (lbrace && rbrace) {
3219  size_t len = strlen(s) + 1;
3220  char *buf = GLOB_ALLOC_N(char, len);
3221  long shift;
3222 
3223  if (!buf) return -1;
3224  memcpy(buf, s, lbrace-s);
3225  shift = (lbrace-s);
3226  p = lbrace;
3227  while (p < rbrace) {
3228  const char *t = ++p;
3229  nest = 0;
3230  while (p < rbrace && !(*p == ',' && nest == 0)) {
3231  if (*p == '{') nest++;
3232  if (*p == '}') nest--;
3233  if (*p == '\\' && escape) {
3234  if (++p == rbrace) break;
3235  }
3236  Inc(p, pend, enc);
3237  }
3238  memcpy(buf+shift, t, p-t);
3239  strlcpy(buf+shift+(p-t), rbrace+1, len-(shift+(p-t)));
3240  status = ruby_brace_expand(buf, flags, func, arg, enc, var);
3241  if (status) break;
3242  }
3243  GLOB_FREE(buf);
3244  }
3245  else if (!lbrace && !rbrace) {
3246  status = glob_call_func(func, s, arg, enc);
3247  }
3248 
3249  RB_GC_GUARD(var);
3250  return status;
3252 
3253 struct brace_args {
3254  ruby_glob_funcs_t funcs;
3255  VALUE value;
3256  int flags;
3257 };
3258 
3259 static int
3260 glob_brace(const char *path, VALUE val, void *enc)
3261 {
3262  struct brace_args *arg = (struct brace_args *)val;
3263 
3264  return ruby_glob0(path, AT_FDCWD, 0, arg->flags, &arg->funcs, arg->value, enc);
3265 }
3266 
3267 int
3268 ruby_brace_glob_with_enc(const char *str, int flags, ruby_glob_func *func, VALUE arg, rb_encoding *enc)
3269 {
3270  struct brace_args args;
3271 
3272  flags &= ~GLOB_VERBOSE;
3273  args.funcs.match = func;
3274  args.funcs.error = 0;
3275  args.value = arg;
3276  args.flags = flags;
3277  return ruby_brace_expand(str, flags, glob_brace, (VALUE)&args, enc, Qfalse);
3278 }
3280 int
3281 ruby_brace_glob(const char *str, int flags, ruby_glob_func *func, VALUE arg)
3282 {
3283  return ruby_brace_glob_with_enc(str, flags, func, arg, rb_ascii8bit_encoding());
3284 }
3285 
3286 static int
3287 push_glob(VALUE ary, VALUE str, VALUE base, int flags)
3288 {
3289  struct glob_args args;
3290  int fd;
3291  rb_encoding *enc = rb_enc_get(str);
3292 
3293 #if defined _WIN32 || defined __APPLE__
3294  str = rb_str_encode_ospath(str);
3295 #endif
3296  if (rb_enc_to_index(enc) == ENCINDEX_US_ASCII)
3297  enc = rb_filesystem_encoding();
3298  if (rb_enc_to_index(enc) == ENCINDEX_US_ASCII)
3299  enc = rb_ascii8bit_encoding();
3300  flags |= GLOB_VERBOSE;
3301  args.func = push_pattern;
3302  args.value = ary;
3303  args.enc = enc;
3304  args.base = 0;
3305  fd = AT_FDCWD;
3306  if (!NIL_P(base)) {
3307  if (!RB_TYPE_P(base, T_STRING) || !rb_enc_check(str, base)) {
3308  struct dir_data *dirp = RTYPEDDATA_GET_DATA(base);
3309  if (!dirp->dir) dir_closed();
3310 #ifdef HAVE_DIRFD
3311  if ((fd = dirfd(dirp->dir)) == -1)
3312  rb_sys_fail_path(dir_inspect(base));
3313 #endif
3314  base = dirp->path;
3315  }
3316  args.base = RSTRING_PTR(base);
3317  }
3318 #if defined _WIN32 || defined __APPLE__
3319  enc = rb_utf8_encoding();
3320 #endif
3321 
3322  return ruby_glob0(RSTRING_PTR(str), fd, args.base, flags, &rb_glob_funcs,
3323  (VALUE)&args, enc);
3324 }
3325 
3326 static VALUE
3327 rb_push_glob(VALUE str, VALUE base, int flags) /* '\0' is delimiter */
3328 {
3329  VALUE ary;
3330  int status;
3331 
3332  /* can contain null bytes as separators */
3333  if (!RB_TYPE_P(str, T_STRING)) {
3334  FilePathValue(str);
3335  }
3336  else if (!rb_str_to_cstr(str)) {
3337  rb_raise(rb_eArgError, "nul-separated glob pattern is deprecated");
3338  }
3339  else {
3341  }
3342  ary = rb_ary_new();
3343 
3344  status = push_glob(ary, str, base, flags);
3345  if (status) GLOB_JUMP_TAG(status);
3346 
3347  return ary;
3348 }
3349 
3350 static VALUE
3351 dir_globs(VALUE args, VALUE base, int flags)
3352 {
3353  VALUE ary = rb_ary_new();
3354  long i;
3355 
3356  for (i = 0; i < RARRAY_LEN(args); ++i) {
3357  int status;
3358  VALUE str = RARRAY_AREF(args, i);
3359  FilePathValue(str);
3360  status = push_glob(ary, str, base, flags);
3361  if (status) GLOB_JUMP_TAG(status);
3362  }
3363  RB_GC_GUARD(args);
3364 
3365  return ary;
3366 }
3367 
3368 static VALUE
3369 dir_glob_option_base(VALUE base)
3370 {
3371  if (NIL_OR_UNDEF_P(base)) {
3372  return Qnil;
3373  }
3374 #if USE_OPENDIR_AT
3375  if (rb_typeddata_is_kind_of(base, &dir_data_type)) {
3376  return base;
3377  }
3378 #endif
3379  FilePathValue(base);
3380  if (!RSTRING_LEN(base)) return Qnil;
3381  return base;
3382 }
3383 
3384 static int
3385 dir_glob_option_sort(VALUE sort)
3386 {
3387  return (rb_bool_expected(sort, "sort", TRUE) ? 0 : FNM_GLOB_NOSORT);
3388 }
3389 
3390 static VALUE
3391 dir_s_aref(rb_execution_context_t *ec, VALUE obj, VALUE args, VALUE base, VALUE sort)
3392 {
3393  const int flags = dir_glob_option_sort(sort);
3394  base = dir_glob_option_base(base);
3395  if (RARRAY_LEN(args) == 1) {
3396  return rb_push_glob(RARRAY_AREF(args, 0), base, flags);
3397  }
3398  return dir_globs(args, base, flags);
3399 }
3400 
3401 static VALUE
3402 dir_s_glob(rb_execution_context_t *ec, VALUE obj, VALUE str, VALUE rflags, VALUE base, VALUE sort)
3403 {
3404  VALUE ary = rb_check_array_type(str);
3405  const int flags = (NUM2INT(rflags) | dir_glob_option_sort(sort)) & ~FNM_CASEFOLD;
3406  base = dir_glob_option_base(base);
3407  if (NIL_P(ary)) {
3408  ary = rb_push_glob(str, base, flags);
3409  }
3410  else {
3411  ary = dir_globs(ary, base, flags);
3412  }
3413 
3414  if (rb_block_given_p()) {
3415  rb_ary_each(ary);
3416  return Qnil;
3417  }
3418  return ary;
3419 }
3420 
3421 static VALUE
3422 dir_open_dir(int argc, VALUE *argv)
3423 {
3424  VALUE dir = rb_funcallv_kw(rb_cDir, rb_intern("open"), argc, argv, RB_PASS_CALLED_KEYWORDS);
3425 
3426  rb_check_typeddata(dir, &dir_data_type);
3427  return dir;
3428 }
3429 
3430 
3431 /*
3432  * call-seq:
3433  * Dir.foreach(dirpath, encoding: 'UTF-8') {|entry_name| ... } -> nil
3434  *
3435  * Calls the block with each entry name in the directory at +dirpath+;
3436  * sets the given encoding onto each passed +entry_name+:
3437  *
3438  * Dir.foreach('/example') {|entry_name| p entry_name }
3439  *
3440  * Output:
3441  *
3442  * "config.h"
3443  * "lib"
3444  * "main.rb"
3445  * ".."
3446  * "."
3447  *
3448  * Encoding:
3449  *
3450  * Dir.foreach('/example') {|entry_name| p entry_name.encoding; break }
3451  * Dir.foreach('/example', encoding: 'US-ASCII') {|entry_name| p entry_name.encoding; break }
3452  *
3453  * Output:
3454  *
3455  * #<Encoding:UTF-8>
3456  * #<Encoding:US-ASCII>
3457  *
3458  * See {String Encoding}[rdoc-ref:encodings.rdoc@String+Encoding].
3459  *
3460  * Returns an enumerator if no block is given.
3461  */
3462 static VALUE
3463 dir_foreach(int argc, VALUE *argv, VALUE io)
3464 {
3465  VALUE dir;
3466 
3467  RETURN_ENUMERATOR(io, argc, argv);
3468  dir = dir_open_dir(argc, argv);
3469  rb_ensure(dir_each, dir, dir_close, dir);
3470  return Qnil;
3471 }
3472 
3473 static VALUE
3474 dir_collect(VALUE dir)
3475 {
3476  VALUE ary = rb_ary_new();
3477  dir_each_entry(dir, rb_ary_push, ary, FALSE);
3478  return ary;
3479 }
3480 
3481 /*
3482  * call-seq:
3483  * Dir.entries(dirname, encoding: 'UTF-8') -> array
3484  *
3485  * Returns an array of the entry names in the directory at +dirpath+;
3486  * sets the given encoding onto each returned entry name:
3487  *
3488  * Dir.entries('/example') # => ["config.h", "lib", "main.rb", "..", "."]
3489  * Dir.entries('/example').first.encoding
3490  * # => #<Encoding:UTF-8>
3491  * Dir.entries('/example', encoding: 'US-ASCII').first.encoding
3492  * # => #<Encoding:US-ASCII>
3493  *
3494  * See {String Encoding}[rdoc-ref:encodings.rdoc@String+Encoding].
3495  *
3496  * Raises an exception if the directory does not exist.
3497  */
3498 static VALUE
3499 dir_entries(int argc, VALUE *argv, VALUE io)
3500 {
3501  VALUE dir;
3502 
3503  dir = dir_open_dir(argc, argv);
3504  return rb_ensure(dir_collect, dir, dir_close, dir);
3505 }
3506 
3507 static VALUE
3508 dir_each_child(VALUE dir)
3509 {
3510  return dir_each_entry(dir, dir_yield, Qnil, TRUE);
3511 }
3512 
3513 /*
3514  * call-seq:
3515  * Dir.each_child(dirpath) {|entry_name| ... } -> nil
3516  * Dir.each_child(dirpath, encoding: 'UTF-8') {|entry_name| ... } -> nil
3517  *
3518  * Like Dir.foreach, except that entries <tt>'.'</tt> and <tt>'..'</tt>
3519  * are not included.
3520  */
3521 static VALUE
3522 dir_s_each_child(int argc, VALUE *argv, VALUE io)
3523 {
3524  VALUE dir;
3525 
3526  RETURN_ENUMERATOR(io, argc, argv);
3527  dir = dir_open_dir(argc, argv);
3528  rb_ensure(dir_each_child, dir, dir_close, dir);
3529  return Qnil;
3530 }
3531 
3532 /*
3533  * call-seq:
3534  * each_child {|entry_name| ... } -> self
3535  *
3536  * Calls the block with each entry name in +self+
3537  * except <tt>'.'</tt> and <tt>'..'</tt>:
3538  *
3539  * dir = Dir.new('/example')
3540  * dir.each_child {|entry_name| p entry_name }
3541  *
3542  * Output:
3543  *
3544  * "config.h"
3545  * "lib"
3546  * "main.rb"
3547  *
3548  * If no block is given, returns an enumerator.
3549  */
3550 static VALUE
3551 dir_each_child_m(VALUE dir)
3552 {
3553  RETURN_ENUMERATOR(dir, 0, 0);
3554  return dir_each_entry(dir, dir_yield, Qnil, TRUE);
3555 }
3556 
3557 /*
3558  * call-seq:
3559  * children -> array
3560  *
3561  * Returns an array of the entry names in +self+
3562  * except for <tt>'.'</tt> and <tt>'..'</tt>:
3563  *
3564  * dir = Dir.new('/example')
3565  * dir.children # => ["config.h", "lib", "main.rb"]
3566  *
3567  */
3568 static VALUE
3569 dir_collect_children(VALUE dir)
3570 {
3571  VALUE ary = rb_ary_new();
3572  dir_each_entry(dir, rb_ary_push, ary, TRUE);
3573  return ary;
3574 }
3575 
3576 /*
3577  * call-seq:
3578  * Dir.children(dirpath) -> array
3579  * Dir.children(dirpath, encoding: 'UTF-8') -> array
3580  *
3581  * Returns an array of the entry names in the directory at +dirpath+
3582  * except for <tt>'.'</tt> and <tt>'..'</tt>;
3583  * sets the given encoding onto each returned entry name:
3584  *
3585  * Dir.children('/example') # => ["config.h", "lib", "main.rb"]
3586  * Dir.children('/example').first.encoding
3587  * # => #<Encoding:UTF-8>
3588  * Dir.children('/example', encoding: 'US-ASCII').first.encoding
3589  * # => #<Encoding:US-ASCII>
3590  *
3591  * See {String Encoding}[rdoc-ref:encodings.rdoc@String+Encoding].
3592  *
3593  * Raises an exception if the directory does not exist.
3594  */
3595 static VALUE
3596 dir_s_children(int argc, VALUE *argv, VALUE io)
3597 {
3598  VALUE dir;
3599 
3600  dir = dir_open_dir(argc, argv);
3601  return rb_ensure(dir_collect_children, dir, dir_close, dir);
3602 }
3603 
3604 static int
3605 fnmatch_brace(const char *pattern, VALUE val, void *enc)
3606 {
3607  struct brace_args *arg = (struct brace_args *)val;
3608  VALUE path = arg->value;
3609  rb_encoding *enc_pattern = enc;
3610  rb_encoding *enc_path = rb_enc_get(path);
3611 
3612  if (enc_pattern != enc_path) {
3613  if (!rb_enc_asciicompat(enc_pattern))
3614  return FNM_NOMATCH;
3615  if (!rb_enc_asciicompat(enc_path))
3616  return FNM_NOMATCH;
3617  if (!rb_enc_str_asciionly_p(path)) {
3618  int cr = ENC_CODERANGE_7BIT;
3619  long len = strlen(pattern);
3620  if (rb_str_coderange_scan_restartable(pattern, pattern + len,
3621  enc_pattern, &cr) != len)
3622  return FNM_NOMATCH;
3623  if (cr != ENC_CODERANGE_7BIT)
3624  return FNM_NOMATCH;
3625  }
3626  }
3627  return (fnmatch(pattern, enc, RSTRING_PTR(path), arg->flags) == 0);
3628 }
3629 
3630 /* :nodoc: */
3631 static VALUE
3632 file_s_fnmatch(int argc, VALUE *argv, VALUE obj)
3633 {
3634  VALUE pattern, path;
3635  VALUE rflags;
3636  int flags;
3637 
3638  if (rb_scan_args(argc, argv, "21", &pattern, &path, &rflags) == 3)
3639  flags = NUM2INT(rflags);
3640  else
3641  flags = 0;
3642 
3643  StringValueCStr(pattern);
3644  FilePathStringValue(path);
3645 
3646  if (flags & FNM_EXTGLOB) {
3647  struct brace_args args;
3648 
3649  args.value = path;
3650  args.flags = flags;
3651  if (ruby_brace_expand(RSTRING_PTR(pattern), flags, fnmatch_brace,
3652  (VALUE)&args, rb_enc_get(pattern), pattern) > 0)
3653  return Qtrue;
3654  }
3655  else {
3656  rb_encoding *enc = rb_enc_compatible(pattern, path);
3657  if (!enc) return Qfalse;
3658  if (fnmatch(RSTRING_PTR(pattern), enc, RSTRING_PTR(path), flags) == 0)
3659  return Qtrue;
3660  }
3661  RB_GC_GUARD(pattern);
3662 
3663  return Qfalse;
3664 }
3665 
3666 /*
3667  * call-seq:
3668  * Dir.home(user_name = nil) -> dirpath
3669  *
3670  * Returns the home directory path of the user specified with +user_name+
3671  * if it is not +nil+, or the current login user:
3672  *
3673  * Dir.home # => "/home/me"
3674  * Dir.home('root') # => "/root"
3675  *
3676  * Raises ArgumentError if +user_name+ is not a user name.
3677  */
3678 static VALUE
3679 dir_s_home(int argc, VALUE *argv, VALUE obj)
3680 {
3681  VALUE user;
3682  const char *u = 0;
3683 
3684  rb_check_arity(argc, 0, 1);
3685  user = (argc > 0) ? argv[0] : Qnil;
3686  if (!NIL_P(user)) {
3687  StringValue(user);
3688  rb_must_asciicompat(user);
3689  u = StringValueCStr(user);
3690  if (*u) {
3691  return rb_home_dir_of(user, rb_str_new(0, 0));
3692  }
3693  }
3694  return rb_default_home_dir(rb_str_new(0, 0));
3695 
3696 }
3697 
3698 #if 0
3699 /*
3700  * call-seq:
3701  * Dir.exist?(dirpath) -> true or false
3702  *
3703  * Returns whether +dirpath+ is a directory in the underlying file system:
3704  *
3705  * Dir.exist?('/example') # => true
3706  * Dir.exist?('/nosuch') # => false
3707  * Dir.exist?('/example/main.rb') # => false
3708  *
3709  * Same as File.directory?.
3710  *
3711  */
3712 VALUE
3713 rb_file_directory_p(void)
3714 {
3715 }
3716 #endif
3717 
3718 static void *
3719 nogvl_dir_empty_p(void *ptr)
3720 {
3721  const char *path = ptr;
3722  DIR *dir = opendir(path);
3723  struct dirent *dp;
3724  VALUE result = Qtrue;
3725 
3726  if (!dir) {
3727  int e = errno;
3728  switch (gc_for_fd_with_gvl(e)) {
3729  default:
3730  dir = opendir(path);
3731  if (dir) break;
3732  e = errno;
3733  /* fall through */
3734  case 0:
3735  if (e == ENOTDIR) return (void *)Qfalse;
3736  return (void *)INT2FIX(e);
3737  }
3738  }
3739  while ((dp = READDIR_NOGVL(dir, NULL)) != NULL) {
3740  if (!to_be_skipped(dp)) {
3741  result = Qfalse;
3742  break;
3743  }
3744  }
3745  check_closedir(dir);
3746  return (void *)result;
3747 }
3748 
3749 /*
3750  * call-seq:
3751  * Dir.empty?(dirpath) -> true or false
3752  *
3753  * Returns whether +dirpath+ specifies an empty directory:
3754  *
3755  * dirpath = '/tmp/foo'
3756  * Dir.mkdir(dirpath)
3757  * Dir.empty?(dirpath) # => true
3758  * Dir.empty?('/example') # => false
3759  * Dir.empty?('/example/main.rb') # => false
3760  *
3761  * Raises an exception if +dirpath+ does not specify a directory or file
3762  * in the underlying file system.
3763  */
3764 static VALUE
3765 rb_dir_s_empty_p(VALUE obj, VALUE dirname)
3766 {
3767  VALUE result, orig;
3768  const char *path;
3769  enum {false_on_notdir = 1};
3770 
3771  FilePathValue(dirname);
3772  orig = rb_str_dup_frozen(dirname);
3773  dirname = rb_str_encode_ospath(dirname);
3774  dirname = rb_str_dup_frozen(dirname);
3775  path = RSTRING_PTR(dirname);
3776 
3777 #if defined HAVE_GETATTRLIST && defined ATTR_DIR_ENTRYCOUNT
3778  {
3779  u_int32_t attrbuf[SIZEUP32(fsobj_tag_t)];
3780  struct attrlist al = {ATTR_BIT_MAP_COUNT, 0, ATTR_CMN_OBJTAG,};
3781  struct getattrlist_args args = GETATTRLIST_ARGS(&al, attrbuf, 0);
3782  if (gvl_getattrlist(&args, path) != 0)
3783  rb_sys_fail_path(orig);
3784  if (*(const fsobj_tag_t *)(attrbuf+1) == VT_HFS) {
3785  al.commonattr = 0;
3786  al.dirattr = ATTR_DIR_ENTRYCOUNT;
3787  if (gvl_getattrlist(&args, path) == 0) {
3788  if (attrbuf[0] >= 2 * sizeof(u_int32_t))
3789  return RBOOL(attrbuf[1] == 0);
3790  if (false_on_notdir) return Qfalse;
3791  }
3792  rb_sys_fail_path(orig);
3793  }
3794  }
3795 #endif
3796 
3797  result = (VALUE)IO_WITHOUT_GVL(nogvl_dir_empty_p, (void *)path);
3798  if (FIXNUM_P(result)) {
3799  rb_syserr_fail_path((int)FIX2LONG(result), orig);
3800  }
3801  return result;
3802 }
3803 
3804 void
3805 Init_Dir(void)
3806 {
3807  rb_gc_register_address(&chdir_lock.path);
3808  rb_gc_register_address(&chdir_lock.thread);
3809 
3810  rb_cDir = rb_define_class("Dir", rb_cObject);
3811 
3813 
3814  rb_define_alloc_func(rb_cDir, dir_s_alloc);
3815  rb_define_singleton_method(rb_cDir,"for_fd", dir_s_for_fd, 1);
3816  rb_define_singleton_method(rb_cDir, "foreach", dir_foreach, -1);
3817  rb_define_singleton_method(rb_cDir, "entries", dir_entries, -1);
3818  rb_define_singleton_method(rb_cDir, "each_child", dir_s_each_child, -1);
3819  rb_define_singleton_method(rb_cDir, "children", dir_s_children, -1);
3820 
3821  rb_define_method(rb_cDir,"fileno", dir_fileno, 0);
3822  rb_define_method(rb_cDir,"path", dir_path, 0);
3823  rb_define_method(rb_cDir,"to_path", dir_path, 0);
3824  rb_define_method(rb_cDir,"inspect", dir_inspect, 0);
3825  rb_define_method(rb_cDir,"read", dir_read, 0);
3826  rb_define_method(rb_cDir,"each", dir_each, 0);
3827  rb_define_method(rb_cDir,"each_child", dir_each_child_m, 0);
3828  rb_define_method(rb_cDir,"children", dir_collect_children, 0);
3829  rb_define_method(rb_cDir,"rewind", dir_rewind, 0);
3830  rb_define_method(rb_cDir,"tell", dir_tell, 0);
3831  rb_define_method(rb_cDir,"seek", dir_seek, 1);
3832  rb_define_method(rb_cDir,"pos", dir_tell, 0);
3833  rb_define_method(rb_cDir,"pos=", dir_set_pos, 1);
3834  rb_define_method(rb_cDir,"close", dir_close, 0);
3835  rb_define_method(rb_cDir,"chdir", dir_chdir, 0);
3836 
3837  rb_define_singleton_method(rb_cDir,"fchdir", dir_s_fchdir, 1);
3838  rb_define_singleton_method(rb_cDir,"chdir", dir_s_chdir, -1);
3839  rb_define_singleton_method(rb_cDir,"getwd", dir_s_getwd, 0);
3840  rb_define_singleton_method(rb_cDir,"pwd", dir_s_getwd, 0);
3841  rb_define_singleton_method(rb_cDir,"chroot", dir_s_chroot, 1);
3842  rb_define_singleton_method(rb_cDir,"mkdir", dir_s_mkdir, -1);
3843  rb_define_singleton_method(rb_cDir,"rmdir", dir_s_rmdir, 1);
3844  rb_define_singleton_method(rb_cDir,"delete", dir_s_rmdir, 1);
3845  rb_define_singleton_method(rb_cDir,"unlink", dir_s_rmdir, 1);
3846  rb_define_singleton_method(rb_cDir,"home", dir_s_home, -1);
3847 
3849  rb_define_singleton_method(rb_cDir,"empty?", rb_dir_s_empty_p, 1);
3850 
3851  rb_define_singleton_method(rb_cFile,"fnmatch", file_s_fnmatch, -1);
3852  rb_define_singleton_method(rb_cFile,"fnmatch?", file_s_fnmatch, -1);
3853 
3854  /* Document-const: FNM_NOESCAPE
3855  * {File::FNM_NOESCAPE}[rdoc-ref:File::Constants@File-3A-3AFNM_NOESCAPE] */
3856  rb_file_const("FNM_NOESCAPE", INT2FIX(FNM_NOESCAPE));
3857  /* Document-const: FNM_PATHNAME
3858  * {File::FNM_PATHNAME}[rdoc-ref:File::Constants@File-3A-3AFNM_PATHNAME] */
3859  rb_file_const("FNM_PATHNAME", INT2FIX(FNM_PATHNAME));
3860  /* Document-const: FNM_DOTMATCH
3861  * {File::FNM_DOTMATCH}[rdoc-ref:File::Constants@File-3A-3AFNM_DOTMATCH] */
3862  rb_file_const("FNM_DOTMATCH", INT2FIX(FNM_DOTMATCH));
3863  /* Document-const: FNM_CASEFOLD
3864  * {File::FNM_CASEFOLD}[rdoc-ref:File::Constants@File-3A-3AFNM_CASEFOLD] */
3865  rb_file_const("FNM_CASEFOLD", INT2FIX(FNM_CASEFOLD));
3866  /* Document-const: FNM_EXTGLOB
3867  * {File::FNM_EXTGLOB}[rdoc-ref:File::Constants@File-3A-3AFNM_EXTGLOB] */
3868  rb_file_const("FNM_EXTGLOB", INT2FIX(FNM_EXTGLOB));
3869  /* Document-const: FNM_SYSCASE
3870  * {File::FNM_SYSCASE}[rdoc-ref:File::Constants@File-3A-3AFNM_SYSCASE] */
3871  rb_file_const("FNM_SYSCASE", INT2FIX(FNM_SYSCASE));
3872  /* Document-const: FNM_SHORTNAME
3873  * {File::FNM_SHORTNAME}[rdoc-ref:File::Constants@File-3A-3AFNM_SHORTNAME] */
3874  rb_file_const("FNM_SHORTNAME", INT2FIX(FNM_SHORTNAME));
3875 }
3876 
3877 #include "dir.rbinc"
#define rb_define_singleton_method(klass, mid, func, arity)
Defines klass.mid.
Definition: cxxanyargs.hpp:685
int rb_enc_toupper(int c, rb_encoding *enc)
Identical to rb_toupper(), except it additionally takes an encoding.
Definition: encoding.c:1251
int ruby_glob(const char *pattern, int flags, ruby_glob_func *func, VALUE arg)
Identical to rb_glob(), except it returns opaque exception states instead of raising exceptions.
Definition: dir.c:3139
void rb_glob(const char *pattern, void(*func)(const char *path, VALUE arg, void *enc), VALUE arg)
The "glob" operator.
int ruby_brace_glob(const char *pattern, int flags, ruby_glob_func *func, VALUE arg)
Identical to ruby_glob(), @shyouhei currently suspects.
Definition: dir.c:3279
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:1187
VALUE rb_define_class(const char *name, VALUE super)
Defines a top-level class.
Definition: class.c:980
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:2635
void rb_define_method(VALUE klass, const char *name, VALUE(*func)(ANYARGS), int argc)
Defines a method.
Definition: class.c:2142
int rb_block_given_p(void)
Determines if the current method is given a block.
Definition: eval.c:916
#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 INT2FIX
Old name of RB_INT2FIX.
Definition: long.h:48
#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 FIX2LONG
Old name of RB_FIX2LONG.
Definition: long.h:46
#define NIL_P
Old name of RB_NIL_P.
#define MBCLEN_CHARFOUND_P(ret)
Old name of ONIGENC_MBCLEN_CHARFOUND_P.
Definition: encoding.h:516
#define NUM2LONG
Old name of RB_NUM2LONG.
Definition: long.h:51
#define FIXNUM_P
Old name of RB_FIXNUM_P.
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:1375
void rb_raise(VALUE exc_class, const char *fmt,...)
Exception entry point.
Definition: error.c:3635
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:1358
void rb_syserr_fail(int e, const char *mesg)
Raises appropriate exception that represents a C errno.
Definition: error.c:3748
void rb_bug(const char *fmt,...)
Interpreter panic switch.
Definition: error.c:1089
void rb_sys_fail(const char *mesg)
Converts a C errno into a Ruby exception, then raises it.
Definition: error.c:3761
VALUE rb_eIOError
IOError exception.
Definition: io.c:189
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:3754
VALUE rb_eRuntimeError
RuntimeError exception.
Definition: error.c:1406
void rb_warn(const char *fmt,...)
Identical to rb_warning(), except it reports unless $VERBOSE is nil.
Definition: error.c:466
VALUE rb_eArgError
ArgumentError exception.
Definition: error.c:1409
VALUE rb_ensure(VALUE(*b_proc)(VALUE), VALUE data1, VALUE(*e_proc)(VALUE), VALUE data2)
An equivalent to ensure clause.
Definition: eval.c:1045
VALUE rb_cDir
Dir class.
Definition: dir.c:504
VALUE rb_mEnumerable
Enumerable module.
Definition: enum.c:27
VALUE rb_cFile
File class.
Definition: file.c:175
#define RB_OBJ_WRITE(old, slot, young)
Declaration of a "back" pointer.
Definition: gc.h:603
Encoding relates APIs.
int rb_enc_precise_mbclen(const char *p, const char *e, rb_encoding *enc)
Queries the number of bytes of the character at the passed pointer.
Definition: encoding.c:1191
rb_encoding * rb_usascii_encoding(void)
Queries the encoding that represents US-ASCII.
Definition: encoding.c:1487
rb_encoding * rb_enc_compatible(VALUE str1, VALUE str2)
Look for the "common" encoding between the two.
Definition: encoding.c:1140
rb_encoding * rb_default_internal_encoding(void)
Queries the "default internal" encoding.
Definition: encoding.c:1676
int rb_enc_to_index(rb_encoding *enc)
Queries the index of the encoding.
Definition: encoding.c:191
rb_encoding * rb_to_encoding(VALUE obj)
Identical to rb_find_encoding(), except it raises an exception instead of returning NULL.
Definition: encoding.c:323
rb_encoding * rb_ascii8bit_encoding(void)
Queries the encoding that represents ASCII-8BIT a.k.a.
Definition: encoding.c:1463
rb_encoding * rb_enc_check(VALUE str1, VALUE str2)
Identical to rb_enc_compatible(), except it raises an exception instead of returning NULL.
Definition: encoding.c:1062
VALUE rb_enc_from_encoding(rb_encoding *enc)
Queries the Ruby-level counterpart instance of rb_cEncoding that corresponds to the passed encoding.
Definition: encoding.c:182
static bool rb_enc_asciicompat(rb_encoding *enc)
Queries if the passed encoding is in some sense compatible with ASCII.
Definition: encoding.h:768
rb_encoding * rb_utf8_encoding(void)
Queries the encoding that represents UTF-8.
Definition: encoding.c:1475
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:571
int rb_enc_mbclen(const char *p, const char *e, rb_encoding *enc)
Queries the number of bytes of the character at the passed pointer.
Definition: encoding.c:1179
VALUE rb_enc_associate_index(VALUE obj, int encindex)
Identical to rb_enc_set_index(), except it additionally does contents fix-up depending on the passed ...
Definition: encoding.c:994
rb_encoding * rb_enc_get(VALUE obj)
Identical to rb_enc_get_index(), except the return type.
Definition: encoding.c:1028
rb_encoding * rb_filesystem_encoding(void)
Queries the "filesystem" encoding.
Definition: encoding.c:1537
VALUE rb_str_conv_enc(VALUE str, rb_encoding *from, rb_encoding *to)
Encoding conversion main routine.
Definition: string.c:1285
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:1098
VALUE rb_enc_str_new(const char *ptr, long len, rb_encoding *enc)
Identical to rb_str_new(), except it additionally takes an encoding.
Definition: string.c:1068
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:1291
int rb_enc_str_asciionly_p(VALUE str)
Queries if the passed string is "ASCII only".
Definition: string.c:919
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:784
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:1066
VALUE rb_funcallv(VALUE recv, ID mid, int argc, const VALUE *argv)
Identical to rb_funcall(), except it takes the method arguments as a C array.
Definition: vm_eval.c:1058
void rb_memerror(void)
Triggers out-of-memory error.
Definition: gc.c:4426
void rb_gc_register_address(VALUE *valptr)
Inform the garbage collector that the global or static variable pointed by valptr stores a live Ruby ...
Definition: gc.c:2927
Defines RBIMPL_HAS_BUILTIN.
VALUE rb_ary_each(VALUE ary)
Iteratively yields each element of the passed array to the implicitly passed block if any.
Definition: array.c:2635
VALUE rb_check_array_type(VALUE obj)
Try converting an object to its array representation using its to_ary method, if any.
Definition: array.c:1008
VALUE rb_ary_new(void)
Allocates a new, empty array.
Definition: array.c:741
VALUE rb_ary_push(VALUE ary, VALUE elem)
Special case of rb_ary_cat() that it adds only one element.
Definition: array.c:1378
VALUE rb_dir_getwd(void)
Queries the path of the current working directory of the current process.
Definition: dir.c:1495
#define RETURN_ENUMERATOR(obj, argc, argv)
Identical to RETURN_SIZED_ENUMERATOR(), except its size is unknown.
Definition: enumerator.h:239
static int rb_check_arity(int argc, int min, int max)
Ensures that the passed integer is in the passed range.
Definition: error.h:284
VALUE rb_str_encode_ospath(VALUE path)
Converts a string into an "OS Path" encoding, if any.
Definition: file.c:252
VALUE rb_file_directory_p(VALUE _, VALUE path)
Queries if the given path is either a directory, or a symlink that (potentially recursively) points t...
Definition: file.c:1620
VALUE rb_protect(VALUE(*func)(VALUE args), VALUE args, int *state)
Protects a function call from potential global escapes from the function.
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:3675
VALUE rb_utf8_str_new_cstr(const char *ptr)
Identical to rb_str_new_cstr(), except it generates a string of "UTF-8" encoding.
Definition: string.c:1092
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:3051
VALUE rb_str_cat2(VALUE, const char *)
Just another name of rb_str_cat_cstr.
VALUE rb_str_dup(VALUE str)
Duplicates a string.
Definition: string.c:1916
void rb_must_asciicompat(VALUE obj)
Asserts that the given string's encoding is (Ruby's definition of) ASCII compatible.
Definition: string.c:2692
VALUE rb_str_new(const char *ptr, long len)
Allocates an instance of rb_cString.
Definition: string.c:1050
VALUE rb_str_dup_frozen(VALUE)
Just another name of rb_str_new_frozen.
VALUE rb_str_new_cstr(const char *ptr)
Identical to rb_str_new(), except it assumes the passed pointer is a pointer to a C string.
Definition: string.c:1074
VALUE rb_str_resize(VALUE str, long len)
Overwrites the length of the string.
Definition: string.c:3315
VALUE rb_thread_current(void)
Obtains the "current" thread.
Definition: thread.c:2982
VALUE rb_class_name(VALUE obj)
Queries the name of the given object's class.
Definition: variable.c:412
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:823
char * ptr
Pointer to the underlying memory region, of at least capa bytes.
Definition: io.h:2
int len
Length of the buffer.
Definition: io.h:8
void * rb_thread_call_with_gvl(void *(*func)(void *), void *data1)
(Re-)acquires the GVL.
Definition: thread.c:1902
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:602
void ruby_qsort(void *, const size_t, const size_t, int(*)(const void *, const void *, void *), void *)
Reentrant implementation of quick sort.
#define RB_NUM2INT
Just another name of rb_num2int_inline.
Definition: int.h:38
VALUE rb_int2inum(intptr_t i)
Converts a C's intptr_t into an instance of rb_cInteger.
Definition: bignum.c:3250
VALUE rb_yield_values(int n,...)
Identical to rb_yield(), except it takes variadic number of parameters and pass them to the block.
Definition: vm_eval.c:1366
VALUE rb_yield_values2(int n, const VALUE *argv)
Identical to rb_yield_values(), except it takes the parameters as a C array instead of variadic argum...
Definition: vm_eval.c:1388
VALUE rb_yield(VALUE val)
Yields the block.
Definition: vm_eval.c:1354
#define MEMCPY(p1, p2, type, n)
Handy macro to call memcpy.
Definition: memory.h:367
#define MEMZERO(p, type, n)
Handy macro to erase a region of memory.
Definition: memory.h:355
static int rb_mul_size_overflow(size_t a, size_t b, size_t max, size_t *c)
Definition: memory.h:526
#define RB_GC_GUARD(v)
Prevents premature destruction of local objects.
Definition: memory.h:162
#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.
Definition: cxxanyargs.hpp:56
char * rb_enc_path_end(const char *path, const char *end, rb_encoding *enc)
This just returns the passed end basically.
Definition: file.c:3594
char * rb_enc_path_skip_prefix(const char *path, const char *end, rb_encoding *enc)
Seeks for non-prefix part of a pathname.
Definition: file.c:3526
#define RARRAY_LEN
Just another name of rb_array_len.
Definition: rarray.h:51
#define RARRAY_AREF(a, i)
Definition: rarray.h:403
#define StringValue(v)
Ensures that the parameter object is a String.
Definition: rstring.h:66
static char * RSTRING_PTR(VALUE str)
Queries the contents pointer of the string.
Definition: rstring.h:416
#define RSTRING_GETMEM(str, ptrvar, lenvar)
Convenient macro to obtain the contents and length at once.
Definition: rstring.h:488
static long RSTRING_LEN(VALUE str)
Queries the length of the string.
Definition: rstring.h:367
#define StringValueCStr(v)
Identical to StringValuePtr, except it additionally checks for the contents for viability as a C stri...
Definition: rstring.h:89
#define TypedData_Get_Struct(obj, type, data_type, sval)
Obtains a C struct from inside of a wrapper Ruby object.
Definition: rtypeddata.h:515
#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:497
VALUE rb_get_path(VALUE obj)
Converts an object to a path.
Definition: file.c:246
#define FilePathValue(v)
Ensures that the parameter object is a path.
Definition: ruby.h:90
int rb_errno(void)
Identical to system errno.
Definition: eval.c:2149
void rb_errno_set(int err)
Set the errno.
Definition: eval.c:2155
#define errno
Ractor-aware version of errno.
Definition: ruby.h:388
#define FilePathStringValue(v)
This macro actually does the same thing as FilePathValue now.
Definition: ruby.h:105
#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
Definition: dir.c:506
This is the struct that holds necessary info for a struct.
Definition: rtypeddata.h:200
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:376