diff options
Diffstat (limited to 'dir.c')
-rw-r--r-- | dir.c | 2636 |
1 files changed, 1460 insertions, 1176 deletions
@@ -77,9 +77,9 @@ char *strchr(char*,char); #endif #define USE_NAME_ON_FS_REAL_BASENAME 1 /* platform dependent APIs to - * get real basenames */ + * get real basenames */ #define USE_NAME_ON_FS_BY_FNMATCH 2 /* select the matching - * basename by fnmatch */ + * basename by fnmatch */ #ifdef HAVE_GETATTRLIST # define USE_NAME_ON_FS USE_NAME_ON_FS_REAL_BASENAME @@ -160,12 +160,12 @@ need_normalization(DIR *dirp, const char *path) int ret = getattrlist(path, &al, attrbuf, sizeof(attrbuf), 0); # endif if (!ret) { - const fsobj_tag_t *tag = (void *)(attrbuf+1); - switch (*tag) { - case VT_HFS: - case VT_CIFS: - return TRUE; - } + const fsobj_tag_t *tag = (void *)(attrbuf+1); + switch (*tag) { + case VT_HFS: + case VT_CIFS: + return TRUE; + } } # endif return FALSE; @@ -175,9 +175,9 @@ static inline int has_nonascii(const char *ptr, size_t len) { while (len > 0) { - if (!ISASCII(*ptr)) return 1; - ptr++; - --len; + if (!ISASCII(*ptr)) return 1; + ptr++; + --len; } return 0; } @@ -254,53 +254,53 @@ bracket( if (p >= pend) return NULL; if (*p == '!' || *p == '^') { - not = 1; - p++; + not = 1; + p++; } while (*p != ']') { - const char *t1 = p; - if (escape && *t1 == '\\') - t1++; - if (!*t1) - return NULL; - p = t1 + (r = rb_enc_mbclen(t1, pend, enc)); - if (p >= pend) return NULL; - if (p[0] == '-' && p[1] != ']') { - const char *t2 = p + 1; - int r2; - if (escape && *t2 == '\\') - t2++; - if (!*t2) - return NULL; - p = t2 + (r2 = rb_enc_mbclen(t2, pend, enc)); - if (ok) continue; - if ((r <= (send-s) && memcmp(t1, s, r) == 0) || - (r2 <= (send-s) && memcmp(t2, s, r2) == 0)) { - ok = 1; - continue; - } - c1 = rb_enc_codepoint(s, send, enc); - if (nocase) c1 = rb_enc_toupper(c1, enc); - c2 = rb_enc_codepoint(t1, pend, enc); - if (nocase) c2 = rb_enc_toupper(c2, enc); - if (c1 < c2) continue; - c2 = rb_enc_codepoint(t2, pend, enc); - if (nocase) c2 = rb_enc_toupper(c2, enc); - if (c1 > c2) continue; - } - else { - if (ok) continue; - if (r <= (send-s) && memcmp(t1, s, r) == 0) { - ok = 1; - continue; - } - if (!nocase) continue; - c1 = rb_enc_toupper(rb_enc_codepoint(s, send, enc), enc); - c2 = rb_enc_toupper(rb_enc_codepoint(p, pend, enc), enc); - if (c1 != c2) continue; - } - ok = 1; + const char *t1 = p; + if (escape && *t1 == '\\') + t1++; + if (!*t1) + return NULL; + p = t1 + (r = rb_enc_mbclen(t1, pend, enc)); + if (p >= pend) return NULL; + if (p[0] == '-' && p[1] != ']') { + const char *t2 = p + 1; + int r2; + if (escape && *t2 == '\\') + t2++; + if (!*t2) + return NULL; + p = t2 + (r2 = rb_enc_mbclen(t2, pend, enc)); + if (ok) continue; + if ((r <= (send-s) && memcmp(t1, s, r) == 0) || + (r2 <= (send-s) && memcmp(t2, s, r2) == 0)) { + ok = 1; + continue; + } + c1 = rb_enc_codepoint(s, send, enc); + if (nocase) c1 = rb_enc_toupper(c1, enc); + c2 = rb_enc_codepoint(t1, pend, enc); + if (nocase) c2 = rb_enc_toupper(c2, enc); + if (c1 < c2) continue; + c2 = rb_enc_codepoint(t2, pend, enc); + if (nocase) c2 = rb_enc_toupper(c2, enc); + if (c1 > c2) continue; + } + else { + if (ok) continue; + if (r <= (send-s) && memcmp(t1, s, r) == 0) { + ok = 1; + continue; + } + if (!nocase) continue; + c1 = rb_enc_toupper(rb_enc_codepoint(s, send, enc), enc); + c2 = rb_enc_toupper(rb_enc_codepoint(p, pend, enc), enc); + if (c1 != c2) continue; + } + ok = 1; } return ok == not ? NULL : (char *)p + 1; @@ -338,72 +338,72 @@ fnmatch_helper( int r; if (period && *s == '.' && *UNESCAPE(p) != '.') /* leading period */ - RETURN(FNM_NOMATCH); + RETURN(FNM_NOMATCH); while (1) { - switch (*p) { - case '*': - do { p++; } while (*p == '*'); - if (ISEND(UNESCAPE(p))) { - p = UNESCAPE(p); - RETURN(0); - } - if (ISEND(s)) - RETURN(FNM_NOMATCH); - ptmp = p; - stmp = s; - continue; - - case '?': - if (ISEND(s)) - RETURN(FNM_NOMATCH); - p++; - Inc(s, send, enc); - continue; - - case '[': { - const char *t; - if (ISEND(s)) - RETURN(FNM_NOMATCH); - if ((t = bracket(p + 1, pend, s, send, flags, enc)) != 0) { - p = t; - Inc(s, send, enc); - continue; - } - goto failed; - } - } - - /* ordinary */ - p = UNESCAPE(p); - if (ISEND(s)) - RETURN(ISEND(p) ? 0 : FNM_NOMATCH); - if (ISEND(p)) - goto failed; - r = rb_enc_precise_mbclen(p, pend, enc); - if (!MBCLEN_CHARFOUND_P(r)) - goto failed; - if (r <= (send-s) && memcmp(p, s, r) == 0) { - p += r; - s += r; - continue; - } - if (!nocase) goto failed; - if (rb_enc_toupper(rb_enc_codepoint(p, pend, enc), enc) != - rb_enc_toupper(rb_enc_codepoint(s, send, enc), enc)) - goto failed; - p += r; - Inc(s, send, enc); - continue; + switch (*p) { + case '*': + do { p++; } while (*p == '*'); + if (ISEND(UNESCAPE(p))) { + p = UNESCAPE(p); + RETURN(0); + } + if (ISEND(s)) + RETURN(FNM_NOMATCH); + ptmp = p; + stmp = s; + continue; + + case '?': + if (ISEND(s)) + RETURN(FNM_NOMATCH); + p++; + Inc(s, send, enc); + continue; + + case '[': { + const char *t; + if (ISEND(s)) + RETURN(FNM_NOMATCH); + if ((t = bracket(p + 1, pend, s, send, flags, enc)) != 0) { + p = t; + Inc(s, send, enc); + continue; + } + goto failed; + } + } + + /* ordinary */ + p = UNESCAPE(p); + if (ISEND(s)) + RETURN(ISEND(p) ? 0 : FNM_NOMATCH); + if (ISEND(p)) + goto failed; + r = rb_enc_precise_mbclen(p, pend, enc); + if (!MBCLEN_CHARFOUND_P(r)) + goto failed; + if (r <= (send-s) && memcmp(p, s, r) == 0) { + p += r; + s += r; + continue; + } + if (!nocase) goto failed; + if (rb_enc_toupper(rb_enc_codepoint(p, pend, enc), enc) != + rb_enc_toupper(rb_enc_codepoint(s, send, enc), enc)) + goto failed; + p += r; + Inc(s, send, enc); + continue; failed: /* try next '*' position */ - if (ptmp && stmp) { - p = ptmp; - Inc(stmp, send, enc); /* !ISEND(*stmp) */ - s = stmp; - continue; - } - RETURN(FNM_NOMATCH); + if (ptmp && stmp) { + p = ptmp; + Inc(stmp, send, enc); /* !ISEND(*stmp) */ + s = stmp; + continue; + } + RETURN(FNM_NOMATCH); } } @@ -424,37 +424,37 @@ fnmatch( const char *stmp = 0; if (pathname) { - while (1) { - if (p[0] == '*' && p[1] == '*' && p[2] == '/') { - do { p += 3; } while (p[0] == '*' && p[1] == '*' && p[2] == '/'); - ptmp = p; - stmp = s; - } - if (fnmatch_helper(&p, &s, flags, enc) == 0) { - while (*s && *s != '/') Inc(s, send, enc); - if (*p && *s) { - p++; - s++; - continue; - } - if (!*p && !*s) - return 0; - } - /* failed : try next recursion */ - if (ptmp && stmp && !(period && *stmp == '.')) { - while (*stmp && *stmp != '/') Inc(stmp, send, enc); - if (*stmp) { - p = ptmp; - stmp++; - s = stmp; - continue; - } - } - return FNM_NOMATCH; - } + while (1) { + if (p[0] == '*' && p[1] == '*' && p[2] == '/') { + do { p += 3; } while (p[0] == '*' && p[1] == '*' && p[2] == '/'); + ptmp = p; + stmp = s; + } + if (fnmatch_helper(&p, &s, flags, enc) == 0) { + while (*s && *s != '/') Inc(s, send, enc); + if (*p && *s) { + p++; + s++; + continue; + } + if (!*p && !*s) + return 0; + } + /* failed : try next recursion */ + if (ptmp && stmp && !(period && *stmp == '.')) { + while (*stmp && *stmp != '/') Inc(stmp, send, enc); + if (*stmp) { + p = ptmp; + stmp++; + s = stmp; + continue; + } + } + return FNM_NOMATCH; + } } else - return fnmatch_helper(&p, &s, flags, enc); + return fnmatch_helper(&p, &s, flags, enc); } VALUE rb_cDir; @@ -466,31 +466,26 @@ struct dir_data { }; static void -dir_mark(void *ptr) -{ - struct dir_data *dir = ptr; - rb_gc_mark(dir->path); -} - -static void dir_free(void *ptr) { struct dir_data *dir = ptr; if (dir->dir) closedir(dir->dir); - xfree(dir); } -static size_t -dir_memsize(const void *ptr) -{ - return sizeof(struct dir_data); -} +RUBY_REFERENCES(dir_refs) = { + RUBY_REF_EDGE(struct dir_data, path), + RUBY_REF_END +}; static const rb_data_type_t dir_data_type = { "dir", - {dir_mark, dir_free, dir_memsize,}, - 0, 0, RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_FREE_IMMEDIATELY + { + RUBY_REFS_LIST_PTR(dir_refs), + dir_free, + NULL, // Nothing allocated externally, so don't need a memsize function + }, + 0, NULL, RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_DECL_MARKING | RUBY_TYPED_EMBEDDABLE }; static VALUE dir_close(VALUE); @@ -520,14 +515,14 @@ static DIR * opendir_without_gvl(const char *path) { if (vm_initialized) { - union { const void *in; void *out; } u; + union { const void *in; void *out; } u; - u.in = path; + u.in = path; - return rb_thread_call_without_gvl(nogvl_opendir, u.out, RUBY_UBF_IO, 0); + return IO_WITHOUT_GVL(nogvl_opendir, u.out); } else - return opendir(path); + return opendir(path); } static VALUE @@ -551,23 +546,23 @@ dir_initialize(rb_execution_context_t *ec, VALUE dir, VALUE dirname, VALUE enc) path = RSTRING_PTR(dirname); dp->dir = opendir_without_gvl(path); if (dp->dir == NULL) { - int e = errno; - if (rb_gc_for_fd(e)) { - dp->dir = opendir_without_gvl(path); - } + int e = errno; + if (rb_gc_for_fd(e)) { + dp->dir = opendir_without_gvl(path); + } #ifdef HAVE_GETATTRLIST - 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) { - dp->dir = opendir_without_gvl(path); - } - } -#endif - if (dp->dir == NULL) { - RB_GC_GUARD(dirname); - rb_syserr_fail_path(e, orig); - } + 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) { + dp->dir = opendir_without_gvl(path); + } + } +#endif + if (dp->dir == NULL) { + RB_GC_GUARD(dirname); + rb_syserr_fail_path(e, orig); + } } RB_OBJ_WRITE(dir, &dp->path, orig); @@ -591,6 +586,45 @@ dir_s_close(rb_execution_context_t *ec, VALUE klass, VALUE dir) return dir_close(dir); } +# if defined(HAVE_FDOPENDIR) && defined(HAVE_DIRFD) +/* + * call-seq: + * Dir.for_fd(fd) -> dir + * + * Returns a new \Dir object representing the directory specified by the given + * integer directory file descriptor +fd+: + * + * d0 = Dir.new('..') + * d1 = Dir.for_fd(d0.fileno) + * + * Note that the returned +d1+ does not have an associated path: + * + * d0.path # => '..' + * d1.path # => nil + * + * This method uses the + * {fdopendir()}[https://www.man7.org/linux/man-pages/man3/fdopendir.3p.html] + * function defined by POSIX 2008; + * the method is not implemented on non-POSIX platforms (raises NotImplementedError). + */ +static VALUE +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)))) { + rb_sys_fail("fdopendir"); + UNREACHABLE_RETURN(Qnil); + } + + RB_OBJ_WRITE(dir, &dp->path, Qnil); + return dir; +} +#else +#define dir_s_for_fd rb_f_notimplement +#endif + NORETURN(static void dir_closed(void)); static void @@ -618,10 +652,13 @@ dir_check(VALUE dir) /* - * call-seq: - * dir.inspect -> string + * call-seq: + * inspect -> string + * + * Returns a string description of +self+: + * + * Dir.new('example').inspect # => "#<Dir:example>" * - * Return a string describing this Dir object. */ static VALUE dir_inspect(VALUE dir) @@ -630,12 +667,12 @@ dir_inspect(VALUE dir) TypedData_Get_Struct(dir, struct dir_data, &dir_data_type, dirp); if (!NIL_P(dirp->path)) { - VALUE str = rb_str_new_cstr("#<"); - rb_str_append(str, rb_class_name(CLASS_OF(dir))); - rb_str_cat2(str, ":"); - rb_str_append(str, dirp->path); - rb_str_cat2(str, ">"); - return str; + VALUE str = rb_str_new_cstr("#<"); + rb_str_append(str, rb_class_name(CLASS_OF(dir))); + rb_str_cat2(str, ":"); + rb_str_append(str, dirp->path); + rb_str_cat2(str, ">"); + return str; } return rb_funcallv(dir, idTo_s, 0, 0); } @@ -655,18 +692,18 @@ dir_inspect(VALUE dir) #ifdef HAVE_DIRFD /* - * call-seq: - * dir.fileno -> integer - * - * Returns the file descriptor used in <em>dir</em>. + * call-seq: + * fileno -> integer * - * d = Dir.new("..") - * d.fileno #=> 8 + * Returns the file descriptor used in <em>dir</em>. * - * This method uses dirfd() function defined by POSIX 2008. - * NotImplementedError is raised on other platforms, such as Windows, - * which doesn't provide the function. + * d = Dir.new('..') + * d.fileno # => 8 * + * This method uses the + * {dirfd()}[https://www.man7.org/linux/man-pages/man3/dirfd.3.html] + * function defined by POSIX 2008; + * the method is not implemented on non-POSIX platforms (raises NotImplementedError). */ static VALUE dir_fileno(VALUE dir) @@ -677,7 +714,7 @@ dir_fileno(VALUE dir) GetDIR(dir, dirp); fd = dirfd(dirp->dir); if (fd == -1) - rb_sys_fail("dirfd"); + rb_sys_fail("dirfd"); return INT2NUM(fd); } #else @@ -685,14 +722,14 @@ dir_fileno(VALUE dir) #endif /* - * call-seq: - * dir.path -> string or nil - * dir.to_path -> string or nil + * call-seq: + * path -> string or nil + * + * Returns the +dirpath+ string that was used to create +self+ + * (or +nil+ if created by method Dir.for_fd): * - * Returns the path parameter passed to <em>dir</em>'s constructor. + * Dir.new('example').path # => "example" * - * d = Dir.new("..") - * d.path #=> ".." */ static VALUE dir_path(VALUE dir) @@ -709,12 +746,12 @@ static int fundamental_encoding_p(rb_encoding *enc) { switch (rb_enc_to_index(enc)) { - case ENCINDEX_ASCII: + case ENCINDEX_ASCII_8BIT: case ENCINDEX_US_ASCII: case ENCINDEX_UTF_8: - return TRUE; + return TRUE; default: - return FALSE; + return FALSE; } } # define READDIR(dir, enc) rb_w32_readdir((dir), (enc)) @@ -731,11 +768,11 @@ to_be_skipped(const struct dirent *dp) #ifdef HAVE_DIRENT_NAMLEN switch (NAMLEN(dp)) { case 2: - if (name[1] != '.') return FALSE; + if (name[1] != '.') return FALSE; case 1: - return TRUE; + return TRUE; default: - break; + break; } #else if (!name[1]) return TRUE; @@ -746,16 +783,18 @@ to_be_skipped(const struct dirent *dp) } /* - * call-seq: - * dir.read -> string or nil + * call-seq: + * read -> string or nil * - * Reads the next entry from <em>dir</em> and returns it as a string. - * Returns <code>nil</code> at the end of the stream. + * Reads and returns the next entry name from +self+; + * returns +nil+ if at end-of-stream; + * see {Dir As Stream-Like}[rdoc-ref:Dir@Dir+As+Stream-Like]: + * + * dir = Dir.new('example') + * dir.read # => "." + * dir.read # => ".." + * dir.read # => "config.h" * - * d = Dir.new("testdir") - * d.read #=> "." - * d.read #=> ".." - * d.read #=> "config.h" */ static VALUE dir_read(VALUE dir) @@ -764,14 +803,14 @@ dir_read(VALUE dir) struct dirent *dp; GetDIR(dir, dirp); - errno = 0; + rb_errno_set(0); if ((dp = READDIR(dirp->dir, dirp->enc)) != NULL) { - return rb_external_str_new_with_enc(dp->d_name, NAMLEN(dp), dirp->enc); + return rb_external_str_new_with_enc(dp->d_name, NAMLEN(dp), dirp->enc); } else { - int e = errno; - if (e != 0) rb_syserr_fail(e, 0); - return Qnil; /* end of stream */ + int e = errno; + if (e != 0) rb_syserr_fail(e, 0); + return Qnil; /* end of stream */ } } @@ -784,24 +823,23 @@ dir_yield(VALUE arg, VALUE path) } /* - * call-seq: - * dir.each { |filename| block } -> dir - * dir.each -> an_enumerator + * call-seq: + * each {|entry_name| ... } -> self * - * Calls the block once for each entry in this directory, passing the - * filename of each entry as a parameter to the block. + * Calls the block with each entry name in +self+: * - * If no block is given, an enumerator is returned instead. + * Dir.new('example').each {|entry_name| p entry_name } * - * d = Dir.new("testdir") - * d.each {|x| puts "Got #{x}" } + * Output: + + * "." + * ".." + * "config.h" + * "lib" + * "main.rb" * - * <em>produces:</em> + * With no block given, returns an Enumerator. * - * Got . - * Got .. - * Got config.h - * Got main.rb */ static VALUE dir_each(VALUE dir) @@ -821,39 +859,40 @@ dir_each_entry(VALUE dir, VALUE (*each)(VALUE, VALUE), VALUE arg, int children_o rewinddir(dirp->dir); IF_NORMALIZE_UTF8PATH(norm_p = need_normalization(dirp->dir, RSTRING_PTR(dirp->path))); while ((dp = READDIR(dirp->dir, dirp->enc)) != NULL) { - const char *name = dp->d_name; - size_t namlen = NAMLEN(dp); - VALUE path; - - if (children_only && name[0] == '.') { - if (namlen == 1) continue; /* current directory */ - if (namlen == 2 && name[1] == '.') continue; /* parent directory */ - } + const char *name = dp->d_name; + size_t namlen = NAMLEN(dp); + VALUE path; + + if (children_only && name[0] == '.') { + if (namlen == 1) continue; /* current directory */ + if (namlen == 2 && name[1] == '.') continue; /* parent directory */ + } #if NORMALIZE_UTF8PATH - if (norm_p && has_nonascii(name, namlen) && - !NIL_P(path = rb_str_normalize_ospath(name, namlen))) { - path = rb_external_str_with_enc(path, dirp->enc); - } - else + if (norm_p && has_nonascii(name, namlen) && + !NIL_P(path = rb_str_normalize_ospath(name, namlen))) { + path = rb_external_str_with_enc(path, dirp->enc); + } + else #endif - path = rb_external_str_new_with_enc(name, namlen, dirp->enc); - (*each)(arg, path); + path = rb_external_str_new_with_enc(name, namlen, dirp->enc); + (*each)(arg, path); } return dir; } #ifdef HAVE_TELLDIR /* - * call-seq: - * dir.pos -> integer - * dir.tell -> integer + * call-seq: + * tell -> integer * - * Returns the current position in <em>dir</em>. See also Dir#seek. + * Returns the current position of +self+; + * see {Dir As Stream-Like}[rdoc-ref:Dir@Dir+As+Stream-Like]: + * + * dir = Dir.new('example') + * dir.tell # => 0 + * dir.read # => "." + * dir.tell # => 1 * - * d = Dir.new("testdir") - * d.tell #=> 0 - * d.read #=> "." - * d.tell #=> 12 */ static VALUE dir_tell(VALUE dir) @@ -871,18 +910,24 @@ dir_tell(VALUE dir) #ifdef HAVE_SEEKDIR /* - * call-seq: - * dir.seek( integer ) -> dir - * - * Seeks to a particular location in <em>dir</em>. <i>integer</i> - * must be a value returned by Dir#tell. - * - * d = Dir.new("testdir") #=> #<Dir:0x401b3c40> - * d.read #=> "." - * i = d.tell #=> 12 - * d.read #=> ".." - * d.seek(i) #=> #<Dir:0x401b3c40> - * d.read #=> ".." + * call-seq: + * seek(position) -> self + * + * Sets the position in +self+ and returns +self+. + * The value of +position+ should have been returned from an earlier call to #tell; + * if not, the return values from subsequent calls to #read are unspecified. + * + * See {Dir As Stream-Like}[rdoc-ref:Dir@Dir+As+Stream-Like]. + * + * Examples: + * + * dir = Dir.new('example') + * dir.pos # => 0 + * dir.seek(3) # => #<Dir:example> + * dir.pos # => 3 + * dir.seek(30) # => #<Dir:example> + * dir.pos # => 5 + * */ static VALUE dir_seek(VALUE dir, VALUE pos) @@ -900,17 +945,24 @@ dir_seek(VALUE dir, VALUE pos) #ifdef HAVE_SEEKDIR /* - * call-seq: - * dir.pos = integer -> integer + * call-seq: + * pos = position -> integer + * + * Sets the position in +self+ and returns +position+. + * The value of +position+ should have been returned from an earlier call to #tell; + * if not, the return values from subsequent calls to #read are unspecified. * - * Synonym for Dir#seek, but returns the position parameter. + * See {Dir As Stream-Like}[rdoc-ref:Dir@Dir+As+Stream-Like]. + * + * Examples: + * + * dir = Dir.new('example') + * dir.pos # => 0 + * dir.pos = 3 # => 3 + * dir.pos # => 3 + * dir.pos = 30 # => 30 + * dir.pos # => 5 * - * d = Dir.new("testdir") #=> #<Dir:0x401b3c40> - * d.read #=> "." - * i = d.pos #=> 12 - * d.read #=> ".." - * d.pos = i #=> 12 - * d.read #=> ".." */ static VALUE dir_set_pos(VALUE dir, VALUE pos) @@ -923,15 +975,19 @@ dir_set_pos(VALUE dir, VALUE pos) #endif /* - * call-seq: - * dir.rewind -> dir + * call-seq: + * rewind -> self * - * Repositions <em>dir</em> to the first entry. + * Sets the position in +self+ to zero; + * see {Dir As Stream-Like}[rdoc-ref:Dir@Dir+As+Stream-Like]: + * + * dir = Dir.new('example') + * dir.read # => "." + * dir.read # => ".." + * dir.pos # => 2 + * dir.rewind # => #<Dir:example> + * dir.pos # => 0 * - * d = Dir.new("testdir") - * d.read #=> "." - * d.rewind #=> #<Dir:0x401b3fb0> - * d.read #=> "." */ static VALUE dir_rewind(VALUE dir) @@ -944,14 +1000,18 @@ dir_rewind(VALUE dir) } /* - * call-seq: - * dir.close -> nil + * call-seq: + * close -> nil * - * Closes the directory stream. - * Calling this method on closed Dir object is ignored since Ruby 2.3. + * Closes the stream in +self+, if it is open, and returns +nil+; + * ignored if +self+ is already closed: + * + * dir = Dir.new('example') + * dir.read # => "." + * dir.close # => nil + * dir.close # => nil + * dir.read # Raises IOError. * - * d = Dir.new("testdir") - * d.close #=> nil */ static VALUE dir_close(VALUE dir) @@ -975,30 +1035,80 @@ nogvl_chdir(void *ptr) } static void -dir_chdir(VALUE path) +dir_chdir0(VALUE path) { if (chdir(RSTRING_PTR(path)) < 0) - rb_sys_fail_path(path); + rb_sys_fail_path(path); +} + +static struct { + VALUE thread; + VALUE path; + int line; + int blocking; +} chdir_lock = { + .blocking = 0, .thread = Qnil, + .path = Qnil, .line = 0, +}; + +static void +chdir_enter(void) +{ + if (chdir_lock.blocking == 0) { + chdir_lock.path = rb_source_location(&chdir_lock.line); + } + chdir_lock.blocking++; + if (NIL_P(chdir_lock.thread)) { + chdir_lock.thread = rb_thread_current(); + } +} + +static void +chdir_leave(void) +{ + chdir_lock.blocking--; + if (chdir_lock.blocking == 0) { + chdir_lock.thread = Qnil; + chdir_lock.path = Qnil; + chdir_lock.line = 0; + } } -static int chdir_blocking = 0; -static VALUE chdir_thread = Qnil; +static int +chdir_alone_block_p(void) +{ + int block_given = rb_block_given_p(); + if (chdir_lock.blocking > 0) { + if (rb_thread_current() != chdir_lock.thread) + rb_raise(rb_eRuntimeError, "conflicting chdir during another chdir block"); + if (!block_given) { + if (!NIL_P(chdir_lock.path)) { + rb_warn("conflicting chdir during another chdir block\n" + "%" PRIsVALUE ":%d: note: previous chdir was here", + chdir_lock.path, chdir_lock.line); + } + else { + rb_warn("conflicting chdir during another chdir block"); + } + } + } + return block_given; +} struct chdir_data { VALUE old_path, new_path; int done; + bool yield_path; }; static VALUE chdir_yield(VALUE v) { struct chdir_data *args = (void *)v; - dir_chdir(args->new_path); + dir_chdir0(args->new_path); args->done = TRUE; - chdir_blocking++; - if (NIL_P(chdir_thread)) - chdir_thread = rb_thread_current(); - return rb_yield(args->new_path); + chdir_enter(); + return args->yield_path ? rb_yield(args->new_path) : rb_yield_values2(0, NULL); } static VALUE @@ -1006,53 +1116,96 @@ chdir_restore(VALUE v) { struct chdir_data *args = (void *)v; if (args->done) { - chdir_blocking--; - if (chdir_blocking == 0) - chdir_thread = Qnil; - dir_chdir(args->old_path); + chdir_leave(); + dir_chdir0(args->old_path); } return Qnil; } +static VALUE +chdir_path(VALUE path, bool yield_path) +{ + if (chdir_alone_block_p()) { + struct chdir_data args; + + args.old_path = rb_str_encode_ospath(rb_dir_getwd()); + args.new_path = path; + args.done = FALSE; + args.yield_path = yield_path; + return rb_ensure(chdir_yield, (VALUE)&args, chdir_restore, (VALUE)&args); + } + else { + char *p = RSTRING_PTR(path); + int r = IO_WITHOUT_GVL_INT(nogvl_chdir, p); + if (r < 0) + rb_sys_fail_path(path); + } + + return INT2FIX(0); +} + /* - * call-seq: - * Dir.chdir( [ string] ) -> 0 - * Dir.chdir( [ string] ) {| path | block } -> anObject - * - * Changes the current working directory of the process to the given - * string. When called without an argument, changes the directory to - * the value of the environment variable <code>HOME</code>, or - * <code>LOGDIR</code>. SystemCallError (probably Errno::ENOENT) if - * the target directory does not exist. - * - * If a block is given, it is passed the name of the new current - * directory, and the block is executed with that as the current - * directory. The original working directory is restored when the block - * exits. The return value of <code>chdir</code> is the value of the - * block. <code>chdir</code> blocks can be nested, but in a - * multi-threaded program an error will be raised if a thread attempts - * to open a <code>chdir</code> block while another thread has one - * open or a call to <code>chdir</code> without a block occurs inside - * a block passed to <code>chdir</code> (even in the same thread). - * - * Dir.chdir("/var/spool/mail") - * puts Dir.pwd - * Dir.chdir("/tmp") do - * puts Dir.pwd - * Dir.chdir("/usr") do - * puts Dir.pwd - * end - * puts Dir.pwd + * call-seq: + * Dir.chdir(new_dirpath) -> 0 + * Dir.chdir -> 0 + * Dir.chdir(new_dirpath) {|new_dirpath| ... } -> object + * Dir.chdir {|cur_dirpath| ... } -> object + * + * Changes the current working directory. + * + * With argument +new_dirpath+ and no block, + * changes to the given +dirpath+: + * + * Dir.pwd # => "/example" + * Dir.chdir('..') # => 0 + * Dir.pwd # => "/" + * + * With no argument and no block: + * + * - Changes to the value of environment variable +HOME+ if defined. + * - Otherwise changes to the value of environment variable +LOGDIR+ if defined. + * - Otherwise makes no change. + * + * With argument +new_dirpath+ and a block, temporarily changes the working directory: + * + * - Calls the block with the argument. + * - Changes to the given directory. + * - Executes the block (yielding the new path). + * - Restores the previous working directory. + * - Returns the block's return value. + * + * Example: + * + * Dir.chdir('/var/spool/mail') + * Dir.pwd # => "/var/spool/mail" + * Dir.chdir('/tmp') do + * Dir.pwd # => "/tmp" + * end + * Dir.pwd # => "/var/spool/mail" + * + * With no argument and a block, + * calls the block with the current working directory (string) + * and returns the block's return value. + * + * Calls to \Dir.chdir with blocks may be nested: + * + * Dir.chdir('/var/spool/mail') + * Dir.pwd # => "/var/spool/mail" + * Dir.chdir('/tmp') do + * Dir.pwd # => "/tmp" + * Dir.chdir('/usr') do + * Dir.pwd # => "/usr" * end - * puts Dir.pwd + * Dir.pwd # => "/tmp" + * end + * Dir.pwd # => "/var/spool/mail" * - * <em>produces:</em> + * In a multi-threaded program an error is raised if a thread attempts + * to open a +chdir+ block while another thread has one open, + * or a call to +chdir+ without a block occurs inside + * a block passed to +chdir+ (even in the same thread). * - * /var/spool/mail - * /tmp - * /usr - * /tmp - * /var/spool/mail + * Raises an exception if the target directory does not exist. */ static VALUE dir_s_chdir(int argc, VALUE *argv, VALUE obj) @@ -1063,39 +1216,170 @@ dir_s_chdir(int argc, VALUE *argv, VALUE obj) path = rb_str_encode_ospath(rb_get_path(argv[0])); } else { - const char *dist = getenv("HOME"); - if (!dist) { - dist = getenv("LOGDIR"); - if (!dist) rb_raise(rb_eArgError, "HOME/LOGDIR not set"); - } - path = rb_str_new2(dist); + const char *dist = getenv("HOME"); + if (!dist) { + dist = getenv("LOGDIR"); + if (!dist) rb_raise(rb_eArgError, "HOME/LOGDIR not set"); + } + path = rb_str_new2(dist); } - if (chdir_blocking > 0) { - if (rb_thread_current() != chdir_thread) - rb_raise(rb_eRuntimeError, "conflicting chdir during another chdir block"); - if (!rb_block_given_p()) - rb_warn("conflicting chdir during another chdir block"); + return chdir_path(path, true); +} + +#if defined(HAVE_FCHDIR) && defined(HAVE_DIRFD) && HAVE_FCHDIR && HAVE_DIRFD +static void * +nogvl_fchdir(void *ptr) +{ + const int *fd = ptr; + + return (void *)(VALUE)fchdir(*fd); +} + +static void +dir_fchdir(int fd) +{ + if (fchdir(fd) < 0) + rb_sys_fail("fchdir"); +} + +struct fchdir_data { + VALUE old_dir; + int fd; + int done; +}; + +static VALUE +fchdir_yield(VALUE v) +{ + struct fchdir_data *args = (void *)v; + dir_fchdir(args->fd); + args->done = TRUE; + chdir_enter(); + return rb_yield_values(0); +} + +static VALUE +fchdir_restore(VALUE v) +{ + struct fchdir_data *args = (void *)v; + if (args->done) { + chdir_leave(); + dir_fchdir(RB_NUM2INT(dir_fileno(args->old_dir))); } + dir_close(args->old_dir); + return Qnil; +} - if (rb_block_given_p()) { - struct chdir_data args; +/* + * call-seq: + * Dir.fchdir(fd) -> 0 + * Dir.fchdir(fd) { ... } -> object + * + * Changes the current working directory to the directory + * specified by the integer file descriptor +fd+. + * + * When passing a file descriptor over a UNIX socket or to a child process, + * using +fchdir+ instead of +chdir+ avoids the + * {time-of-check to time-of-use vulnerability}[https://en.wikipedia.org/wiki/Time-of-check_to_time-of-use] + * + * With no block, changes to the directory given by +fd+: + * + * Dir.chdir('/var/spool/mail') + * Dir.pwd # => "/var/spool/mail" + * dir = Dir.new('/usr') + * fd = dir.fileno + * Dir.fchdir(fd) + * Dir.pwd # => "/usr" + * + * With a block, temporarily changes the working directory: + * + * - Calls the block with the argument. + * - Changes to the given directory. + * - Executes the block (yields no args). + * - Restores the previous working directory. + * - Returns the block's return value. + * + * Example: + * + * Dir.chdir('/var/spool/mail') + * Dir.pwd # => "/var/spool/mail" + * dir = Dir.new('/tmp') + * fd = dir.fileno + * Dir.fchdir(fd) do + * Dir.pwd # => "/tmp" + * end + * Dir.pwd # => "/var/spool/mail" + * + * This method uses the + * {fchdir()}[https://www.man7.org/linux/man-pages/man3/fchdir.3p.html] + * function defined by POSIX 2008; + * the method is not implemented on non-POSIX platforms (raises NotImplementedError). + * + * Raises an exception if the file descriptor is not valid. + * + * In a multi-threaded program an error is raised if a thread attempts + * to open a +chdir+ block while another thread has one open, + * or a call to +chdir+ without a block occurs inside + * a block passed to +chdir+ (even in the same thread). + */ +static VALUE +dir_s_fchdir(VALUE klass, VALUE fd_value) +{ + int fd = RB_NUM2INT(fd_value); - args.old_path = rb_str_encode_ospath(rb_dir_getwd()); - args.new_path = path; - args.done = FALSE; - return rb_ensure(chdir_yield, (VALUE)&args, chdir_restore, (VALUE)&args); + if (chdir_alone_block_p()) { + struct fchdir_data args; + args.old_dir = dir_s_alloc(klass); + dir_initialize(NULL, args.old_dir, rb_fstring_cstr("."), Qnil); + args.fd = fd; + args.done = FALSE; + return rb_ensure(fchdir_yield, (VALUE)&args, fchdir_restore, (VALUE)&args); } else { - char *p = RSTRING_PTR(path); - int r = (int)(VALUE)rb_thread_call_without_gvl(nogvl_chdir, p, - RUBY_UBF_IO, 0); - if (r < 0) - rb_sys_fail_path(path); + int r = IO_WITHOUT_GVL_INT(nogvl_fchdir, &fd); + if (r < 0) + rb_sys_fail("fchdir"); } return INT2FIX(0); } +#else +#define dir_s_fchdir rb_f_notimplement +#endif + +/* + * call-seq: + * chdir -> 0 + * chdir { ... } -> object + * + * Changes the current working directory to +self+: + * + * Dir.pwd # => "/" + * dir = Dir.new('example') + * dir.chdir + * Dir.pwd # => "/example" + * + * With a block, temporarily changes the working directory: + * + * - Calls the block. + * - Changes to the given directory. + * - Executes the block (yields no args). + * - Restores the previous working directory. + * - Returns the block's return value. + * + * Uses Dir.fchdir if available, and Dir.chdir if not, see those + * methods for caveats. + */ +static VALUE +dir_chdir(VALUE dir) +{ +#if defined(HAVE_FCHDIR) && defined(HAVE_DIRFD) && HAVE_FCHDIR && HAVE_DIRFD + return dir_s_fchdir(rb_cDir, dir_fileno(dir)); +#else + return chdir_path(dir_get(dir)->path, false); +#endif +} #ifndef _WIN32 VALUE @@ -1131,28 +1415,26 @@ rb_dir_getwd(void) switch (fsenc) { case ENCINDEX_US_ASCII: - fsenc = ENCINDEX_ASCII; - case ENCINDEX_ASCII: - break; + fsenc = ENCINDEX_ASCII_8BIT; + case ENCINDEX_ASCII_8BIT: + break; #if defined _WIN32 || defined __APPLE__ default: - return rb_str_conv_enc(cwd, NULL, fs); + return rb_str_conv_enc(cwd, NULL, fs); #endif } return rb_enc_associate_index(cwd, fsenc); } /* - * call-seq: - * Dir.getwd -> string - * Dir.pwd -> string + * call-seq: + * Dir.pwd -> string * - * Returns the path to the current working directory of this process as - * a string. + * Returns the path to the current working directory: + * + * Dir.chdir("/tmp") # => 0 + * Dir.pwd # => "/tmp" * - * Dir.chdir("/tmp") #=> 0 - * Dir.getwd #=> "/tmp" - * Dir.pwd #=> "/tmp" */ static VALUE dir_s_getwd(VALUE dir) @@ -1174,28 +1456,31 @@ check_dirname(VALUE dir) pend = path + len; pend = rb_enc_path_end(rb_enc_path_skip_prefix(path, pend, enc), pend, enc); if (pend - path < len) { - d = rb_str_subseq(d, 0, pend - path); - StringValueCStr(d); + d = rb_str_subseq(d, 0, pend - path); + StringValueCStr(d); } return rb_str_encode_ospath(d); } #if defined(HAVE_CHROOT) /* - * call-seq: - * Dir.chroot( string ) -> 0 + * call-seq: + * Dir.chroot(dirpath) -> 0 * - * Changes this process's idea of the file system root. Only a - * privileged process may make this call. Not available on all - * platforms. On Unix systems, see <code>chroot(2)</code> for more - * information. + * Changes the root directory of the calling process to that specified in +dirpath+. + * The new root directory is used for pathnames beginning with <tt>'/'</tt>. + * The root directory is inherited by all children of the calling process. + * + * Only a privileged process may call +chroot+. + * + * See {Linux chroot}[https://man7.org/linux/man-pages/man2/chroot.2.html]. */ static VALUE dir_s_chroot(VALUE dir, VALUE path) { path = check_dirname(path); if (chroot(RSTRING_PTR(path)) == -1) - rb_sys_fail_path(path); + rb_sys_fail_path(path); return INT2FIX(0); } @@ -1217,18 +1502,20 @@ nogvl_mkdir(void *ptr) } /* - * call-seq: - * Dir.mkdir( string [, integer] ) -> 0 + * call-seq: + * Dir.mkdir(dirpath, permissions = 0775) -> 0 * - * Makes a new directory named by <i>string</i>, with permissions - * specified by the optional parameter <i>anInteger</i>. The - * permissions may be modified by the value of File::umask, and are - * ignored on NT. Raises a SystemCallError if the directory cannot be - * created. See also the discussion of permissions in the class - * documentation for File. + * Creates a directory in the underlying file system + * at +dirpath+ with the given +permissions+; + * returns zero: * - * Dir.mkdir(File.join(Dir.home, ".foo"), 0700) #=> 0 + * Dir.mkdir('foo') + * File.stat(Dir.new('foo')).mode.to_s(8)[1..4] # => "0755" + * Dir.mkdir('bar', 0644) + * File.stat(Dir.new('bar')).mode.to_s(8)[1..4] # => "0644" * + * See {File Permissions}[rdoc-ref:File@File+Permissions]. + * Note that argument +permissions+ is ignored on Windows. */ static VALUE dir_s_mkdir(int argc, VALUE *argv, VALUE obj) @@ -1238,17 +1525,17 @@ dir_s_mkdir(int argc, VALUE *argv, VALUE obj) int r; if (rb_scan_args(argc, argv, "11", &path, &vmode) == 2) { - m.mode = NUM2MODET(vmode); + m.mode = NUM2MODET(vmode); } else { - m.mode = 0777; + m.mode = 0777; } path = check_dirname(path); m.path = RSTRING_PTR(path); - r = (int)(VALUE)rb_thread_call_without_gvl(nogvl_mkdir, &m, RUBY_UBF_IO, 0); + r = IO_WITHOUT_GVL_INT(nogvl_mkdir, &m); if (r < 0) - rb_sys_fail_path(path); + rb_sys_fail_path(path); return INT2FIX(0); } @@ -1262,13 +1549,14 @@ nogvl_rmdir(void *ptr) } /* - * call-seq: - * Dir.delete( string ) -> 0 - * Dir.rmdir( string ) -> 0 - * Dir.unlink( string ) -> 0 + * call-seq: + * Dir.rmdir(dirpath) -> 0 + * + * Removes the directory at +dirpath+ from the underlying file system: * - * Deletes the named directory. Raises a subclass of SystemCallError - * if the directory isn't empty. + * Dir.rmdir('foo') # => 0 + * + * Raises an exception if the directory is not empty. */ static VALUE dir_s_rmdir(VALUE obj, VALUE dir) @@ -1278,9 +1566,9 @@ dir_s_rmdir(VALUE obj, VALUE dir) dir = check_dirname(dir); p = RSTRING_PTR(dir); - r = (int)(VALUE)rb_thread_call_without_gvl(nogvl_rmdir, (void *)p, RUBY_UBF_IO, 0); + r = IO_WITHOUT_GVL_INT(nogvl_rmdir, (void *)p); if (r < 0) - rb_sys_fail_path(dir); + rb_sys_fail_path(dir); return INT2FIX(0); } @@ -1386,8 +1674,8 @@ at_subpath(int fd, size_t baselen, const char *path) { #if USE_OPENDIR_AT if (fd != (int)AT_FDCWD && baselen > 0) { - path += baselen; - if (*path == '/') ++path; + path += baselen; + if (*path == '/') ++path; } #endif return *path ? path : "."; @@ -1403,7 +1691,7 @@ do_stat(int fd, size_t baselen, const char *path, struct stat *pst, int flags, r int ret = STAT(path, pst); #endif if (ret < 0 && !to_be_ignored(errno)) - sys_warning(path, enc); + sys_warning(path, enc); return ret; } @@ -1418,7 +1706,7 @@ do_lstat(int fd, size_t baselen, const char *path, struct stat *pst, int flags, int ret = lstat(path, pst); #endif if (ret < 0 && !to_be_ignored(errno)) - sys_warning(path, enc); + sys_warning(path, enc); return ret; } @@ -1443,9 +1731,9 @@ static int gc_for_fd_with_gvl(int e) { if (vm_initialized) - return (int)(VALUE)rb_thread_call_with_gvl(with_gvl_gc_for_fd, &e); + return (int)(VALUE)rb_thread_call_with_gvl(with_gvl_gc_for_fd, &e); else - return RBOOL(rb_gc_for_fd(e)); + return RBOOL(rb_gc_for_fd(e)); } static void * @@ -1457,32 +1745,32 @@ nogvl_opendir_at(void *ptr) #if USE_OPENDIR_AT const int opendir_flags = (O_RDONLY|O_CLOEXEC| # ifdef O_DIRECTORY - O_DIRECTORY| + O_DIRECTORY| # endif /* O_DIRECTORY */ - 0); + 0); int fd = openat(oaa->basefd, oaa->path, opendir_flags); dirp = fd >= 0 ? fdopendir(fd) : 0; if (!dirp) { - int e = errno; - - switch (gc_for_fd_with_gvl(e)) { - default: - if (fd < 0) fd = openat(oaa->basefd, oaa->path, opendir_flags); - if (fd >= 0) dirp = fdopendir(fd); - if (dirp) return dirp; - - e = errno; - /* fallthrough*/ - case 0: - if (fd >= 0) close(fd); - errno = e; - } + int e = errno; + + switch (gc_for_fd_with_gvl(e)) { + default: + if (fd < 0) fd = openat(oaa->basefd, oaa->path, opendir_flags); + if (fd >= 0) dirp = fdopendir(fd); + if (dirp) return dirp; + + e = errno; + /* fallthrough*/ + case 0: + if (fd >= 0) close(fd); + rb_errno_set(e); + } } #else /* !USE_OPENDIR_AT */ dirp = opendir(oaa->path); if (!dirp && gc_for_fd_with_gvl(errno)) - dirp = opendir(oaa->path); + dirp = opendir(oaa->path); #endif /* !USE_OPENDIR_AT */ return dirp; @@ -1497,37 +1785,37 @@ opendir_at(int basefd, const char *path) oaa.path = path; if (vm_initialized) - return rb_thread_call_without_gvl(nogvl_opendir_at, &oaa, RUBY_UBF_IO, 0); + return IO_WITHOUT_GVL(nogvl_opendir_at, &oaa); else - return nogvl_opendir_at(&oaa); + return nogvl_opendir_at(&oaa); } static DIR * do_opendir(const int basefd, size_t baselen, const char *path, int flags, rb_encoding *enc, - ruby_glob_errfunc *errfunc, VALUE arg, int *status) + ruby_glob_errfunc *errfunc, VALUE arg, int *status) { DIR *dirp; #ifdef _WIN32 VALUE tmp = 0; if (!fundamental_encoding_p(enc)) { - tmp = rb_enc_str_new(path, strlen(path), enc); - tmp = rb_str_encode_ospath(tmp); - path = RSTRING_PTR(tmp); + tmp = rb_enc_str_new(path, strlen(path), enc); + tmp = rb_str_encode_ospath(tmp); + path = RSTRING_PTR(tmp); } #endif dirp = opendir_at(basefd, at_subpath(basefd, baselen, path)); if (!dirp) { - int e = errno; - - *status = 0; - if (!to_be_ignored(e)) { - if (errfunc) { - *status = (*errfunc)(path, arg, enc, e); - } - else { - sys_warning(path, enc); - } - } + int e = errno; + + *status = 0; + if (!to_be_ignored(e)) { + if (errfunc) { + *status = (*errfunc)(path, arg, enc, e); + } + else { + sys_warning(path, enc); + } + } } #ifdef _WIN32 if (tmp) rb_str_resize(tmp, 0); /* GC guard */ @@ -1550,37 +1838,37 @@ has_magic(const char *p, const char *pend, int flags, rb_encoding *enc) register char c; while (p < pend && (c = *p++) != 0) { - switch (c) { - case '{': - return BRACE; + switch (c) { + case '{': + return BRACE; - case '*': - case '?': - case '[': - hasmagical = 1; - break; + case '*': + case '?': + case '[': + hasmagical = 1; + break; - case '\\': - if (escape && p++ >= pend) - continue; - break; + case '\\': + if (escape && p++ >= pend) + continue; + break; #ifdef _WIN32 - case '.': - break; + case '.': + break; - case '~': - hasalpha = 1; - break; + case '~': + hasalpha = 1; + break; #endif - default: - if (IS_WIN32 || ISALPHA(c)) { - hasalpha = 1; - } - break; - } + default: + if (IS_WIN32 || ISALPHA(c)) { + hasalpha = 1; + } + break; + } - p = Next(p-1, pend, enc); + p = Next(p-1, pend, enc); } return hasmagical ? MAGICAL : hasalpha ? ALPHA : PLAIN; @@ -1596,33 +1884,33 @@ find_dirsep(const char *p, const char *pend, int flags, rb_encoding *enc) int open = 0; while ((c = *p++) != 0) { - switch (c) { - case '[': - open = 1; - continue; - case ']': - open = 0; - continue; - - case '{': - open = 1; - continue; - case '}': - open = 0; - continue; - - case '/': - if (!open) - return (char *)p-1; - continue; - - case '\\': - if (escape && !(c = *p++)) - return (char *)p-1; - continue; - } - - p = Next(p-1, pend, enc); + switch (c) { + case '[': + open = 1; + continue; + case ']': + open = 0; + continue; + + case '{': + open = 1; + continue; + case '}': + open = 0; + continue; + + case '/': + if (!open) + return (char *)p-1; + continue; + + case '\\': + if (escape && !(c = *p++)) + return (char *)p-1; + continue; + } + + p = Next(p-1, pend, enc); } return (char *)p-1; @@ -1636,20 +1924,20 @@ remove_backslashes(char *p, register const char *pend, rb_encoding *enc) char *s = p; while (*p) { - if (*p == '\\') { - if (t != s) - memmove(t, s, p - s); - t += p - s; - s = ++p; - if (!*p) break; - } - Inc(p, pend, enc); + if (*p == '\\') { + if (t != s) + memmove(t, s, p - s); + t += p - s; + s = ++p; + if (!*p) break; + } + Inc(p, pend, enc); } while (*p++); if (t != s) - memmove(t, s, p - s); /* move '\0' too */ + memmove(t, s, p - s); /* move '\0' too */ return p; } @@ -1670,49 +1958,49 @@ glob_make_pattern(const char *p, const char *e, int flags, rb_encoding *enc) int recursive = 0; while (p < e && *p) { - tmp = GLOB_ALLOC(struct glob_pattern); - if (!tmp) goto error; - if (p + 2 < e && p[0] == '*' && p[1] == '*' && p[2] == '/') { - /* fold continuous RECURSIVEs (needed in glob_helper) */ - do { p += 3; while (*p == '/') p++; } while (p[0] == '*' && p[1] == '*' && p[2] == '/'); - tmp->type = RECURSIVE; - tmp->str = 0; - dirsep = 1; - recursive = 1; - } - else { - const char *m = find_dirsep(p, e, flags, enc); - const enum glob_pattern_type magic = has_magic(p, m, flags, enc); - const enum glob_pattern_type non_magic = (USE_NAME_ON_FS || FNM_SYSCASE) ? PLAIN : ALPHA; - char *buf; - - if (!(FNM_SYSCASE || magic > non_magic) && !recursive && *m) { - const char *m2; - while (has_magic(m+1, m2 = find_dirsep(m+1, e, flags, enc), flags, enc) <= non_magic && - *m2) { - m = m2; - } - } - buf = GLOB_ALLOC_N(char, m-p+1); - if (!buf) { - GLOB_FREE(tmp); - goto error; - } - memcpy(buf, p, m-p); - buf[m-p] = '\0'; - tmp->type = magic > MAGICAL ? MAGICAL : magic > non_magic ? magic : PLAIN; - tmp->str = buf; - if (*m) { - dirsep = 1; - p = m + 1; - } - else { - dirsep = 0; - p = m; - } - } - *tail = tmp; - tail = &tmp->next; + tmp = GLOB_ALLOC(struct glob_pattern); + if (!tmp) goto error; + if (p + 2 < e && p[0] == '*' && p[1] == '*' && p[2] == '/') { + /* fold continuous RECURSIVEs (needed in glob_helper) */ + do { p += 3; while (*p == '/') p++; } while (p[0] == '*' && p[1] == '*' && p[2] == '/'); + tmp->type = RECURSIVE; + tmp->str = 0; + dirsep = 1; + recursive = 1; + } + else { + const char *m = find_dirsep(p, e, flags, enc); + const enum glob_pattern_type magic = has_magic(p, m, flags, enc); + const enum glob_pattern_type non_magic = (USE_NAME_ON_FS || FNM_SYSCASE) ? PLAIN : ALPHA; + char *buf; + + if (!(FNM_SYSCASE || magic > non_magic) && !recursive && *m) { + const char *m2; + while (has_magic(m+1, m2 = find_dirsep(m+1, e, flags, enc), flags, enc) <= non_magic && + *m2) { + m = m2; + } + } + buf = GLOB_ALLOC_N(char, m-p+1); + if (!buf) { + GLOB_FREE(tmp); + goto error; + } + memcpy(buf, p, m-p); + buf[m-p] = '\0'; + tmp->type = magic > MAGICAL ? MAGICAL : magic > non_magic ? magic : PLAIN; + tmp->str = buf; + if (*m) { + dirsep = 1; + p = m + 1; + } + else { + dirsep = 0; + p = m; + } + } + *tail = tmp; + tail = &tmp->next; } tmp = GLOB_ALLOC(struct glob_pattern); @@ -1736,11 +2024,11 @@ static void glob_free_pattern(struct glob_pattern *list) { while (list) { - struct glob_pattern *tmp = list; - list = list->next; - if (tmp->str) - GLOB_FREE(tmp->str); - GLOB_FREE(tmp); + struct glob_pattern *tmp = list; + list = list->next; + if (tmp->str) + GLOB_FREE(tmp->str); + GLOB_FREE(tmp); } } @@ -1752,7 +2040,7 @@ join_path(const char *path, size_t len, int dirsep, const char *name, size_t nam if (!buf) return 0; memcpy(buf, path, len); if (dirsep) { - buf[len++] = '/'; + buf[len++] = '/'; } memcpy(buf+len, name, namlen); buf[len+namlen] = '\0'; @@ -1769,8 +2057,8 @@ static int is_case_sensitive(DIR *dirp, const char *path) { struct { - u_int32_t length; - vol_capabilities_attr_t cap[1]; + u_int32_t length; + vol_capabilities_attr_t cap[1]; } __attribute__((aligned(4), packed)) attrbuf[1]; struct attrlist al = {ATTR_BIT_MAP_COUNT, 0, 0, ATTR_VOL_INFO|ATTR_VOL_CAPABILITIES}; const vol_capabilities_attr_t *const cap = attrbuf[0].cap; @@ -1779,13 +2067,13 @@ is_case_sensitive(DIR *dirp, const char *path) # if defined HAVE_FGETATTRLIST if (fgetattrlist(dirfd(dirp), &al, attrbuf, sizeof(attrbuf), FSOPT_NOFOLLOW)) - return -1; + return -1; # else if (getattrlist(path, &al, attrbuf, sizeof(attrbuf), FSOPT_NOFOLLOW)) - return -1; + return -1; # endif if (!(cap->valid[idx] & mask)) - return -1; + return -1; return (cap->capabilities[idx] & mask) != 0; } @@ -1793,10 +2081,10 @@ static char * replace_real_basename(char *path, long base, rb_encoding *enc, int norm_p, int flags, rb_pathtype_t *type) { struct { - u_int32_t length; - attrreference_t ref[1]; - fsobj_type_t objtype; - char path[MAXPATHLEN * 3]; + u_int32_t length; + attrreference_t ref[1]; + fsobj_type_t objtype; + char path[MAXPATHLEN * 3]; } __attribute__((aligned(4), packed)) attrbuf[1]; struct attrlist al = {ATTR_BIT_MAP_COUNT, 0, ATTR_CMN_NAME|ATTR_CMN_OBJTYPE}; const attrreference_t *const ar = attrbuf[0].ref; @@ -1807,9 +2095,9 @@ replace_real_basename(char *path, long base, rb_encoding *enc, int norm_p, int f *type = path_noent; if (getattrlist(path, &al, attrbuf, sizeof(attrbuf), FSOPT_NOFOLLOW)) { - if (!to_be_ignored(errno)) - sys_warning(path, enc); - return path; + if (!to_be_ignored(errno)) + sys_warning(path, enc); + return path; } switch (attrbuf[0].objtype) { @@ -1821,21 +2109,21 @@ replace_real_basename(char *path, long base, rb_encoding *enc, int norm_p, int f name = (char *)ar + ar->attr_dataoffset; len = (long)ar->attr_length - 1; if (name + len > (char *)attrbuf + sizeof(attrbuf)) - return path; + return path; # if NORMALIZE_UTF8PATH if (norm_p && has_nonascii(name, len)) { - if (!NIL_P(utf8str = rb_str_normalize_ospath(name, len))) { - RSTRING_GETMEM(utf8str, name, len); - } + if (!NIL_P(utf8str = rb_str_normalize_ospath(name, len))) { + RSTRING_GETMEM(utf8str, name, len); + } } # endif tmp = GLOB_REALLOC(path, base + len + 1); if (tmp) { - path = tmp; - memcpy(path + base, name, len); - path[base + len] = '\0'; + path = tmp; + memcpy(path + base, name, len); + path[base + len] = '\0'; } IF_NORMALIZE_UTF8PATH(if (!NIL_P(utf8str)) rb_str_resize(utf8str, 0)); return path; @@ -1856,62 +2144,62 @@ replace_real_basename(char *path, long base, rb_encoding *enc, int norm_p, int f long wlen; int e = 0; if (!fundamental_encoding_p(enc)) { - tmp = rb_enc_str_new_cstr(plainname, enc); - tmp = rb_str_encode_ospath(tmp); - plainname = RSTRING_PTR(tmp); + tmp = rb_enc_str_new_cstr(plainname, enc); + tmp = rb_str_encode_ospath(tmp); + plainname = RSTRING_PTR(tmp); } wplain = rb_w32_mbstr_to_wstr(CP_UTF8, plainname, -1, &wlen); if (tmp) rb_str_resize(tmp, 0); if (!wplain) return path; if (GetFileAttributesExW(wplain, GetFileExInfoStandard, &fa)) { - h = FindFirstFileW(wplain, &fd); - e = rb_w32_map_errno(GetLastError()); + h = FindFirstFileW(wplain, &fd); + e = rb_w32_map_errno(GetLastError()); } if (fa.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) { - if (!rb_w32_reparse_symlink_p(wplain)) - fa.dwFileAttributes &= ~FILE_ATTRIBUTE_REPARSE_POINT; + if (!rb_w32_reparse_symlink_p(wplain)) + fa.dwFileAttributes &= ~FILE_ATTRIBUTE_REPARSE_POINT; } free(wplain); if (h == INVALID_HANDLE_VALUE) { - *type = path_noent; - if (e && !to_be_ignored(e)) { - errno = e; - sys_warning(path, enc); - } - return path; + *type = path_noent; + if (e && !to_be_ignored(e)) { + errno = e; + sys_warning(path, enc); + } + return path; } FindClose(h); *type = - (fa.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) ? path_symlink : - (fa.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? path_directory : - path_regular; + (fa.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) ? path_symlink : + (fa.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? path_directory : + path_regular; if (tmp) { - char *buf; - tmp = rb_w32_conv_from_wchar(fd.cFileName, enc); - wlen = RSTRING_LEN(tmp); - buf = GLOB_REALLOC(path, base + wlen + 1); - if (buf) { - path = buf; - memcpy(path + base, RSTRING_PTR(tmp), wlen); - path[base + wlen] = 0; - } - rb_str_resize(tmp, 0); + char *buf; + tmp = rb_w32_conv_from_wchar(fd.cFileName, enc); + wlen = RSTRING_LEN(tmp); + buf = GLOB_REALLOC(path, base + wlen + 1); + if (buf) { + path = buf; + memcpy(path + base, RSTRING_PTR(tmp), wlen); + path[base + wlen] = 0; + } + rb_str_resize(tmp, 0); } else { - char *utf8filename; - wlen = WideCharToMultiByte(CP_UTF8, 0, fd.cFileName, -1, NULL, 0, NULL, NULL); - utf8filename = GLOB_REALLOC(0, wlen); - if (utf8filename) { - char *buf; - WideCharToMultiByte(CP_UTF8, 0, fd.cFileName, -1, utf8filename, wlen, NULL, NULL); - buf = GLOB_REALLOC(path, base + wlen + 1); - if (buf) { - path = buf; - memcpy(path + base, utf8filename, wlen); - path[base + wlen] = 0; - } - GLOB_FREE(utf8filename); - } + char *utf8filename; + wlen = WideCharToMultiByte(CP_UTF8, 0, fd.cFileName, -1, NULL, 0, NULL, NULL); + utf8filename = GLOB_REALLOC(0, wlen); + if (utf8filename) { + char *buf; + WideCharToMultiByte(CP_UTF8, 0, fd.cFileName, -1, utf8filename, wlen, NULL, NULL); + buf = GLOB_REALLOC(path, base + wlen + 1); + if (buf) { + path = buf; + memcpy(path + base, utf8filename, wlen); + path[base + wlen] = 0; + } + GLOB_FREE(utf8filename); + } } return path; } @@ -2003,7 +2291,7 @@ rb_glob_error(const char *path, VALUE a, const void *enc, int error) #ifdef ENOTCAPABLE case ENOTCAPABLE: #endif - errfunc = glob_func_warning; + errfunc = glob_func_warning; } args.path = path; args.enc = enc; @@ -2027,7 +2315,7 @@ dirent_match(const char *pat, rb_encoding *enc, const char *name, const rb_diren if (fnmatch(pat, enc, name, flags) == 0) return 1; #ifdef _WIN32 if (dp->d_altname && (flags & FNM_SHORTNAME)) { - if (fnmatch(pat, enc, dp->d_altname, flags) == 0) return 1; + if (fnmatch(pat, enc, dp->d_altname, flags) == 0) return 1; } #endif return 0; @@ -2068,39 +2356,39 @@ join_path_from_pattern(struct glob_pattern **beg) size_t path_len = 0; for (p = *beg; p; p = p->next) { - const char *str; - switch (p->type) { - case RECURSIVE: - str = "**"; - break; - case MATCH_DIR: - /* append last slash */ - str = ""; - break; - default: - str = p->str; - if (!str) continue; - } - if (!path) { - path_len = strlen(str); - path = GLOB_ALLOC_N(char, path_len + 1); + const char *str; + switch (p->type) { + case RECURSIVE: + str = "**"; + break; + case MATCH_DIR: + /* append last slash */ + str = ""; + break; + default: + str = p->str; + if (!str) continue; + } + if (!path) { + path_len = strlen(str); + path = GLOB_ALLOC_N(char, path_len + 1); if (path) { memcpy(path, str, path_len); path[path_len] = '\0'; } } else { - size_t len = strlen(str); - char *tmp; - tmp = GLOB_REALLOC(path, path_len + len + 2); - if (tmp) { - path = tmp; - path[path_len++] = '/'; - memcpy(path + path_len, str, len); - path_len += len; - path[path_len] = '\0'; - } - } + size_t len = strlen(str); + char *tmp; + tmp = GLOB_REALLOC(path, path_len + len + 2); + if (tmp) { + path = tmp; + path[path_len++] = '/'; + memcpy(path + path_len, str, len); + path_len += len; + path[path_len] = '\0'; + } + } } return path; } @@ -2108,7 +2396,7 @@ join_path_from_pattern(struct glob_pattern **beg) static int push_caller(const char *path, VALUE val, void *enc); static int ruby_brace_expand(const char *str, int flags, ruby_glob_func *func, VALUE arg, - rb_encoding *enc, VALUE var); + rb_encoding *enc, VALUE var); static const size_t rb_dirent_name_offset = offsetof(rb_dirent_t, d_type) + sizeof(uint8_t); @@ -2216,7 +2504,7 @@ glob_opendir(ruby_glob_entries_t *ent, DIR *dirp, int flags, rb_encoding *enc) ent->sort.entries = newp; } #endif - while ((dp = READDIR(dirp, enc)) != NULL) { + while ((dp = READDIR(dirp, enc)) != NULL) { rb_dirent_t *rdp = dirent_copy(dp, NULL); if (!rdp) { goto nomem; @@ -2288,133 +2576,133 @@ glob_helper( rb_check_stack_overflow(); for (cur = beg; cur < end; ++cur) { - struct glob_pattern *p = *cur; - if (p->type == RECURSIVE) { - recursive = 1; - p = p->next; - } - switch (p->type) { - case PLAIN: - plain = 1; - break; - case ALPHA: + struct glob_pattern *p = *cur; + if (p->type == RECURSIVE) { + recursive = 1; + p = p->next; + } + switch (p->type) { + case PLAIN: + plain = 1; + break; + case ALPHA: #if USE_NAME_ON_FS == USE_NAME_ON_FS_REAL_BASENAME - plain = 1; + plain = 1; #else - magical = 1; -#endif - break; - case BRACE: - if (!recursive) { - brace = 1; - } - break; - case MAGICAL: - magical = 2; - break; - case MATCH_ALL: - match_all = 1; - break; - case MATCH_DIR: - match_dir = 1; - break; - case RECURSIVE: - rb_bug("continuous RECURSIVEs"); - } + magical = 1; +#endif + break; + case BRACE: + if (!recursive || strchr(p->str, '/')) { + brace = 1; + } + break; + case MAGICAL: + magical = 2; + break; + case MATCH_ALL: + match_all = 1; + break; + case MATCH_DIR: + match_dir = 1; + break; + case RECURSIVE: + rb_bug("continuous RECURSIVEs"); + } } if (brace) { - struct push_glob_args args; - char* brace_path = join_path_from_pattern(beg); - if (!brace_path) return -1; - args.fd = fd; - args.path = path; - args.baselen = baselen; - args.namelen = namelen; - args.dirsep = dirsep; - args.pathtype = pathtype; - args.flags = flags; - args.funcs = funcs; - args.arg = arg; - status = ruby_brace_expand(brace_path, flags, push_caller, (VALUE)&args, enc, Qfalse); - GLOB_FREE(brace_path); - return status; + struct push_glob_args args; + char* brace_path = join_path_from_pattern(beg); + if (!brace_path) return -1; + args.fd = fd; + args.path = path; + args.baselen = baselen; + args.namelen = namelen; + args.dirsep = dirsep; + args.pathtype = pathtype; + args.flags = flags; + args.funcs = funcs; + args.arg = arg; + status = ruby_brace_expand(brace_path, flags, push_caller, (VALUE)&args, enc, Qfalse); + GLOB_FREE(brace_path); + return status; } if (*path) { - if (match_all && pathtype == path_unknown) { - if (do_lstat(fd, baselen, path, &st, flags, enc) == 0) { - pathtype = IFTODT(st.st_mode); - } - else { - pathtype = path_noent; - } - } - if (match_dir && (pathtype == path_unknown || pathtype == path_symlink)) { - if (do_stat(fd, baselen, path, &st, flags, enc) == 0) { - pathtype = IFTODT(st.st_mode); - } - else { - pathtype = path_noent; - } - } - if (match_all && pathtype > path_noent) { - const char *subpath = path + baselen + (baselen && path[baselen] == '/'); - status = glob_call_func(funcs->match, subpath, arg, enc); - if (status) return status; - } - if (match_dir && pathtype == path_directory) { - int seplen = (baselen && path[baselen] == '/'); - const char *subpath = path + baselen + seplen; - char *tmp = join_path(subpath, namelen - seplen, dirsep, "", 0); - if (!tmp) return -1; - status = glob_call_func(funcs->match, tmp, arg, enc); - GLOB_FREE(tmp); - if (status) return status; - } + if (match_all && pathtype == path_unknown) { + if (do_lstat(fd, baselen, path, &st, flags, enc) == 0) { + pathtype = IFTODT(st.st_mode); + } + else { + pathtype = path_noent; + } + } + if (match_dir && (pathtype == path_unknown || pathtype == path_symlink)) { + if (do_stat(fd, baselen, path, &st, flags, enc) == 0) { + pathtype = IFTODT(st.st_mode); + } + else { + pathtype = path_noent; + } + } + if (match_all && pathtype > path_noent) { + const char *subpath = path + baselen + (baselen && path[baselen] == '/'); + status = glob_call_func(funcs->match, subpath, arg, enc); + if (status) return status; + } + if (match_dir && pathtype == path_directory) { + int seplen = (baselen && path[baselen] == '/'); + const char *subpath = path + baselen + seplen; + char *tmp = join_path(subpath, namelen - seplen, dirsep, "", 0); + if (!tmp) return -1; + status = glob_call_func(funcs->match, tmp, arg, enc); + GLOB_FREE(tmp); + if (status) return status; + } } if (pathtype == path_noent) return 0; if (magical || recursive) { - rb_dirent_t *dp; - DIR *dirp; + rb_dirent_t *dp; + DIR *dirp; # if USE_NAME_ON_FS == USE_NAME_ON_FS_BY_FNMATCH - char *plainname = 0; + char *plainname = 0; # endif - IF_NORMALIZE_UTF8PATH(int norm_p); + IF_NORMALIZE_UTF8PATH(int norm_p); # if USE_NAME_ON_FS == USE_NAME_ON_FS_BY_FNMATCH - if (cur + 1 == end && (*cur)->type <= ALPHA) { - plainname = join_path(path, pathlen, dirsep, (*cur)->str, strlen((*cur)->str)); - if (!plainname) return -1; - dirp = do_opendir(fd, basename, plainname, flags, enc, funcs->error, arg, &status); - GLOB_FREE(plainname); - } - else + if (cur + 1 == end && (*cur)->type <= ALPHA) { + plainname = join_path(path, pathlen, dirsep, (*cur)->str, strlen((*cur)->str)); + if (!plainname) return -1; + dirp = do_opendir(fd, basename, plainname, flags, enc, funcs->error, arg, &status); + GLOB_FREE(plainname); + } + else # else - ; + ; # endif - dirp = do_opendir(fd, baselen, path, flags, enc, funcs->error, arg, &status); - if (dirp == NULL) { + dirp = do_opendir(fd, baselen, path, flags, enc, funcs->error, arg, &status); + if (dirp == NULL) { # if FNM_SYSCASE || NORMALIZE_UTF8PATH - if ((magical < 2) && !recursive && (errno == EACCES)) { - /* no read permission, fallback */ - goto literally; - } + if ((magical < 2) && !recursive && (errno == EACCES)) { + /* no read permission, fallback */ + goto literally; + } # endif - return status; - } - IF_NORMALIZE_UTF8PATH(norm_p = need_normalization(dirp, *path ? path : ".")); + return status; + } + IF_NORMALIZE_UTF8PATH(norm_p = need_normalization(dirp, *path ? path : ".")); # if NORMALIZE_UTF8PATH - if (!(norm_p || magical || recursive)) { - closedir(dirp); - goto literally; - } + if (!(norm_p || magical || recursive)) { + closedir(dirp); + goto literally; + } # endif # ifdef HAVE_GETATTRLIST - if (is_case_sensitive(dirp, path) == 0) - flags |= FNM_CASEFOLD; + if (is_case_sensitive(dirp, path) == 0) + flags |= FNM_CASEFOLD; # endif ruby_glob_entries_t globent; if (!glob_opendir(&globent, dirp, flags, enc)) { @@ -2428,182 +2716,182 @@ glob_helper( return status; } - int skipdot = (flags & FNM_GLOB_SKIPDOT); - flags |= FNM_GLOB_SKIPDOT; - - while ((dp = glob_getent(&globent, flags, enc)) != NULL) { - char *buf; - rb_pathtype_t new_pathtype = path_unknown; - const char *name; - size_t namlen; - int dotfile = 0; - IF_NORMALIZE_UTF8PATH(VALUE utf8str = Qnil); - - name = dp->d_name; - namlen = dp->d_namlen; - if (name[0] == '.') { - ++dotfile; - if (namlen == 1) { - /* unless DOTMATCH, skip current directories not to recurse infinitely */ - if (recursive && !(flags & FNM_DOTMATCH)) continue; - if (skipdot) continue; - ++dotfile; - new_pathtype = path_directory; /* force to skip stat/lstat */ - } - else if (namlen == 2 && name[1] == '.') { - /* always skip parent directories not to recurse infinitely */ - continue; - } - } + int skipdot = (flags & FNM_GLOB_SKIPDOT); + flags |= FNM_GLOB_SKIPDOT; + + while ((dp = glob_getent(&globent, flags, enc)) != NULL) { + char *buf; + rb_pathtype_t new_pathtype = path_unknown; + const char *name; + size_t namlen; + int dotfile = 0; + IF_NORMALIZE_UTF8PATH(VALUE utf8str = Qnil); + + name = dp->d_name; + namlen = dp->d_namlen; + if (name[0] == '.') { + ++dotfile; + if (namlen == 1) { + /* unless DOTMATCH, skip current directories not to recurse infinitely */ + if (recursive && !(flags & FNM_DOTMATCH)) continue; + if (skipdot) continue; + ++dotfile; + new_pathtype = path_directory; /* force to skip stat/lstat */ + } + else if (namlen == 2 && name[1] == '.') { + /* always skip parent directories not to recurse infinitely */ + continue; + } + } # if NORMALIZE_UTF8PATH - if (norm_p && has_nonascii(name, namlen)) { - if (!NIL_P(utf8str = rb_str_normalize_ospath(name, namlen))) { - RSTRING_GETMEM(utf8str, name, namlen); - } - } + if (norm_p && has_nonascii(name, namlen)) { + if (!NIL_P(utf8str = rb_str_normalize_ospath(name, namlen))) { + RSTRING_GETMEM(utf8str, name, namlen); + } + } # endif - buf = join_path(path, pathlen, dirsep, name, namlen); - IF_NORMALIZE_UTF8PATH(if (!NIL_P(utf8str)) rb_str_resize(utf8str, 0)); - if (!buf) { - status = -1; - break; - } - name = buf + pathlen + (dirsep != 0); + buf = join_path(path, pathlen, dirsep, name, namlen); + IF_NORMALIZE_UTF8PATH(if (!NIL_P(utf8str)) rb_str_resize(utf8str, 0)); + if (!buf) { + status = -1; + break; + } + name = buf + pathlen + (dirsep != 0); #if !EMULATE_IFTODT - if (dp->d_type != DT_UNKNOWN) { - /* Got it. We need no more lstat. */ - new_pathtype = dp->d_type; - } -#endif - 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) - new_pathtype = IFTODT(st.st_mode); - else - new_pathtype = path_noent; - } - - new_beg = new_end = GLOB_ALLOC_N(struct glob_pattern *, (end - beg) * 2); - if (!new_beg) { - GLOB_FREE(buf); - status = -1; - break; - } - - for (cur = beg; cur < end; ++cur) { - struct glob_pattern *p = *cur; - struct dirent_brace_args args; - if (p->type == RECURSIVE) { - if (new_pathtype == path_directory || /* not symlink but real directory */ - new_pathtype == path_exist) { - if (dotfile < ((flags & FNM_DOTMATCH) ? 2 : 1)) - *new_end++ = p; /* append recursive pattern */ - } - p = p->next; /* 0 times recursion */ - } - switch (p->type) { - case BRACE: - args.name = name; - args.dp = dp; - args.flags = flags; - if (ruby_brace_expand(p->str, flags, dirent_match_brace, - (VALUE)&args, enc, Qfalse) > 0) - *new_end++ = p->next; - break; - case ALPHA: + if (dp->d_type != DT_UNKNOWN) { + /* Got it. We need no more lstat. */ + new_pathtype = dp->d_type; + } +#endif + 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) + new_pathtype = IFTODT(st.st_mode); + else + new_pathtype = path_noent; + } + + new_beg = new_end = GLOB_ALLOC_N(struct glob_pattern *, (end - beg) * 2); + if (!new_beg) { + GLOB_FREE(buf); + status = -1; + break; + } + + for (cur = beg; cur < end; ++cur) { + struct glob_pattern *p = *cur; + struct dirent_brace_args args; + if (p->type == RECURSIVE) { + if (new_pathtype == path_directory || /* not symlink but real directory */ + new_pathtype == path_exist) { + if (dotfile < ((flags & FNM_DOTMATCH) ? 2 : 1)) + *new_end++ = p; /* append recursive pattern */ + } + p = p->next; /* 0 times recursion */ + } + switch (p->type) { + case BRACE: + args.name = name; + args.dp = dp; + args.flags = flags; + if (ruby_brace_expand(p->str, flags, dirent_match_brace, + (VALUE)&args, enc, Qfalse) > 0) + *new_end++ = p->next; + break; + case ALPHA: # if USE_NAME_ON_FS == USE_NAME_ON_FS_BY_FNMATCH - if (plainname) { - *new_end++ = p->next; - break; - } + if (plainname) { + *new_end++ = p->next; + break; + } # endif - case PLAIN: - case MAGICAL: - if (dirent_match(p->str, enc, name, dp, flags)) - *new_end++ = p->next; - default: - break; - } - } - - status = glob_helper(fd, buf, baselen, name - buf - baselen + namlen, 1, - new_pathtype, new_beg, new_end, - flags, funcs, arg, enc); - GLOB_FREE(buf); - GLOB_FREE(new_beg); - if (status) break; - } + case PLAIN: + case MAGICAL: + if (dirent_match(p->str, enc, name, dp, flags)) + *new_end++ = p->next; + default: + break; + } + } + + status = glob_helper(fd, buf, baselen, name - buf - baselen + namlen, 1, + new_pathtype, new_beg, new_end, + flags, funcs, arg, enc); + GLOB_FREE(buf); + GLOB_FREE(new_beg); + if (status) break; + } glob_dir_finish(&globent, flags); } else if (plain) { - struct glob_pattern **copy_beg, **copy_end, **cur2; + struct glob_pattern **copy_beg, **copy_end, **cur2; # if FNM_SYSCASE || NORMALIZE_UTF8PATH literally: # endif - copy_beg = copy_end = GLOB_ALLOC_N(struct glob_pattern *, end - beg); - if (!copy_beg) return -1; - for (cur = beg; cur < end; ++cur) - *copy_end++ = (*cur)->type <= ALPHA ? *cur : 0; - - for (cur = copy_beg; cur < copy_end; ++cur) { - if (*cur) { - rb_pathtype_t new_pathtype = path_unknown; - char *buf; - char *name; - size_t len = strlen((*cur)->str) + 1; - name = GLOB_ALLOC_N(char, len); - if (!name) { - status = -1; - break; - } - memcpy(name, (*cur)->str, len); - if (escape) - len = remove_backslashes(name, name+len-1, enc) - name; - - new_beg = new_end = GLOB_ALLOC_N(struct glob_pattern *, end - beg); - if (!new_beg) { - GLOB_FREE(name); - status = -1; - break; - } - *new_end++ = (*cur)->next; - for (cur2 = cur + 1; cur2 < copy_end; ++cur2) { - if (*cur2 && fnmatch((*cur2)->str, enc, name, flags) == 0) { - *new_end++ = (*cur2)->next; - *cur2 = 0; - } - } - - buf = join_path(path, pathlen, dirsep, name, len); - GLOB_FREE(name); - if (!buf) { - GLOB_FREE(new_beg); - status = -1; - break; - } + copy_beg = copy_end = GLOB_ALLOC_N(struct glob_pattern *, end - beg); + if (!copy_beg) return -1; + for (cur = beg; cur < end; ++cur) + *copy_end++ = (*cur)->type <= ALPHA ? *cur : 0; + + for (cur = copy_beg; cur < copy_end; ++cur) { + if (*cur) { + rb_pathtype_t new_pathtype = path_unknown; + char *buf; + char *name; + size_t len = strlen((*cur)->str) + 1; + name = GLOB_ALLOC_N(char, len); + if (!name) { + status = -1; + break; + } + memcpy(name, (*cur)->str, len); + if (escape) + len = remove_backslashes(name, name+len-1, enc) - name; + + new_beg = new_end = GLOB_ALLOC_N(struct glob_pattern *, end - beg); + if (!new_beg) { + GLOB_FREE(name); + status = -1; + break; + } + *new_end++ = (*cur)->next; + for (cur2 = cur + 1; cur2 < copy_end; ++cur2) { + if (*cur2 && fnmatch((*cur2)->str, enc, name, flags) == 0) { + *new_end++ = (*cur2)->next; + *cur2 = 0; + } + } + + buf = join_path(path, pathlen, dirsep, name, len); + GLOB_FREE(name); + if (!buf) { + GLOB_FREE(new_beg); + status = -1; + break; + } #if USE_NAME_ON_FS == USE_NAME_ON_FS_REAL_BASENAME - if ((*cur)->type == ALPHA) { - buf = replace_real_basename(buf, pathlen + (dirsep != 0), enc, - IF_NORMALIZE_UTF8PATH(1)+0, - flags, &new_pathtype); - if (!buf) break; - } -#endif - status = glob_helper(fd, buf, baselen, - namelen + strlen(buf + pathlen), 1, - new_pathtype, new_beg, new_end, - flags, funcs, arg, enc); - GLOB_FREE(buf); - GLOB_FREE(new_beg); - if (status) break; - } - } - - GLOB_FREE(copy_beg); + if ((*cur)->type == ALPHA) { + buf = replace_real_basename(buf, pathlen + (dirsep != 0), enc, + IF_NORMALIZE_UTF8PATH(1)+0, + flags, &new_pathtype); + if (!buf) break; + } +#endif + status = glob_helper(fd, buf, baselen, + namelen + strlen(buf + pathlen), 1, + new_pathtype, new_beg, new_end, + flags, funcs, arg, enc); + GLOB_FREE(buf); + GLOB_FREE(new_beg); + if (status) break; + } + } + + GLOB_FREE(copy_beg); } return status; @@ -2618,11 +2906,11 @@ push_caller(const char *path, VALUE val, void *enc) list = glob_make_pattern(path, path + strlen(path), arg->flags, enc); if (!list) { - return -1; + return -1; } status = glob_helper(arg->fd, arg->path, arg->baselen, arg->namelen, arg->dirsep, - arg->pathtype, &list, &list + 1, arg->flags, arg->funcs, - arg->arg, enc); + arg->pathtype, &list, &list + 1, arg->flags, arg->funcs, + arg->arg, enc); glob_free_pattern(list); return status; } @@ -2647,8 +2935,8 @@ push_glob0_caller(const char *path, VALUE val, void *enc) static int ruby_glob0(const char *path, int fd, const char *base, int flags, - const ruby_glob_funcs_t *funcs, VALUE arg, - rb_encoding *enc) + const ruby_glob_funcs_t *funcs, VALUE arg, + rb_encoding *enc) { struct glob_pattern *list; const char *root, *start; @@ -2677,10 +2965,10 @@ ruby_glob0(const char *path, int fd, const char *base, int flags, n = root - start; if (!n && base) { - n = strlen(base); - baselen = n; - start = base; - dirsep = TRUE; + n = strlen(base); + baselen = n; + start = base; + dirsep = TRUE; } buf = GLOB_ALLOC_N(char, n + 1); if (!buf) return -1; @@ -2689,12 +2977,12 @@ ruby_glob0(const char *path, int fd, const char *base, int flags, list = glob_make_pattern(root, root + strlen(root), flags, enc); if (!list) { - GLOB_FREE(buf); - return -1; + GLOB_FREE(buf); + return -1; } status = glob_helper(fd, buf, baselen, n-baselen, dirsep, - path_unknown, &list, &list + 1, - flags, funcs, arg, enc); + path_unknown, &list, &list + 1, + flags, funcs, arg, enc); glob_free_pattern(list); GLOB_FREE(buf); @@ -2708,7 +2996,7 @@ ruby_glob(const char *path, int flags, ruby_glob_func *func, VALUE arg) funcs.match = func; funcs.error = 0; return ruby_glob0(path, AT_FDCWD, 0, flags & ~GLOB_VERBOSE, - &funcs, arg, rb_ascii8bit_encoding()); + &funcs, arg, rb_ascii8bit_encoding()); } static int @@ -2737,7 +3025,7 @@ rb_glob(const char *path, void (*func)(const char *, VALUE, void *), VALUE arg) args.enc = rb_ascii8bit_encoding(); status = ruby_glob0(path, AT_FDCWD, 0, GLOB_VERBOSE, &rb_glob_funcs, - (VALUE)&args, args.enc); + (VALUE)&args, args.enc); if (status) GLOB_JUMP_TAG(status); } @@ -2756,7 +3044,7 @@ push_pattern(const char *path, VALUE ary, void *enc) static int ruby_brace_expand(const char *str, int flags, ruby_glob_func *func, VALUE arg, - rb_encoding *enc, VALUE var) + rb_encoding *enc, VALUE var) { const int escape = !(flags & FNM_NOESCAPE); const char *p = str; @@ -2766,48 +3054,48 @@ ruby_brace_expand(const char *str, int flags, ruby_glob_func *func, VALUE arg, int nest = 0, status = 0; while (*p) { - if (*p == '{' && nest++ == 0) { - lbrace = p; - } - if (*p == '}' && lbrace && --nest == 0) { - rbrace = p; - break; - } - if (*p == '\\' && escape) { - if (!*++p) break; - } - Inc(p, pend, enc); + if (*p == '{' && nest++ == 0) { + lbrace = p; + } + if (*p == '}' && lbrace && --nest == 0) { + rbrace = p; + break; + } + if (*p == '\\' && escape) { + if (!*++p) break; + } + Inc(p, pend, enc); } if (lbrace && rbrace) { - size_t len = strlen(s) + 1; - char *buf = GLOB_ALLOC_N(char, len); - long shift; - - if (!buf) return -1; - memcpy(buf, s, lbrace-s); - shift = (lbrace-s); - p = lbrace; - while (p < rbrace) { - const char *t = ++p; - nest = 0; - while (p < rbrace && !(*p == ',' && nest == 0)) { - if (*p == '{') nest++; - if (*p == '}') nest--; - if (*p == '\\' && escape) { - if (++p == rbrace) break; - } - Inc(p, pend, enc); - } - memcpy(buf+shift, t, p-t); - strlcpy(buf+shift+(p-t), rbrace+1, len-(shift+(p-t))); - status = ruby_brace_expand(buf, flags, func, arg, enc, var); - if (status) break; - } - GLOB_FREE(buf); + size_t len = strlen(s) + 1; + char *buf = GLOB_ALLOC_N(char, len); + long shift; + + if (!buf) return -1; + memcpy(buf, s, lbrace-s); + shift = (lbrace-s); + p = lbrace; + while (p < rbrace) { + const char *t = ++p; + nest = 0; + while (p < rbrace && !(*p == ',' && nest == 0)) { + if (*p == '{') nest++; + if (*p == '}') nest--; + if (*p == '\\' && escape) { + if (++p == rbrace) break; + } + Inc(p, pend, enc); + } + memcpy(buf+shift, t, p-t); + strlcpy(buf+shift+(p-t), rbrace+1, len-(shift+(p-t))); + status = ruby_brace_expand(buf, flags, func, arg, enc, var); + if (status) break; + } + GLOB_FREE(buf); } else if (!lbrace && !rbrace) { - status = glob_call_func(func, s, arg, enc); + status = glob_call_func(func, s, arg, enc); } RB_GC_GUARD(var); @@ -2858,9 +3146,9 @@ push_glob(VALUE ary, VALUE str, VALUE base, int flags) str = rb_str_encode_ospath(str); #endif if (rb_enc_to_index(enc) == ENCINDEX_US_ASCII) - enc = rb_filesystem_encoding(); + enc = rb_filesystem_encoding(); if (rb_enc_to_index(enc) == ENCINDEX_US_ASCII) - enc = rb_ascii8bit_encoding(); + enc = rb_ascii8bit_encoding(); flags |= GLOB_VERBOSE; args.func = push_pattern; args.value = ary; @@ -2868,23 +3156,23 @@ push_glob(VALUE ary, VALUE str, VALUE base, int flags) args.base = 0; fd = AT_FDCWD; if (!NIL_P(base)) { - if (!RB_TYPE_P(base, T_STRING) || !rb_enc_check(str, base)) { - struct dir_data *dirp = DATA_PTR(base); - if (!dirp->dir) dir_closed(); + if (!RB_TYPE_P(base, T_STRING) || !rb_enc_check(str, base)) { + struct dir_data *dirp = RTYPEDDATA_GET_DATA(base); + if (!dirp->dir) dir_closed(); #ifdef HAVE_DIRFD - if ((fd = dirfd(dirp->dir)) == -1) - rb_sys_fail_path(dir_inspect(base)); + if ((fd = dirfd(dirp->dir)) == -1) + rb_sys_fail_path(dir_inspect(base)); #endif - base = dirp->path; - } - args.base = RSTRING_PTR(base); + base = dirp->path; + } + args.base = RSTRING_PTR(base); } #if defined _WIN32 || defined __APPLE__ enc = rb_utf8_encoding(); #endif return ruby_glob0(RSTRING_PTR(str), fd, args.base, flags, &rb_glob_funcs, - (VALUE)&args, enc); + (VALUE)&args, enc); } static VALUE @@ -2895,13 +3183,13 @@ rb_push_glob(VALUE str, VALUE base, int flags) /* '\0' is delimiter */ /* can contain null bytes as separators */ if (!RB_TYPE_P(str, T_STRING)) { - FilePathValue(str); + FilePathValue(str); } else if (!rb_str_to_cstr(str)) { rb_raise(rb_eArgError, "nul-separated glob pattern is deprecated"); } else { - rb_enc_check(str, rb_enc_from_encoding(rb_usascii_encoding())); + rb_enc_check(str, rb_enc_from_encoding(rb_usascii_encoding())); } ary = rb_ary_new(); @@ -2918,11 +3206,11 @@ dir_globs(VALUE args, VALUE base, int flags) long i; for (i = 0; i < RARRAY_LEN(args); ++i) { - int status; - VALUE str = RARRAY_AREF(args, i); - FilePathValue(str); - status = push_glob(ary, str, base, flags); - if (status) GLOB_JUMP_TAG(status); + int status; + VALUE str = RARRAY_AREF(args, i); + FilePathValue(str); + status = push_glob(ary, str, base, flags); + if (status) GLOB_JUMP_TAG(status); } RB_GC_GUARD(args); @@ -2932,12 +3220,12 @@ dir_globs(VALUE args, VALUE base, int flags) static VALUE dir_glob_option_base(VALUE base) { - if (base == Qundef || NIL_P(base)) { - return Qnil; + if (NIL_OR_UNDEF_P(base)) { + return Qnil; } #if USE_OPENDIR_AT if (rb_typeddata_is_kind_of(base, &dir_data_type)) { - return base; + return base; } #endif FilePathValue(base); @@ -2948,7 +3236,7 @@ dir_glob_option_base(VALUE base) static int dir_glob_option_sort(VALUE sort) { - return (rb_bool_expected(sort, "sort") ? 0 : FNM_GLOB_NOSORT); + return (rb_bool_expected(sort, "sort", TRUE) ? 0 : FNM_GLOB_NOSORT); } static VALUE @@ -2957,7 +3245,7 @@ dir_s_aref(rb_execution_context_t *ec, VALUE obj, VALUE args, VALUE base, VALUE const int flags = dir_glob_option_sort(sort); base = dir_glob_option_base(base); if (RARRAY_LEN(args) == 1) { - return rb_push_glob(RARRAY_AREF(args, 0), base, flags); + return rb_push_glob(RARRAY_AREF(args, 0), base, flags); } return dir_globs(args, base, flags); } @@ -2969,15 +3257,15 @@ dir_s_glob(rb_execution_context_t *ec, VALUE obj, VALUE str, VALUE rflags, VALUE const int flags = (NUM2INT(rflags) | dir_glob_option_sort(sort)) & ~FNM_CASEFOLD; base = dir_glob_option_base(base); if (NIL_P(ary)) { - ary = rb_push_glob(str, base, flags); + ary = rb_push_glob(str, base, flags); } else { ary = dir_globs(ary, base, flags); } if (rb_block_given_p()) { - rb_ary_each(ary); - return Qnil; + rb_ary_each(ary); + return Qnil; } return ary; } @@ -2993,26 +3281,35 @@ dir_open_dir(int argc, VALUE *argv) /* - * call-seq: - * Dir.foreach( dirname ) {| filename | block } -> nil - * Dir.foreach( dirname, encoding: enc ) {| filename | block } -> nil - * Dir.foreach( dirname ) -> an_enumerator - * Dir.foreach( dirname, encoding: enc ) -> an_enumerator + * call-seq: + * Dir.foreach(dirpath, encoding: 'UTF-8') {|entry_name| ... } -> nil + * + * Calls the block with each entry name in the directory at +dirpath+; + * sets the given encoding onto each passed +entry_name+: + * + * Dir.foreach('/example') {|entry_name| p entry_name } * - * Calls the block once for each entry in the named directory, passing - * the filename of each entry as a parameter to the block. + * Output: * - * If no block is given, an enumerator is returned instead. + * "config.h" + * "lib" + * "main.rb" + * ".." + * "." * - * Dir.foreach("testdir") {|x| puts "Got #{x}" } + * Encoding: * - * <em>produces:</em> + * Dir.foreach('/example') {|entry_name| p entry_name.encoding; break } + * Dir.foreach('/example', encoding: 'US-ASCII') {|entry_name| p entry_name.encoding; break } * - * Got . - * Got .. - * Got config.h - * Got main.rb + * Output: * + * #<Encoding:UTF-8> + * #<Encoding:US-ASCII> + * + * See {String Encoding}[rdoc-ref:encodings.rdoc@String+Encoding]. + * + * Returns an enumerator if no block is given. */ static VALUE dir_foreach(int argc, VALUE *argv, VALUE io) @@ -3034,19 +3331,21 @@ dir_collect(VALUE dir) } /* - * call-seq: - * Dir.entries( dirname ) -> array - * Dir.entries( dirname, encoding: enc ) -> array + * call-seq: + * Dir.entries(dirname, encoding: 'UTF-8') -> array * - * Returns an array containing all of the filenames in the given - * directory. Will raise a SystemCallError if the named directory - * doesn't exist. + * Returns an array of the entry names in the directory at +dirpath+; + * sets the given encoding onto each returned entry name: * - * The optional <i>encoding</i> keyword argument specifies the encoding of the - * directory. If not specified, the filesystem encoding is used. + * Dir.entries('/example') # => ["config.h", "lib", "main.rb", "..", "."] + * Dir.entries('/example').first.encoding + * # => #<Encoding:UTF-8> + * Dir.entries('/example', encoding: 'US-ASCII').first.encoding + * # => #<Encoding:US-ASCII> * - * Dir.entries("testdir") #=> [".", "..", "config.h", "main.rb"] + * See {String Encoding}[rdoc-ref:encodings.rdoc@String+Encoding]. * + * Raises an exception if the directory does not exist. */ static VALUE dir_entries(int argc, VALUE *argv, VALUE io) @@ -3064,25 +3363,12 @@ dir_each_child(VALUE dir) } /* - * call-seq: - * Dir.each_child( dirname ) {| filename | block } -> nil - * Dir.each_child( dirname, encoding: enc ) {| filename | block } -> nil - * Dir.each_child( dirname ) -> an_enumerator - * Dir.each_child( dirname, encoding: enc ) -> an_enumerator - * - * Calls the block once for each entry except for "." and ".." in the - * named directory, passing the filename of each entry as a parameter - * to the block. - * - * If no block is given, an enumerator is returned instead. - * - * Dir.each_child("testdir") {|x| puts "Got #{x}" } - * - * <em>produces:</em> - * - * Got config.h - * Got main.rb + * call-seq: + * Dir.each_child(dirpath) {|entry_name| ... } -> nil + * Dir.each_child(dirpath, encoding: 'UTF-8') {|entry_name| ... } -> nil * + * Like Dir.foreach, except that entries <tt>'.'</tt> and <tt>'..'</tt> + * are not included. */ static VALUE dir_s_each_child(int argc, VALUE *argv, VALUE io) @@ -3096,24 +3382,22 @@ dir_s_each_child(int argc, VALUE *argv, VALUE io) } /* - * call-seq: - * dir.each_child {| filename | block } -> dir - * dir.each_child -> an_enumerator - * - * Calls the block once for each entry except for "." and ".." in - * this directory, passing the filename of each entry as a parameter - * to the block. + * call-seq: + * each_child {|entry_name| ... } -> self * - * If no block is given, an enumerator is returned instead. + * Calls the block with each entry name in +self+ + * except <tt>'.'</tt> and <tt>'..'</tt>: * - * d = Dir.new("testdir") - * d.each_child {|x| puts "Got #{x}" } + * dir = Dir.new('/example') + * dir.each_child {|entry_name| p entry_name } * - * <em>produces:</em> + * Output: * - * Got config.h - * Got main.rb + * "config.h" + * "lib" + * "main.rb" * + * If no block is given, returns an enumerator. */ static VALUE dir_each_child_m(VALUE dir) @@ -3123,14 +3407,14 @@ dir_each_child_m(VALUE dir) } /* - * call-seq: - * dir.children -> array + * call-seq: + * children -> array * - * Returns an array containing all of the filenames except for "." - * and ".." in this directory. + * Returns an array of the entry names in +self+ + * except for <tt>'.'</tt> and <tt>'..'</tt>: * - * d = Dir.new("testdir") - * d.children #=> ["config.h", "main.rb"] + * dir = Dir.new('/example') + * dir.children # => ["config.h", "lib", "main.rb"] * */ static VALUE @@ -3142,19 +3426,23 @@ dir_collect_children(VALUE dir) } /* - * call-seq: - * Dir.children( dirname ) -> array - * Dir.children( dirname, encoding: enc ) -> array + * call-seq: + * Dir.children(dirpath) -> array + * Dir.children(dirpath, encoding: 'UTF-8') -> array * - * Returns an array containing all of the filenames except for "." - * and ".." in the given directory. Will raise a SystemCallError if - * the named directory doesn't exist. + * Returns an array of the entry names in the directory at +dirpath+ + * except for <tt>'.'</tt> and <tt>'..'</tt>; + * sets the given encoding onto each returned entry name: * - * The optional <i>encoding</i> keyword argument specifies the encoding of the - * directory. If not specified, the filesystem encoding is used. + * Dir.children('/example') # => ["config.h", "lib", "main.rb"] + * Dir.children('/example').first.encoding + * # => #<Encoding:UTF-8> + * Dir.children('/example', encoding: 'US-ASCII').first.encoding + * # => #<Encoding:US-ASCII> * - * Dir.children("testdir") #=> ["config.h", "main.rb"] + * See {String Encoding}[rdoc-ref:encodings.rdoc@String+Encoding]. * + * Raises an exception if the directory does not exist. */ static VALUE dir_s_children(int argc, VALUE *argv, VALUE io) @@ -3174,19 +3462,19 @@ fnmatch_brace(const char *pattern, VALUE val, void *enc) rb_encoding *enc_path = rb_enc_get(path); if (enc_pattern != enc_path) { - if (!rb_enc_asciicompat(enc_pattern)) - return FNM_NOMATCH; - if (!rb_enc_asciicompat(enc_path)) - return FNM_NOMATCH; - if (!rb_enc_str_asciionly_p(path)) { - int cr = ENC_CODERANGE_7BIT; - long len = strlen(pattern); - if (rb_str_coderange_scan_restartable(pattern, pattern + len, - enc_pattern, &cr) != len) - return FNM_NOMATCH; - if (cr != ENC_CODERANGE_7BIT) - return FNM_NOMATCH; - } + if (!rb_enc_asciicompat(enc_pattern)) + return FNM_NOMATCH; + if (!rb_enc_asciicompat(enc_path)) + return FNM_NOMATCH; + if (!rb_enc_str_asciionly_p(path)) { + int cr = ENC_CODERANGE_7BIT; + long len = strlen(pattern); + if (rb_str_coderange_scan_restartable(pattern, pattern + len, + enc_pattern, &cr) != len) + return FNM_NOMATCH; + if (cr != ENC_CODERANGE_7BIT) + return FNM_NOMATCH; + } } return (fnmatch(pattern, enc, RSTRING_PTR(path), arg->flags) == 0); } @@ -3200,27 +3488,27 @@ file_s_fnmatch(int argc, VALUE *argv, VALUE obj) int flags; if (rb_scan_args(argc, argv, "21", &pattern, &path, &rflags) == 3) - flags = NUM2INT(rflags); + flags = NUM2INT(rflags); else - flags = 0; + flags = 0; StringValueCStr(pattern); FilePathStringValue(path); if (flags & FNM_EXTGLOB) { - struct brace_args args; + struct brace_args args; - args.value = path; - args.flags = flags; - if (ruby_brace_expand(RSTRING_PTR(pattern), flags, fnmatch_brace, - (VALUE)&args, rb_enc_get(pattern), pattern) > 0) - return Qtrue; + args.value = path; + args.flags = flags; + if (ruby_brace_expand(RSTRING_PTR(pattern), flags, fnmatch_brace, + (VALUE)&args, rb_enc_get(pattern), pattern) > 0) + return Qtrue; } else { - rb_encoding *enc = rb_enc_compatible(pattern, path); - if (!enc) return Qfalse; - if (fnmatch(RSTRING_PTR(pattern), enc, RSTRING_PTR(path), flags) == 0) - return Qtrue; + rb_encoding *enc = rb_enc_compatible(pattern, path); + if (!enc) return Qfalse; + if (fnmatch(RSTRING_PTR(pattern), enc, RSTRING_PTR(path), flags) == 0) + return Qtrue; } RB_GC_GUARD(pattern); @@ -3228,12 +3516,16 @@ file_s_fnmatch(int argc, VALUE *argv, VALUE obj) } /* - * call-seq: - * Dir.home() -> "/home/me" - * Dir.home("root") -> "/root" + * call-seq: + * Dir.home(user_name = nil) -> dirpath + * + * Retruns the home directory path of the user specified with +user_name+ + * if it is not +nil+, or the current login user: * - * Returns the home directory of the current user or the named user - * if given. + * Dir.home # => "/home/me" + * Dir.home('root') # => "/root" + * + * Raises ArgumentError if +user_name+ is not a user name. */ static VALUE dir_s_home(int argc, VALUE *argv, VALUE obj) @@ -3244,12 +3536,12 @@ dir_s_home(int argc, VALUE *argv, VALUE obj) rb_check_arity(argc, 0, 1); user = (argc > 0) ? argv[0] : Qnil; if (!NIL_P(user)) { - SafeStringValue(user); - rb_must_asciicompat(user); - u = StringValueCStr(user); - if (*u) { - return rb_home_dir_of(user, rb_str_new(0, 0)); - } + StringValue(user); + rb_must_asciicompat(user); + u = StringValueCStr(user); + if (*u) { + return rb_home_dir_of(user, rb_str_new(0, 0)); + } } return rb_default_home_dir(rb_str_new(0, 0)); @@ -3258,10 +3550,15 @@ dir_s_home(int argc, VALUE *argv, VALUE obj) #if 0 /* * call-seq: - * Dir.exist?(file_name) -> true or false + * Dir.exist?(dirpath) -> true or false * - * Returns <code>true</code> if the named file is a directory, - * <code>false</code> otherwise. + * Returns whether +dirpath+ is a directory in the underlying file system: + * + * Dir.exist?('/example') # => true + * Dir.exist?('/nosuch') # => false + * Dir.exist?('/example/main.rb') # => false + * + * Same as File.directory?. * */ VALUE @@ -3279,24 +3576,23 @@ nogvl_dir_empty_p(void *ptr) VALUE result = Qtrue; if (!dir) { - int e = errno; - switch (gc_for_fd_with_gvl(e)) { - default: - dir = opendir(path); - if (dir) break; - e = errno; - /* fall through */ - case 0: - if (e == ENOTDIR) return (void *)Qfalse; - errno = e; /* for rb_sys_fail_path */ - return (void *)Qundef; - } + int e = errno; + switch (gc_for_fd_with_gvl(e)) { + default: + dir = opendir(path); + if (dir) break; + e = errno; + /* fall through */ + case 0: + if (e == ENOTDIR) return (void *)Qfalse; + return (void *)INT2FIX(e); + } } while ((dp = READDIR(dir, NULL)) != NULL) { - if (!to_be_skipped(dp)) { - result = Qfalse; - break; - } + if (!to_be_skipped(dp)) { + result = Qfalse; + break; + } } closedir(dir); return (void *)result; @@ -3304,10 +3600,18 @@ nogvl_dir_empty_p(void *ptr) /* * call-seq: - * Dir.empty?(path_name) -> true or false + * Dir.empty?(dirpath) -> true or false + * + * Returns whether +dirpath+ specifies an empty directory: * - * Returns <code>true</code> if the named file is an empty directory, - * <code>false</code> if it is not a directory or non-empty. + * dirpath = '/tmp/foo' + * Dir.mkdir(dirpath) + * Dir.empty?(dirpath) # => true + * Dir.empty?('/example') # => false + * Dir.empty?('/example/main.rb') # => false + * + * Raises an exception if +dirpath+ does not specify a directory or file + * in the underlying file system. */ static VALUE rb_dir_s_empty_p(VALUE obj, VALUE dirname) @@ -3324,27 +3628,26 @@ rb_dir_s_empty_p(VALUE obj, VALUE dirname) #if defined HAVE_GETATTRLIST && defined ATTR_DIR_ENTRYCOUNT { - 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) - 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 (attrbuf[0] >= 2 * sizeof(u_int32_t)) - return RBOOL(attrbuf[1] == 0); - if (false_on_notdir) return Qfalse; - } - rb_sys_fail_path(orig); - } - } -#endif - - result = (VALUE)rb_thread_call_without_gvl(nogvl_dir_empty_p, (void *)path, - RUBY_UBF_IO, 0); - if (result == Qundef) { - rb_sys_fail_path(orig); + 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) + 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 (attrbuf[0] >= 2 * sizeof(u_int32_t)) + return RBOOL(attrbuf[1] == 0); + if (false_on_notdir) return Qfalse; + } + rb_sys_fail_path(orig); + } + } +#endif + + result = (VALUE)IO_WITHOUT_GVL(nogvl_dir_empty_p, (void *)path); + if (FIXNUM_P(result)) { + rb_syserr_fail_path((int)FIX2LONG(result), orig); } return result; } @@ -3352,11 +3655,15 @@ rb_dir_s_empty_p(VALUE obj, VALUE dirname) void Init_Dir(void) { + rb_gc_register_address(&chdir_lock.path); + rb_gc_register_address(&chdir_lock.thread); + rb_cDir = rb_define_class("Dir", rb_cObject); rb_include_module(rb_cDir, rb_mEnumerable); rb_define_alloc_func(rb_cDir, dir_s_alloc); + rb_define_singleton_method(rb_cDir,"for_fd", dir_s_for_fd, 1); rb_define_singleton_method(rb_cDir, "foreach", dir_foreach, -1); rb_define_singleton_method(rb_cDir, "entries", dir_entries, -1); rb_define_singleton_method(rb_cDir, "each_child", dir_s_each_child, -1); @@ -3376,7 +3683,9 @@ Init_Dir(void) rb_define_method(rb_cDir,"pos", dir_tell, 0); rb_define_method(rb_cDir,"pos=", dir_set_pos, 1); rb_define_method(rb_cDir,"close", dir_close, 0); + rb_define_method(rb_cDir,"chdir", dir_chdir, 0); + rb_define_singleton_method(rb_cDir,"fchdir", dir_s_fchdir, 1); rb_define_singleton_method(rb_cDir,"chdir", dir_s_chdir, -1); rb_define_singleton_method(rb_cDir,"getwd", dir_s_getwd, 0); rb_define_singleton_method(rb_cDir,"pwd", dir_s_getwd, 0); @@ -3393,51 +3702,26 @@ 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: File::Constants::FNM_NOESCAPE - * - * Disables escapes in File.fnmatch and Dir.glob patterns - */ + /* Document-const: FNM_NOESCAPE + * {File::FNM_NOESCAPE}[rdoc-ref:File::Constants@File-3A-3AFNM_NOESCAPE] */ rb_file_const("FNM_NOESCAPE", INT2FIX(FNM_NOESCAPE)); - - /* Document-const: File::Constants::FNM_PATHNAME - * - * Wildcards in File.fnmatch and Dir.glob patterns do not match directory - * separators - */ + /* Document-const: FNM_PATHNAME + * {File::FNM_PATHNAME}[rdoc-ref:File::Constants@File-3A-3AFNM_PATHNAME] */ rb_file_const("FNM_PATHNAME", INT2FIX(FNM_PATHNAME)); - - /* Document-const: File::Constants::FNM_DOTMATCH - * - * The '*' wildcard matches filenames starting with "." in File.fnmatch - * and Dir.glob patterns - */ + /* Document-const: FNM_DOTMATCH + * {File::FNM_DOTMATCH}[rdoc-ref:File::Constants@File-3A-3AFNM_DOTMATCH] */ rb_file_const("FNM_DOTMATCH", INT2FIX(FNM_DOTMATCH)); - - /* Document-const: File::Constants::FNM_CASEFOLD - * - * Makes File.fnmatch patterns case insensitive (but not Dir.glob - * patterns). - */ + /* Document-const: FNM_CASEFOLD + * {File::FNM_CASEFOLD}[rdoc-ref:File::Constants@File-3A-3AFNM_CASEFOLD] */ rb_file_const("FNM_CASEFOLD", INT2FIX(FNM_CASEFOLD)); - - /* Document-const: File::Constants::FNM_EXTGLOB - * - * Allows file globbing through "{a,b}" in File.fnmatch patterns. - */ + /* Document-const: FNM_EXTGLOB + * {File::FNM_EXTGLOB}[rdoc-ref:File::Constants@File-3A-3AFNM_EXTGLOB] */ rb_file_const("FNM_EXTGLOB", INT2FIX(FNM_EXTGLOB)); - - /* Document-const: File::Constants::FNM_SYSCASE - * - * System default case insensitiveness, equals to FNM_CASEFOLD or - * 0. - */ + /* Document-const: FNM_SYSCASE + * {File::FNM_SYSCASE}[rdoc-ref:File::Constants@File-3A-3AFNM_SYSCASE] */ rb_file_const("FNM_SYSCASE", INT2FIX(FNM_SYSCASE)); - - /* Document-const: File::Constants::FNM_SHORTNAME - * - * Makes patterns to match short names if existing. Valid only - * on Microsoft Windows. - */ + /* Document-const: FNM_SHORTNAME + * {File::FNM_SHORTNAME}[rdoc-ref:File::Constants@File-3A-3AFNM_SHORTNAME] */ rb_file_const("FNM_SHORTNAME", INT2FIX(FNM_SHORTNAME)); } |