diff options
-rw-r--r-- | dir.c | 119 | ||||
-rw-r--r-- | error.c | 174 | ||||
-rw-r--r-- | internal.h | 8 | ||||
-rw-r--r-- | test/ruby/test_dir.rb | 18 | ||||
-rw-r--r-- | version.h | 6 |
5 files changed, 241 insertions, 84 deletions
@@ -1248,6 +1248,12 @@ to_be_ignored(int e) #define STAT(p, s) stat((p), (s)) #endif +typedef int ruby_glob_errfunc(const char*, VALUE, const void*, int); +typedef struct { + ruby_glob_func *match; + ruby_glob_errfunc *error; +} ruby_glob_funcs_t; + /* System call with warning */ static int do_stat(const char *path, struct stat *pst, int flags, rb_encoding *enc) @@ -1274,7 +1280,8 @@ do_lstat(const char *path, struct stat *pst, int flags, rb_encoding *enc) #endif static DIR * -do_opendir(const char *path, int flags, rb_encoding *enc) +do_opendir(const char *path, int flags, rb_encoding *enc, + ruby_glob_errfunc *errfunc, VALUE arg, int *status) { DIR *dirp; #ifdef _WIN32 @@ -1295,7 +1302,12 @@ do_opendir(const char *path, int flags, rb_encoding *enc) e = errno; /* fallback */ case 0: + *status = 0; if (to_be_ignored(e)) break; + if (errfunc) { + *status = (*errfunc)(path, arg, enc, e); + break; + } sys_warning(path, enc); } } @@ -1705,6 +1717,61 @@ glob_func_caller(VALUE val) return Qnil; } +struct glob_error_args { + const char *path; + rb_encoding *enc; + int error; +}; + +static VALUE +glob_func_warning(VALUE val) +{ + struct glob_error_args *arg = (struct glob_error_args *)val; + rb_syserr_enc_warning(arg->error, arg->enc, "%s", arg->path); + return Qnil; +} + +#if 0 +static int +rb_glob_warning(const char *path, VALUE a, const void *enc, int error) +{ + int status; + struct glob_error_args args; + + args.path = path; + args.enc = enc; + args.error = error; + rb_protect(glob_func_warning, (VALUE)&args, &status); + return status; +} +#endif + +static VALUE +glob_func_error(VALUE val) +{ + struct glob_error_args *arg = (struct glob_error_args *)val; + VALUE path = rb_enc_str_new_cstr(arg->path, arg->enc); + rb_syserr_fail_str(arg->error, path); + return Qnil; +} + +static int +rb_glob_error(const char *path, VALUE a, const void *enc, int error) +{ + int status; + struct glob_error_args args; + VALUE (*errfunc)(VALUE) = glob_func_error; + + if (error == EACCES) { + errfunc = glob_func_warning; + } + args.path = path; + args.enc = enc; + args.error = error; + rb_protect(errfunc, (VALUE)&args, &status); + return status; +} + static inline int dirent_match(const char *pat, rb_encoding *enc, const char *name, const struct dirent *dp, int flags) { @@ -1726,7 +1793,7 @@ glob_helper( struct glob_pattern **beg, struct glob_pattern **end, int flags, - ruby_glob_func *func, + const ruby_glob_funcs_t *funcs, VALUE arg, rb_encoding *enc) { @@ -1785,13 +1852,13 @@ glob_helper( } } if (match_all && pathtype > path_noent) { - status = glob_call_func(func, path, arg, enc); + status = glob_call_func(funcs->match, path, arg, enc); if (status) return status; } if (match_dir && pathtype == path_directory) { char *tmp = join_path(path, pathlen, dirsep, "", 0); if (!tmp) return -1; - status = glob_call_func(func, tmp, arg, enc); + status = glob_call_func(funcs->match, tmp, arg, enc); GLOB_FREE(tmp); if (status) return status; } @@ -1810,12 +1877,14 @@ glob_helper( 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(plainname, flags, enc); + dirp = do_opendir(plainname, flags, enc, funcs->error, arg, &status); GLOB_FREE(plainname); } else +# else + ; # endif - dirp = do_opendir(*path ? path : ".", flags, enc); + dirp = do_opendir(*path ? path : ".", flags, enc, funcs->error, arg, &status); if (dirp == NULL) { # if FNM_SYSCASE || NORMALIZE_UTF8PATH if ((magical < 2) && !recursive && (errno == EACCES)) { @@ -1823,7 +1892,7 @@ glob_helper( goto literally; } # endif - return 0; + return status; } IF_NORMALIZE_UTF8PATH(norm_p = need_normalization(dirp, *path ? path : ".")); @@ -1923,7 +1992,7 @@ glob_helper( status = glob_helper(buf, name - buf + namlen, 1, new_pathtype, new_beg, new_end, - flags, func, arg, enc); + flags, funcs, arg, enc); GLOB_FREE(buf); GLOB_FREE(new_beg); if (status) break; @@ -1983,11 +2052,12 @@ glob_helper( long base = pathlen + (dirsep != 0); buf = replace_real_basename(buf, base, enc, IF_NORMALIZE_UTF8PATH(1)+0, flags, &new_pathtype); + if (!buf) break; } #endif status = glob_helper(buf, pathlen + strlen(buf + pathlen), 1, new_pathtype, new_beg, new_end, - flags, func, arg, enc); + flags, funcs, arg, enc); GLOB_FREE(buf); GLOB_FREE(new_beg); if (status) break; @@ -2001,7 +2071,9 @@ glob_helper( } static int -ruby_glob0(const char *path, int flags, ruby_glob_func *func, VALUE arg, rb_encoding *enc) +ruby_glob0(const char *path, int flags, + const ruby_glob_funcs_t *funcs, VALUE arg, + rb_encoding *enc) { struct glob_pattern *list; const char *root, *start; @@ -2029,7 +2101,7 @@ ruby_glob0(const char *path, int flags, ruby_glob_func *func, VALUE arg, rb_enco return -1; } status = glob_helper(buf, n, 0, path_unknown, &list, &list + 1, - flags, func, arg, enc); + flags, funcs, arg, enc); glob_free_pattern(list); GLOB_FREE(buf); @@ -2039,8 +2111,11 @@ ruby_glob0(const char *path, int flags, ruby_glob_func *func, VALUE arg, rb_enco int ruby_glob(const char *path, int flags, ruby_glob_func *func, VALUE arg) { - return ruby_glob0(path, flags & ~GLOB_VERBOSE, func, arg, - rb_ascii8bit_encoding()); + ruby_glob_funcs_t funcs; + funcs.match = func; + funcs.error = NULL; + return ruby_glob0(path, flags & ~GLOB_VERBOSE, + &funcs, arg, rb_ascii8bit_encoding()); } static int @@ -2054,6 +2129,10 @@ rb_glob_caller(const char *path, VALUE a, void *enc) return status; } +static const ruby_glob_funcs_t rb_glob_funcs = { + rb_glob_caller, rb_glob_error, +}; + void rb_glob(const char *path, void (*func)(const char *, VALUE, void *), VALUE arg) { @@ -2064,8 +2143,8 @@ rb_glob(const char *path, void (*func)(const char *, VALUE, void *), VALUE arg) args.value = arg; args.enc = rb_ascii8bit_encoding(); - status = ruby_glob0(path, GLOB_VERBOSE, rb_glob_caller, (VALUE)&args, - args.enc); + status = ruby_glob0(path, GLOB_VERBOSE, &rb_glob_funcs, + (VALUE)&args, args.enc); if (status) GLOB_JUMP_TAG(status); } @@ -2143,7 +2222,7 @@ ruby_brace_expand(const char *str, int flags, ruby_glob_func *func, VALUE arg, } struct brace_args { - ruby_glob_func *func; + ruby_glob_funcs_t funcs; VALUE value; int flags; }; @@ -2153,7 +2232,7 @@ glob_brace(const char *path, VALUE val, void *enc) { struct brace_args *arg = (struct brace_args *)val; - return ruby_glob0(path, arg->flags, arg->func, arg->value, enc); + return ruby_glob0(path, arg->flags, &arg->funcs, arg->value, enc); } int @@ -2162,7 +2241,8 @@ ruby_brace_glob_with_enc(const char *str, int flags, ruby_glob_func *func, VALUE struct brace_args args; flags &= ~GLOB_VERBOSE; - args.func = func; + args.funcs.match = func; + args.funcs.error = NULL; args.value = arg; args.flags = flags; return ruby_brace_expand(str, flags, glob_brace, (VALUE)&args, enc); @@ -2184,7 +2264,8 @@ push_caller(const char *path, VALUE val, void *enc) { struct push_glob_args *arg = (struct push_glob_args *)val; - return ruby_glob0(path, arg->flags, rb_glob_caller, (VALUE)&arg->glob, enc); + return ruby_glob0(path, arg->flags, &rb_glob_funcs, + (VALUE)&arg->glob, enc); } static int @@ -217,62 +217,52 @@ warning_string(rb_encoding *enc, const char *fmt, va_list args) fmt, args); } +#define with_warning_string(mesg, enc, fmt) \ + VALUE mesg; \ + va_list args; va_start(args, fmt); \ + mesg = warning_string(enc, fmt, args); \ + va_end(args); + void rb_warn(const char *fmt, ...) { - VALUE mesg; - va_list args; - - if (NIL_P(ruby_verbose)) return; - - va_start(args, fmt); - mesg = warning_string(0, fmt, args); - va_end(args); - rb_write_warning_str(mesg); + if (!NIL_P(ruby_verbose)) { + with_warning_string(mesg, 0, fmt) { + rb_write_warning_str(mesg); + } + } } void rb_enc_warn(rb_encoding *enc, const char *fmt, ...) { - VALUE mesg; - va_list args; - - if (NIL_P(ruby_verbose)) return; - - va_start(args, fmt); - mesg = warning_string(enc, fmt, args); - va_end(args); - rb_write_warning_str(mesg); + if (!NIL_P(ruby_verbose)) { + with_warning_string(mesg, enc, fmt) { + rb_write_warning_str(mesg); + } + } } /* rb_warning() reports only in verbose mode */ void rb_warning(const char *fmt, ...) { - VALUE mesg; - va_list args; - - if (!RTEST(ruby_verbose)) return; - - va_start(args, fmt); - mesg = warning_string(0, fmt, args); - va_end(args); - rb_write_warning_str(mesg); + if (RTEST(ruby_verbose)) { + with_warning_string(mesg, 0, fmt) { + rb_write_warning_str(mesg); + } + } } #if 0 void rb_enc_warning(rb_encoding *enc, const char *fmt, ...) { - VALUE mesg; - va_list args; - - if (!RTEST(ruby_verbose)) return; - - va_start(args, fmt); - mesg = warning_string(enc, fmt, args); - va_end(args); - rb_write_warning_str(mesg); + if (RTEST(ruby_verbose)) { + with_warning_string(mesg, enc, fmt) { + rb_write_warning_str(mesg); + } + } } #endif @@ -2394,44 +2384,104 @@ rb_mod_syserr_fail_str(VALUE mod, int e, VALUE mesg) rb_exc_raise(exc); } +static void +syserr_warning(VALUE mesg, int err) +{ + rb_str_set_len(mesg, RSTRING_LEN(mesg)-1); + rb_str_catf(mesg, ": %s\n", strerror(err)); + rb_write_warning_str(mesg); +} + +#if 0 void -rb_sys_warning(const char *fmt, ...) +rb_sys_warn(const char *fmt, ...) { - VALUE mesg; - va_list args; - int errno_save; + if (!NIL_P(ruby_verbose)) { + int errno_save = errno; + with_warning_string(mesg, 0, fmt) { + syserr_warning(mesg, errno_save); + } + errno = errno_save; + } +} - errno_save = errno; +void +rb_syserr_warn(int err, const char *fmt, ...) +{ + if (!NIL_P(ruby_verbose)) { + with_warning_string(mesg, 0, fmt) { + syserr_warning(mesg, err); + } + } +} - if (!RTEST(ruby_verbose)) return; +void +rb_sys_enc_warn(rb_encoding *enc, const char *fmt, ...) +{ + if (!NIL_P(ruby_verbose)) { + int errno_save = errno; + with_warning_string(mesg, enc, fmt) { + syserr_warning(mesg, errno_save); + } + errno = errno_save; + } +} - va_start(args, fmt); - mesg = warning_string(0, fmt, args); - va_end(args); - rb_str_set_len(mesg, RSTRING_LEN(mesg)-1); - rb_str_catf(mesg, ": %s\n", strerror(errno_save)); - rb_write_warning_str(mesg); - errno = errno_save; +void +rb_syserr_enc_warn(int err, rb_encoding *enc, const char *fmt, ...) +{ + if (!NIL_P(ruby_verbose)) { + with_warning_string(mesg, enc, fmt) { + syserr_warning(mesg, err); + } + } } +#endif void -rb_sys_enc_warning(rb_encoding *enc, const char *fmt, ...) +rb_sys_warning(const char *fmt, ...) { - VALUE mesg; - va_list args; - int errno_save; + if (RTEST(ruby_verbose)) { + int errno_save = errno; + with_warning_string(mesg, 0, fmt) { + syserr_warning(mesg, errno_save); + } + errno = errno_save; + } +} - errno_save = errno; +#if 0 +void +rb_syserr_warning(int err, const char *fmt, ...) +{ + if (RTEST(ruby_verbose)) { + with_warning_string(mesg, 0, fmt) { + syserr_warning(mesg, err); + } + } +} +#endif - if (!RTEST(ruby_verbose)) return; +void +rb_sys_enc_warning(rb_encoding *enc, const char *fmt, ...) +{ + if (RTEST(ruby_verbose)) { + int errno_save = errno; + with_warning_string(mesg, enc, fmt) { + syserr_warning(mesg, errno_save); + } + errno = errno_save; + } +} - va_start(args, fmt); - mesg = warning_string(enc, fmt, args); - va_end(args); - rb_str_set_len(mesg, RSTRING_LEN(mesg)-1); - rb_str_catf(mesg, ": %s\n", strerror(errno_save)); - rb_write_warning_str(mesg); - errno = errno_save; +void +rb_syserr_enc_warning(int err, rb_encoding *enc, const char *fmt, ...) +{ + if (RTEST(ruby_verbose)) { + with_warning_string(mesg, enc, fmt) { + syserr_warning(mesg, err); + } + } } void diff --git a/internal.h b/internal.h index 45e32c35b7..cb758b94ad 100644 --- a/internal.h +++ b/internal.h @@ -1015,9 +1015,17 @@ VALUE rb_check_backtrace(VALUE); NORETURN(void rb_async_bug_errno(const char *,int)); const char *rb_builtin_type_name(int t); const char *rb_builtin_class_name(VALUE x); +PRINTF_ARGS(void rb_sys_warn(const char *fmt, ...), 1, 2); +PRINTF_ARGS(void rb_syserr_warn(int err, const char *fmt, ...), 2, 3); PRINTF_ARGS(void rb_enc_warn(rb_encoding *enc, const char *fmt, ...), 2, 3); +PRINTF_ARGS(void rb_sys_enc_warn(rb_encoding *enc, const char *fmt, ...), 2, 3); +PRINTF_ARGS(void rb_syserr_enc_warn(int err, rb_encoding *enc, const char *fmt, ...), 3, 4); +PRINTF_ARGS(void rb_sys_warning(const char *fmt, ...), 1, 2); +PRINTF_ARGS(void rb_syserr_warning(int err, const char *fmt, ...), 2, 3); PRINTF_ARGS(void rb_enc_warning(rb_encoding *enc, const char *fmt, ...), 2, 3); PRINTF_ARGS(void rb_sys_enc_warning(rb_encoding *enc, const char *fmt, ...), 2, 3); +PRINTF_ARGS(void rb_syserr_enc_warning(int err, rb_encoding *enc, const char *fmt, ...), 3, 4); + VALUE rb_name_err_new(VALUE mesg, VALUE recv, VALUE method); #define rb_name_err_raise_str(mesg, recv, name) \ rb_exc_raise(rb_name_err_new(mesg, recv, name)) diff --git a/test/ruby/test_dir.rb b/test/ruby/test_dir.rb index d100fd7d59..553f1aef63 100644 --- a/test/ruby/test_dir.rb +++ b/test/ruby/test_dir.rb @@ -184,6 +184,24 @@ class TestDir < Test::Unit::TestCase end end + if Process.const_defined?(:RLIMIT_NOFILE) + def test_glob_too_may_open_files + assert_separately([], "#{<<-"begin;"}\n#{<<-'end;'}", chdir: @root) + begin; + n = 16 + Process.setrlimit(Process::RLIMIT_NOFILE, n) + files = [] + begin + n.times {files << File.open('b')} + rescue Errno::EMFILE, Errno::ENFILE => e + end + assert_raise(e.class) { + Dir.glob('*') + } + end; + end + end + def assert_entries(entries) entries.sort! assert_equal(%w(. ..) + ("a".."z").to_a, entries) @@ -1,10 +1,10 @@ #define RUBY_VERSION "2.4.4" -#define RUBY_RELEASE_DATE "2017-12-20" -#define RUBY_PATCHLEVEL 214 +#define RUBY_RELEASE_DATE "2017-12-21" +#define RUBY_PATCHLEVEL 215 #define RUBY_RELEASE_YEAR 2017 #define RUBY_RELEASE_MONTH 12 -#define RUBY_RELEASE_DAY 20 +#define RUBY_RELEASE_DAY 21 #include "ruby/version.h" |