module Bootsnap::LoadPathCache::Native
Public Class Methods
scan_dir(p1)
click to toggle source
static VALUE
bs_rb_scan_dir(VALUE self, VALUE abspath)
{
Check_Type(abspath, T_STRING);
VALUE dirs = rb_ary_new();
VALUE requirables = rb_ary_new();
VALUE result = rb_ary_new_from_args(2, requirables, dirs);
DIR *dirp = opendir(RSTRING_PTR(abspath));
if (dirp == NULL) {
if (errno == ENOTDIR || errno == ENOENT) {
return result;
}
bs_syserr_fail_path("opendir", errno, abspath);
return Qundef;
}
struct dirent *entry;
struct stat st;
int dfd = -1;
while (1) {
errno = 0;
entry = readdir(dirp);
if (entry == NULL) break;
if (entry->d_name[0] == '.') continue;
if (RB_UNLIKELY(entry->d_type == DT_UNKNOWN || entry->d_type == DT_LNK)) {
// Note: the original implementation of LoadPathCache did follow symlink.
// So this is replicated here, but I'm not sure it's a good idea.
if (dfd < 0) {
dfd = dirfd(dirp);
if (dfd < 0) {
int err = errno;
closedir(dirp);
bs_syserr_fail_path("dirfd", err, abspath);
return Qundef;
}
}
if (fstatat(dfd, entry->d_name, &st, 0)) {
if (errno == ENOENT) continue; // Broken symlink
int err = errno;
closedir(dirp);
bs_syserr_fail_dir_entry("fstatat", err, abspath, entry->d_name);
return Qundef;
}
if (S_ISREG(st.st_mode)) {
entry->d_type = DT_REG;
} else if (S_ISDIR(st.st_mode)) {
entry->d_type = DT_DIR;
}
}
if (entry->d_type == DT_DIR) {
rb_ary_push(dirs, rb_utf8_str_new_cstr(entry->d_name));
continue;
} else if (entry->d_type == DT_REG) {
size_t len = strlen(entry->d_name);
bool is_requirable = (
// Comparing 4B allows compiler to optimize this into a single 32b integer comparison.
(len > 3 && memcmp(entry->d_name + (len - 3), ".rb", 4) == 0)
|| (len > DLEXT_MAXLEN && memcmp(entry->d_name + (len - DLEXT_MAXLEN), DLEXT, DLEXT_MAXLEN + 1) == 0)
#ifdef DLEXT2
|| (len > DLEXT2_MAXLEN && memcmp(entry->d_name + (len - DLEXT2_MAXLEN), DLEXT2, DLEXT2_MAXLEN + 1) == 0)
#endif
);
if (is_requirable) {
rb_ary_push(requirables, rb_utf8_str_new(entry->d_name, len));
}
}
}
if (errno) {
int err = errno;
closedir(dirp);
bs_syserr_fail_path("readdir", err, abspath);
} else if (closedir(dirp)) {
bs_syserr_fail_path("closedir", errno, abspath);
return Qundef;
}
return result;
}