diff options
Diffstat (limited to 'dir.c')
| -rw-r--r-- | dir.c | 539 |
1 files changed, 458 insertions, 81 deletions
@@ -22,10 +22,6 @@ #include <unistd.h> #endif -#ifndef O_CLOEXEC -# define O_CLOEXEC 0 -#endif - #ifndef USE_OPENDIR_AT # if defined(HAVE_FDOPENDIR) && defined(HAVE_DIRFD) && \ defined(HAVE_OPENAT) && defined(HAVE_FSTATAT) @@ -35,8 +31,12 @@ # endif #endif -#if USE_OPENDIR_AT -# include <fcntl.h> +#ifdef HAVE_FCNTL_H +# include <fcntl.h> +#endif + +#ifndef O_CLOEXEC +# define O_CLOEXEC 0 #endif #undef HAVE_DIRENT_NAMLEN @@ -143,6 +143,50 @@ char *strchr(char*,char); # define IS_WIN32 0 #endif +#ifdef HAVE_GETATTRLIST +struct getattrlist_args { + const char *path; + int fd; + struct attrlist *list; + void *buf; + size_t size; + unsigned int options; +}; + +# define GETATTRLIST_ARGS(list_, buf_, options_) (struct getattrlist_args) \ + {.list = list_, .buf = buf_, .size = sizeof(buf_), .options = options_} + +static void * +nogvl_getattrlist(void *args) +{ + struct getattrlist_args *arg = args; + return (void *)(VALUE)getattrlist(arg->path, arg->list, arg->buf, arg->size, arg->options); +} + +static int +gvl_getattrlist(struct getattrlist_args *args, const char *path) +{ + args->path = path; + return IO_WITHOUT_GVL_INT(nogvl_getattrlist, args); +} + +# ifdef HAVE_FGETATTRLIST +static void * +nogvl_fgetattrlist(void *args) +{ + struct getattrlist_args *arg = args; + return (void *)(VALUE)fgetattrlist(arg->fd, arg->list, arg->buf, arg->size, arg->options); +} + +static int +gvl_fgetattrlist(struct getattrlist_args *args, int fd) +{ + args->fd = fd; + return IO_WITHOUT_GVL_INT(nogvl_fgetattrlist, args); +} +# endif +#endif + #if NORMALIZE_UTF8PATH # if defined HAVE_FGETATTRLIST || !defined HAVE_GETATTRLIST # define need_normalization(dirp, path) need_normalization(dirp) @@ -155,10 +199,11 @@ need_normalization(DIR *dirp, const char *path) # if defined HAVE_FGETATTRLIST || defined HAVE_GETATTRLIST u_int32_t attrbuf[SIZEUP32(fsobj_tag_t)]; struct attrlist al = {ATTR_BIT_MAP_COUNT, 0, ATTR_CMN_OBJTAG,}; + struct getattrlist_args args = GETATTRLIST_ARGS(&al, attrbuf, 0); # if defined HAVE_FGETATTRLIST - int ret = fgetattrlist(dirfd(dirp), &al, attrbuf, sizeof(attrbuf), 0); + int ret = gvl_fgetattrlist(&args, dirfd(dirp)); # else - int ret = getattrlist(path, &al, attrbuf, sizeof(attrbuf), 0); + int ret = gvl_getattrlist(&args, path); # endif if (!ret) { const fsobj_tag_t *tag = (void *)(attrbuf+1); @@ -459,6 +504,20 @@ fnmatch( } VALUE rb_cDir; +static VALUE sym_directory, sym_link, sym_file, sym_unknown; + +#if defined(DT_BLK) || defined(S_IFBLK) +static VALUE sym_block_device; +#endif +#if defined(DT_CHR) || defined(S_IFCHR) +static VALUE sym_character_device; +#endif +#if defined(DT_FIFO) || defined(S_IFIFO) +static VALUE sym_fifo; +#endif +#if defined(DT_SOCK) || defined(S_IFSOCK) +static VALUE sym_socket; +#endif struct dir_data { DIR *dir; @@ -509,7 +568,7 @@ nogvl_opendir(void *ptr) { const char *path = ptr; - return (void *)opendir(path); + return opendir(path); } static DIR * @@ -526,6 +585,25 @@ opendir_without_gvl(const char *path) return opendir(path); } +static void +close_dir_data(struct dir_data *dp) +{ + if (dp->dir) { + if (closedir(dp->dir) < 0) { + dp->dir = NULL; + rb_sys_fail("closedir"); + } + dp->dir = NULL; + } +} + +static void +check_closedir(DIR *dirp) +{ + if (closedir(dirp) < 0) + rb_sys_fail("closedir"); +} + static VALUE dir_initialize(rb_execution_context_t *ec, VALUE dir, VALUE dirname, VALUE enc) { @@ -540,8 +618,7 @@ dir_initialize(rb_execution_context_t *ec, VALUE dir, VALUE dirname, VALUE enc) dirname = rb_str_dup_frozen(dirname); TypedData_Get_Struct(dir, struct dir_data, &dir_data_type, dp); - if (dp->dir) closedir(dp->dir); - dp->dir = NULL; + close_dir_data(dp); RB_OBJ_WRITE(dir, &dp->path, Qnil); dp->enc = fsenc; path = RSTRING_PTR(dirname); @@ -555,7 +632,8 @@ dir_initialize(rb_execution_context_t *ec, VALUE dir, VALUE dirname, VALUE enc) else if (e == EIO) { u_int32_t attrbuf[1]; struct attrlist al = {ATTR_BIT_MAP_COUNT, 0}; - if (getattrlist(path, &al, attrbuf, sizeof(attrbuf), FSOPT_NOFOLLOW) == 0) { + struct getattrlist_args args = GETATTRLIST_ARGS(&al, attrbuf, FSOPT_NOFOLLOW); + if (gvl_getattrlist(&args, path) == 0) { dp->dir = opendir_without_gvl(path); } } @@ -588,6 +666,12 @@ dir_s_close(rb_execution_context_t *ec, VALUE klass, VALUE dir) } # if defined(HAVE_FDOPENDIR) && defined(HAVE_DIRFD) +static void * +nogvl_fdopendir(void *fd) +{ + return fdopendir((int)(VALUE)fd); +} + /* * call-seq: * Dir.for_fd(fd) -> dir @@ -614,7 +698,7 @@ dir_s_for_fd(VALUE klass, VALUE fd) struct dir_data *dp; VALUE dir = TypedData_Make_Struct(klass, struct dir_data, &dir_data_type, dp); - if (!(dp->dir = fdopendir(NUM2INT(fd)))) { + if (!(dp->dir = IO_WITHOUT_GVL(nogvl_fdopendir, (void *)(VALUE)NUM2INT(fd)))) { rb_sys_fail("fdopendir"); UNREACHABLE_RETURN(Qnil); } @@ -756,8 +840,28 @@ fundamental_encoding_p(rb_encoding *enc) } } # define READDIR(dir, enc) rb_w32_readdir((dir), (enc)) +# define READDIR_NOGVL READDIR #else -# define READDIR(dir, enc) readdir((dir)) +NORETURN(static void *sys_failure(void *function)); +static void * +sys_failure(void *function) +{ + rb_sys_fail(function); +} + +static void * +nogvl_readdir(void *dir) +{ + rb_errno_set(0); + if ((dir = readdir(dir)) == NULL) { + if (rb_errno()) + rb_thread_call_with_gvl(sys_failure, (void *)"readdir"); + } + return dir; +} + +# define READDIR(dir, enc) IO_WITHOUT_GVL(nogvl_readdir, (void *)(dir)) +# define READDIR_NOGVL(dir, enc) nogvl_readdir((dir)) #endif /* safe to use without GVL */ @@ -815,14 +919,109 @@ dir_read(VALUE dir) } } -static VALUE dir_each_entry(VALUE, VALUE (*)(VALUE, VALUE), VALUE, int); +struct dir_entry_args { + struct dir_data *dirp; + struct dirent *dp; +}; + +static VALUE dir_each_entry(VALUE, VALUE (*)(VALUE, VALUE, struct dir_entry_args *), VALUE, int); static VALUE -dir_yield(VALUE arg, VALUE path) +dir_yield(VALUE arg, VALUE path, struct dir_entry_args *_unused) { return rb_yield(path); } +static int do_lstat(int fd, const char *path, struct stat *pst, int flags, rb_encoding *enc); + +static VALUE +dir_yield_with_type(VALUE arg, VALUE path, struct dir_entry_args *dir_entry) +{ + VALUE type; + switch (dir_entry->dp->d_type) { +#ifdef DT_BLK + case DT_BLK: + type = sym_block_device; + break; +#endif +#ifdef DT_CHR + case DT_CHR: + type = sym_character_device; + break; +#endif + case DT_DIR: + type = sym_directory; + break; +#ifdef DT_FIFO + case DT_FIFO: + type = sym_fifo; + break; +#endif + case DT_LNK: + type = sym_link; + break; + case DT_REG: + type = sym_file; + break; +#ifdef DT_SOCK + case DT_SOCK: + type = sym_socket; + break; +#endif + default: + type = sym_unknown; + break; + } + +#ifdef HAVE_DIRFD + if (RUBY_DEBUG || RB_UNLIKELY(type == sym_unknown)) { + struct stat st; + if (do_lstat(dirfd(dir_entry->dirp->dir), dir_entry->dp->d_name, &st, 0, rb_filesystem_encoding()) == 0) { + switch (st.st_mode & S_IFMT) { + case S_IFDIR: + type = sym_directory; + break; + case S_IFLNK: + type = sym_link; + break; + case S_IFREG: + type = sym_file; + break; +#ifdef S_IFSOCK + case S_IFSOCK: + type = sym_socket; + break; +#endif +#ifdef S_IFIFO + case S_IFIFO: + type = sym_fifo; + break; +#endif +#ifdef S_IFBLK + case S_IFBLK: + type = sym_block_device; + break; +#endif +#ifdef S_IFCHR + case S_IFCHR: + type = sym_character_device; + break; +#endif + default: + break; + } + } + } +#endif // HAVE_DIRFD + + if (NIL_P(arg)) { + return rb_yield_values(2, path, type); + } + else { + return rb_ary_push(arg, rb_assoc_new(path, type)); + } +} + /* * call-seq: * each {|entry_name| ... } -> self @@ -850,7 +1049,7 @@ dir_each(VALUE dir) } static VALUE -dir_each_entry(VALUE dir, VALUE (*each)(VALUE, VALUE), VALUE arg, int children_only) +dir_each_entry(VALUE dir, VALUE (*each)(VALUE, VALUE, struct dir_entry_args *), VALUE arg, int children_only) { struct dir_data *dirp; struct dirent *dp; @@ -876,7 +1075,11 @@ dir_each_entry(VALUE dir, VALUE (*each)(VALUE, VALUE), VALUE arg, int children_o else #endif path = rb_external_str_new_with_enc(name, namlen, dirp->enc); - (*each)(arg, path); + struct dir_entry_args each_args = { + .dirp = dirp, + .dp = dp, + }; + (*each)(arg, path, &each_args); } return dir; } @@ -902,7 +1105,8 @@ dir_tell(VALUE dir) long pos; GetDIR(dir, dirp); - pos = telldir(dirp->dir); + if((pos = telldir(dirp->dir)) < 0) + rb_sys_fail("telldir"); return rb_int2inum(pos); } #else @@ -1021,8 +1225,7 @@ dir_close(VALUE dir) dirp = dir_get(dir); if (!dirp->dir) return Qnil; - closedir(dirp->dir); - dirp->dir = NULL; + close_dir_data(dirp); return Qnil; } @@ -1038,7 +1241,7 @@ nogvl_chdir(void *ptr) static void dir_chdir0(VALUE path) { - if (chdir(RSTRING_PTR(path)) < 0) + if (IO_WITHOUT_GVL_INT(nogvl_chdir, (void*)RSTRING_PTR(path)) < 0) rb_sys_fail_path(path); } @@ -1240,7 +1443,7 @@ nogvl_fchdir(void *ptr) static void dir_fchdir(int fd) { - if (fchdir(fd) < 0) + if (IO_WITHOUT_GVL_INT(nogvl_fchdir, (void *)&fd) < 0) rb_sys_fail("fchdir"); } @@ -1382,24 +1585,55 @@ dir_chdir(VALUE dir) #endif } +static VALUE last_cwd; + #ifndef _WIN32 +static VALUE +getcwd_to_str(VALUE arg) +{ + const char *path = (const char *)arg; +#ifdef __APPLE__ + return rb_str_normalize_ospath(path, strlen(path)); +#else + return rb_str_new2(path); +#endif +} + +static VALUE +getcwd_xfree(VALUE arg) +{ + xfree((void *)arg); + return Qnil; +} + +static VALUE +rb_dir_getwd_ospath_slowpath(void) +{ + char *path = ruby_getcwd(); + return rb_ensure(getcwd_to_str, (VALUE)path, getcwd_xfree, (VALUE)path); +} + VALUE rb_dir_getwd_ospath(void) { - char *path; - VALUE cwd; - VALUE path_guard; + char buf[PATH_MAX]; + char *path = getcwd(buf, PATH_MAX); + if (!path) { + return rb_dir_getwd_ospath_slowpath(); + } + + VALUE cached_cwd = RUBY_ATOMIC_VALUE_LOAD(last_cwd); - path_guard = rb_imemo_tmpbuf_auto_free_pointer(); - path = ruby_getcwd(); - rb_imemo_tmpbuf_set_ptr(path_guard, path); + if (!cached_cwd || strcmp(RSTRING_PTR(cached_cwd), path) != 0) { #ifdef __APPLE__ - cwd = rb_str_normalize_ospath(path, strlen(path)); + cached_cwd = rb_str_normalize_ospath(path, strlen(path)); #else - cwd = rb_str_new2(path); + cached_cwd = rb_str_new2(path); #endif - rb_free_tmp_buffer(&path_guard); - return cwd; + rb_str_freeze(cached_cwd); + RUBY_ATOMIC_VALUE_SET(last_cwd, cached_cwd); + } + return cached_cwd; } #endif @@ -1408,7 +1642,7 @@ rb_dir_getwd(void) { rb_encoding *fs = rb_filesystem_encoding(); int fsenc = rb_enc_to_index(fs); - VALUE cwd = rb_dir_getwd_ospath(); + VALUE cwd = rb_str_new_shared(rb_dir_getwd_ospath()); switch (fsenc) { case ENCINDEX_US_ASCII: @@ -1460,6 +1694,12 @@ check_dirname(VALUE dir) } #if defined(HAVE_CHROOT) +static void * +nogvl_chroot(void *dirname) +{ + return (void *)(VALUE)chroot((const char *)dirname); +} + /* * call-seq: * Dir.chroot(dirpath) -> 0 @@ -1476,7 +1716,7 @@ static VALUE dir_s_chroot(VALUE dir, VALUE path) { path = check_dirname(path); - if (chroot(RSTRING_PTR(path)) == -1) + if (IO_WITHOUT_GVL_INT(nogvl_chroot, (void *)RSTRING_PTR(path)) == -1) rb_sys_fail_path(path); return INT2FIX(0); @@ -1653,11 +1893,11 @@ to_be_ignored(int e) } #ifdef _WIN32 -#define STAT(p, s) rb_w32_ustati128((p), (s)) -#undef lstat -#define lstat(p, s) rb_w32_ulstati128((p), (s)) +#define STAT(args) (int)(VALUE)nogvl_stat(&(args)) +#define LSTAT(args) (int)(VALUE)nogvl_lstat(&(args)) #else -#define STAT(p, s) stat((p), (s)) +#define STAT(args) IO_WITHOUT_GVL_INT(nogvl_stat, (void *)&(args)) +#define LSTAT(args) IO_WITHOUT_GVL_INT(nogvl_lstat, (void *)&(args)) #endif typedef int ruby_glob_errfunc(const char*, VALUE, const void*, int); @@ -1678,14 +1918,50 @@ at_subpath(int fd, size_t baselen, const char *path) return *path ? path : "."; } +#if USE_OPENDIR_AT +struct fstatat_args { + int fd; + int flag; + const char *path; + struct stat *pst; +}; + +static void * +nogvl_fstatat(void *args) +{ + struct fstatat_args *arg = (struct fstatat_args *)args; + return (void *)(VALUE)fstatat(arg->fd, arg->path, arg->pst, arg->flag); +} +#else +struct stat_args { + const char *path; + struct stat *pst; +}; + +static void * +nogvl_stat(void *args) +{ + struct stat_args *arg = (struct stat_args *)args; + return (void *)(VALUE)stat(arg->path, arg->pst); +} +#endif + /* System call with warning */ static int -do_stat(int fd, size_t baselen, const char *path, struct stat *pst, int flags, rb_encoding *enc) +do_stat(int fd, const char *path, struct stat *pst, int flags, rb_encoding *enc) { #if USE_OPENDIR_AT - int ret = fstatat(fd, at_subpath(fd, baselen, path), pst, 0); + struct fstatat_args args; + args.fd = fd; + args.path = path; + args.pst = pst; + args.flag = 0; + int ret = IO_WITHOUT_GVL_INT(nogvl_fstatat, (void *)&args); #else - int ret = STAT(path, pst); + struct stat_args args; + args.path = path; + args.pst = pst; + int ret = STAT(args); #endif if (ret < 0 && !to_be_ignored(errno)) sys_warning(path, enc); @@ -1694,13 +1970,30 @@ do_stat(int fd, size_t baselen, const char *path, struct stat *pst, int flags, r } #if defined HAVE_LSTAT || defined lstat || USE_OPENDIR_AT +#if !USE_OPENDIR_AT +static void * +nogvl_lstat(void *args) +{ + struct stat_args *arg = (struct stat_args *)args; + return (void *)(VALUE)lstat(arg->path, arg->pst); +} +#endif + static int -do_lstat(int fd, size_t baselen, const char *path, struct stat *pst, int flags, rb_encoding *enc) +do_lstat(int fd, const char *path, struct stat *pst, int flags, rb_encoding *enc) { #if USE_OPENDIR_AT - int ret = fstatat(fd, at_subpath(fd, baselen, path), pst, AT_SYMLINK_NOFOLLOW); + struct fstatat_args args; + args.fd = fd; + args.path = path; + args.pst = pst; + args.flag = AT_SYMLINK_NOFOLLOW; + int ret = IO_WITHOUT_GVL_INT(nogvl_fstatat, (void *)&args); #else - int ret = lstat(path, pst); + struct stat_args args; + args.path = path; + args.pst = pst; + int ret = LSTAT(args); #endif if (ret < 0 && !to_be_ignored(errno)) sys_warning(path, enc); @@ -2061,14 +2354,15 @@ is_case_sensitive(DIR *dirp, const char *path) const vol_capabilities_attr_t *const cap = attrbuf[0].cap; const int idx = VOL_CAPABILITIES_FORMAT; const uint32_t mask = VOL_CAP_FMT_CASE_SENSITIVE; - + struct getattrlist_args args = GETATTRLIST_ARGS(&al, attrbuf, FSOPT_NOFOLLOW); # if defined HAVE_FGETATTRLIST - if (fgetattrlist(dirfd(dirp), &al, attrbuf, sizeof(attrbuf), FSOPT_NOFOLLOW)) - return -1; + int ret = gvl_fgetattrlist(&args, dirfd(dirp)); # else - if (getattrlist(path, &al, attrbuf, sizeof(attrbuf), FSOPT_NOFOLLOW)) - return -1; + int ret = gvl_getattrlist(&args, path); # endif + if (ret) + return -1; + if (!(cap->valid[idx] & mask)) return -1; return (cap->capabilities[idx] & mask) != 0; @@ -2091,7 +2385,8 @@ replace_real_basename(char *path, long base, rb_encoding *enc, int norm_p, int f IF_NORMALIZE_UTF8PATH(VALUE utf8str = Qnil); *type = path_noent; - if (getattrlist(path, &al, attrbuf, sizeof(attrbuf), FSOPT_NOFOLLOW)) { + struct getattrlist_args args = GETATTRLIST_ARGS(&al, attrbuf, FSOPT_NOFOLLOW); + if (gvl_getattrlist(&args, path)) { if (!to_be_ignored(errno)) sys_warning(path, enc); return path; @@ -2464,7 +2759,7 @@ static void glob_dir_finish(ruby_glob_entries_t *ent, int flags) { if (flags & FNM_GLOB_NOSORT) { - closedir(ent->nosort.dirp); + check_closedir(ent->nosort.dirp); ent->nosort.dirp = NULL; } else if (ent->sort.entries) { @@ -2495,7 +2790,7 @@ glob_opendir(ruby_glob_entries_t *ent, DIR *dirp, int flags, rb_encoding *enc) #ifdef _WIN32 if ((capacity = dirp->nfiles) > 0) { if (!(newp = GLOB_ALLOC_N(rb_dirent_t, capacity))) { - closedir(dirp); + check_closedir(dirp); return NULL; } ent->sort.entries = newp; @@ -2508,14 +2803,16 @@ glob_opendir(ruby_glob_entries_t *ent, DIR *dirp, int flags, rb_encoding *enc) } if (count >= capacity) { capacity += 256; - if (!(newp = GLOB_REALLOC_N(ent->sort.entries, capacity))) + if (!(newp = GLOB_REALLOC_N(ent->sort.entries, capacity))) { + GLOB_FREE(rdp); goto nomem; + } ent->sort.entries = newp; } ent->sort.entries[count++] = rdp; ent->sort.count = count; } - closedir(dirp); + check_closedir(dirp); if (count < capacity) { if (!(newp = GLOB_REALLOC_N(ent->sort.entries, count))) { glob_dir_finish(ent, 0); @@ -2530,7 +2827,7 @@ glob_opendir(ruby_glob_entries_t *ent, DIR *dirp, int flags, rb_encoding *enc) nomem: glob_dir_finish(ent, 0); - closedir(dirp); + check_closedir(dirp); return NULL; } @@ -2628,7 +2925,7 @@ glob_helper( if (*path) { if (match_all && pathtype == path_unknown) { - if (do_lstat(fd, baselen, path, &st, flags, enc) == 0) { + if (do_lstat(fd, path, &st, flags, enc) == 0) { pathtype = IFTODT(st.st_mode); } else { @@ -2636,7 +2933,7 @@ glob_helper( } } if (match_dir && (pathtype == path_unknown || pathtype == path_symlink)) { - if (do_stat(fd, baselen, path, &st, flags, enc) == 0) { + if (do_stat(fd, path, &st, flags, enc) == 0) { pathtype = IFTODT(st.st_mode); } else { @@ -2693,7 +2990,7 @@ glob_helper( # if NORMALIZE_UTF8PATH if (!(norm_p || magical || recursive)) { - closedir(dirp); + check_closedir(dirp); goto literally; } # endif @@ -2764,7 +3061,7 @@ glob_helper( if (recursive && dotfile < ((flags & FNM_DOTMATCH) ? 2 : 1) && new_pathtype == path_unknown) { /* RECURSIVE never match dot files unless FNM_DOTMATCH is set */ - if (do_lstat(fd, baselen, buf, &st, flags, enc) == 0) + if (do_lstat(fd, buf, &st, flags, enc) == 0) new_pathtype = IFTODT(st.st_mode); else new_pathtype = path_noent; @@ -3320,10 +3617,16 @@ dir_foreach(int argc, VALUE *argv, VALUE io) } static VALUE +dir_entry_ary_push(VALUE ary, VALUE entry, struct dir_entry_args *_unused) +{ + return rb_ary_push(ary, entry); +} + +static VALUE dir_collect(VALUE dir) { VALUE ary = rb_ary_new(); - dir_each_entry(dir, rb_ary_push, ary, FALSE); + dir_each_entry(dir, dir_entry_ary_push, ary, FALSE); return ary; } @@ -3418,12 +3721,37 @@ static VALUE dir_collect_children(VALUE dir) { VALUE ary = rb_ary_new(); - dir_each_entry(dir, rb_ary_push, ary, TRUE); + dir_each_entry(dir, dir_entry_ary_push, ary, TRUE); return ary; } /* * call-seq: + * children -> array + * + * Returns an array of the entry names in +self+ along with their type + * except for <tt>'.'</tt> and <tt>'..'</tt>: + * + * dir = Dir.new('/example') + * dir.scan # => [["config.h", :file], ["lib", :directory], ["main.rb", :file]] + * + */ +static VALUE +dir_scan_children(VALUE dir) +{ + if (rb_block_given_p()) { + dir_each_entry(dir, dir_yield_with_type, Qnil, TRUE); + return Qnil; + } + else { + VALUE ary = rb_ary_new(); + dir_each_entry(dir, dir_yield_with_type, ary, TRUE); + return ary; + } +} + +/* + * call-seq: * Dir.children(dirpath) -> array * Dir.children(dirpath, encoding: 'UTF-8') -> array * @@ -3450,6 +3778,40 @@ dir_s_children(int argc, VALUE *argv, VALUE io) return rb_ensure(dir_collect_children, dir, dir_close, dir); } +/* + * call-seq: + * Dir.scan(dirpath) {|entry_name, entry_type| ... } -> nil + * Dir.scan(dirpath, encoding: 'UTF-8') {|entry_name, entry_type| ... } -> nil + * Dir.scan(dirpath) -> [[entry_name, entry_type], ...] + * Dir.scan(dirpath, encoding: 'UTF-8') -> [[entry_name, entry_type], ...] + * + * Yields or returns an array of the entry names in the directory at +dirpath+ + * associated with their type, except for <tt>'.'</tt> and <tt>'..'</tt>; + * sets the given encoding onto each returned entry name. + * + * The type symbol is one of: + * ``<code>:file</code>'', ``<code>:directory</code>'', + * ``<code>:characterSpecial</code>'', ``<code>:blockSpecial</code>'', + * ``<code>:fifo</code>'', ``<code>:link</code>'', + * or ``<code>:socket</code>'': + * + * Dir.children('/example') # => [["config.h", :file], ["lib", :directory], ["main.rb", :file]] + * Dir.children('/example').first.first.encoding + * # => #<Encoding:UTF-8> + * Dir.children('/example', encoding: 'US-ASCII').first.encoding + * # => #<Encoding:US-ASCII> + * + * See {String Encoding}[rdoc-ref:encodings.rdoc@String+Encoding]. + * + * Raises an exception if the directory does not exist. + */ +static VALUE +dir_s_scan(int argc, VALUE *argv, VALUE klass) +{ + VALUE dir = dir_open_dir(argc, argv); + return rb_ensure(dir_scan_children, dir, dir_close, dir); +} + static int fnmatch_brace(const char *pattern, VALUE val, void *enc) { @@ -3516,7 +3878,7 @@ file_s_fnmatch(int argc, VALUE *argv, VALUE obj) * call-seq: * Dir.home(user_name = nil) -> dirpath * - * Retruns the home directory path of the user specified with +user_name+ + * Returns the home directory path of the user specified with +user_name+ * if it is not +nil+, or the current login user: * * Dir.home # => "/home/me" @@ -3585,13 +3947,13 @@ nogvl_dir_empty_p(void *ptr) return (void *)INT2FIX(e); } } - while ((dp = READDIR(dir, NULL)) != NULL) { + while ((dp = READDIR_NOGVL(dir, NULL)) != NULL) { if (!to_be_skipped(dp)) { result = Qfalse; break; } } - closedir(dir); + check_closedir(dir); return (void *)result; } @@ -3627,12 +3989,13 @@ rb_dir_s_empty_p(VALUE obj, VALUE dirname) { u_int32_t attrbuf[SIZEUP32(fsobj_tag_t)]; struct attrlist al = {ATTR_BIT_MAP_COUNT, 0, ATTR_CMN_OBJTAG,}; - if (getattrlist(path, &al, attrbuf, sizeof(attrbuf), 0) != 0) + struct getattrlist_args args = GETATTRLIST_ARGS(&al, attrbuf, 0); + if (gvl_getattrlist(&args, path) != 0) rb_sys_fail_path(orig); if (*(const fsobj_tag_t *)(attrbuf+1) == VT_HFS) { al.commonattr = 0; al.dirattr = ATTR_DIR_ENTRYCOUNT; - if (getattrlist(path, &al, attrbuf, sizeof(attrbuf), 0) == 0) { + if (gvl_getattrlist(&args, path) == 0) { if (attrbuf[0] >= 2 * sizeof(u_int32_t)) return RBOOL(attrbuf[1] == 0); if (false_on_notdir) return Qfalse; @@ -3652,8 +4015,27 @@ rb_dir_s_empty_p(VALUE obj, VALUE dirname) void Init_Dir(void) { + sym_directory = ID2SYM(rb_intern("directory")); + sym_link = ID2SYM(rb_intern("link")); + sym_file = ID2SYM(rb_intern("file")); + sym_unknown = ID2SYM(rb_intern("unknown")); + +#if defined(DT_BLK) || defined(S_IFBLK) + sym_block_device = ID2SYM(rb_intern("blockSpecial")); +#endif +#if defined(DT_CHR) || defined(S_IFCHR) + sym_character_device = ID2SYM(rb_intern("characterSpecial")); +#endif +#if defined(DT_FIFO) || defined(S_IFIFO) + sym_fifo = ID2SYM(rb_intern("fifo")); +#endif +#if defined(DT_SOCK) || defined(S_IFSOCK) + sym_socket = ID2SYM(rb_intern("socket")); +#endif + rb_gc_register_address(&chdir_lock.path); rb_gc_register_address(&chdir_lock.thread); + rb_gc_register_address(&last_cwd); rb_cDir = rb_define_class("Dir", rb_cObject); @@ -3665,6 +4047,7 @@ Init_Dir(void) rb_define_singleton_method(rb_cDir, "entries", dir_entries, -1); rb_define_singleton_method(rb_cDir, "each_child", dir_s_each_child, -1); rb_define_singleton_method(rb_cDir, "children", dir_s_children, -1); + rb_define_singleton_method(rb_cDir, "scan", dir_s_scan, -1); rb_define_method(rb_cDir,"fileno", dir_fileno, 0); rb_define_method(rb_cDir,"path", dir_path, 0); @@ -3674,6 +4057,7 @@ Init_Dir(void) rb_define_method(rb_cDir,"each", dir_each, 0); rb_define_method(rb_cDir,"each_child", dir_each_child_m, 0); rb_define_method(rb_cDir,"children", dir_collect_children, 0); + rb_define_method(rb_cDir,"scan", dir_scan_children, 0); rb_define_method(rb_cDir,"rewind", dir_rewind, 0); rb_define_method(rb_cDir,"tell", dir_tell, 0); rb_define_method(rb_cDir,"seek", dir_seek, 1); @@ -3699,26 +4083,19 @@ Init_Dir(void) rb_define_singleton_method(rb_cFile,"fnmatch", file_s_fnmatch, -1); rb_define_singleton_method(rb_cFile,"fnmatch?", file_s_fnmatch, -1); - /* Document-const: FNM_NOESCAPE - * {File::FNM_NOESCAPE}[rdoc-ref:File::Constants@File-3A-3AFNM_NOESCAPE] */ + /* {File::FNM_NOESCAPE}[rdoc-ref:File::Constants@File-3A-3AFNM_NOESCAPE] */ rb_file_const("FNM_NOESCAPE", INT2FIX(FNM_NOESCAPE)); - /* Document-const: FNM_PATHNAME - * {File::FNM_PATHNAME}[rdoc-ref:File::Constants@File-3A-3AFNM_PATHNAME] */ + /* {File::FNM_PATHNAME}[rdoc-ref:File::Constants@File-3A-3AFNM_PATHNAME] */ rb_file_const("FNM_PATHNAME", INT2FIX(FNM_PATHNAME)); - /* Document-const: FNM_DOTMATCH - * {File::FNM_DOTMATCH}[rdoc-ref:File::Constants@File-3A-3AFNM_DOTMATCH] */ + /* {File::FNM_DOTMATCH}[rdoc-ref:File::Constants@File-3A-3AFNM_DOTMATCH] */ rb_file_const("FNM_DOTMATCH", INT2FIX(FNM_DOTMATCH)); - /* Document-const: FNM_CASEFOLD - * {File::FNM_CASEFOLD}[rdoc-ref:File::Constants@File-3A-3AFNM_CASEFOLD] */ + /* {File::FNM_CASEFOLD}[rdoc-ref:File::Constants@File-3A-3AFNM_CASEFOLD] */ rb_file_const("FNM_CASEFOLD", INT2FIX(FNM_CASEFOLD)); - /* Document-const: FNM_EXTGLOB - * {File::FNM_EXTGLOB}[rdoc-ref:File::Constants@File-3A-3AFNM_EXTGLOB] */ + /* {File::FNM_EXTGLOB}[rdoc-ref:File::Constants@File-3A-3AFNM_EXTGLOB] */ rb_file_const("FNM_EXTGLOB", INT2FIX(FNM_EXTGLOB)); - /* Document-const: FNM_SYSCASE - * {File::FNM_SYSCASE}[rdoc-ref:File::Constants@File-3A-3AFNM_SYSCASE] */ + /* {File::FNM_SYSCASE}[rdoc-ref:File::Constants@File-3A-3AFNM_SYSCASE] */ rb_file_const("FNM_SYSCASE", INT2FIX(FNM_SYSCASE)); - /* Document-const: FNM_SHORTNAME - * {File::FNM_SHORTNAME}[rdoc-ref:File::Constants@File-3A-3AFNM_SHORTNAME] */ + /* {File::FNM_SHORTNAME}[rdoc-ref:File::Constants@File-3A-3AFNM_SHORTNAME] */ rb_file_const("FNM_SHORTNAME", INT2FIX(FNM_SHORTNAME)); } |
