ruby-changes:49252
From: nagachika <ko1@a...>
Date: Thu, 21 Dec 2017 00:20:20 +0900 (JST)
Subject: [ruby-changes:49252] nagachika:r61367 (ruby_2_4): merge revision(s) 58146, 58150, 58156: [Backport #13276]
nagachika 2017-12-21 00:20:15 +0900 (Thu, 21 Dec 2017) New Revision: 61367 https://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=revision&revision=61367 Log: merge revision(s) 58146,58150,58156: [Backport #13276] error.c: refactor warning messages * error.c (with_warning_string): extract building warning message string from variadic arguments. * error.c (syserr_warning): write warning message with the system error message. error.c: warning functions * error.c: define warning functions in all combinations of * no errno, system errno, argument * without/with encoding * enabled/disabled by default dir.c: err at glob failure * dir.c (glob_helper): raise a SystemCallError exception when opendir() failed, except for ENOENT, ENOTDIR, and EACCES. this behavior predates 1.0; the comments in glob.c claimed that glob() returned -1 on error but actualy the pointer to a global variable, then dir_glob() did check only -1 as the comments, and ignored actual errors. [ruby-core:80226] [Bug #13276] dir.c: ruby_glob_funcs_t Modified directories: branches/ruby_2_4/ Modified files: branches/ruby_2_4/dir.c branches/ruby_2_4/error.c branches/ruby_2_4/internal.h branches/ruby_2_4/test/ruby/test_dir.rb branches/ruby_2_4/version.h Index: ruby_2_4/dir.c =================================================================== --- ruby_2_4/dir.c (revision 61366) +++ ruby_2_4/dir.c (revision 61367) @@ -1248,6 +1248,12 @@ to_be_ignored(int e) https://github.com/ruby/ruby/blob/trunk/ruby_2_4/dir.c#L1248 #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 * https://github.com/ruby/ruby/blob/trunk/ruby_2_4/dir.c#L1280 #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, https://github.com/ruby/ruby/blob/trunk/ruby_2_4/dir.c#L1302 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) https://github.com/ruby/ruby/blob/trunk/ruby_2_4/dir.c#L1717 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( https://github.com/ruby/ruby/blob/trunk/ruby_2_4/dir.c#L1793 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( https://github.com/ruby/ruby/blob/trunk/ruby_2_4/dir.c#L1852 } } 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( https://github.com/ruby/ruby/blob/trunk/ruby_2_4/dir.c#L1877 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( https://github.com/ruby/ruby/blob/trunk/ruby_2_4/dir.c#L1892 goto literally; } # endif - return 0; + return status; } IF_NORMALIZE_UTF8PATH(norm_p = need_normalization(dirp, *path ? path : ".")); @@ -1923,7 +1992,7 @@ glob_helper( https://github.com/ruby/ruby/blob/trunk/ruby_2_4/dir.c#L1992 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( https://github.com/ruby/ruby/blob/trunk/ruby_2_4/dir.c#L2052 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( https://github.com/ruby/ruby/blob/trunk/ruby_2_4/dir.c#L2071 } 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, https://github.com/ruby/ruby/blob/trunk/ruby_2_4/dir.c#L2101 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, https://github.com/ruby/ruby/blob/trunk/ruby_2_4/dir.c#L2111 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 https://github.com/ruby/ruby/blob/trunk/ruby_2_4/dir.c#L2129 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)(c https://github.com/ruby/ruby/blob/trunk/ruby_2_4/dir.c#L2143 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 f https://github.com/ruby/ruby/blob/trunk/ruby_2_4/dir.c#L2222 } 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, https://github.com/ruby/ruby/blob/trunk/ruby_2_4/dir.c#L2232 { 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 https://github.com/ruby/ruby/blob/trunk/ruby_2_4/dir.c#L2241 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, https://github.com/ruby/ruby/blob/trunk/ruby_2_4/dir.c#L2264 { 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 Index: ruby_2_4/test/ruby/test_dir.rb =================================================================== --- ruby_2_4/test/ruby/test_dir.rb (revision 61366) +++ ruby_2_4/test/ruby/test_dir.rb (revision 61367) @@ -184,6 +184,24 @@ class TestDir < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/ruby_2_4/test/ruby/test_dir.rb#L184 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) Index: ruby_2_4/internal.h =================================================================== --- ruby_2_4/internal.h (revision 61366) +++ ruby_2_4/internal.h (revision 61367) @@ -1015,9 +1015,17 @@ VALUE rb_check_backtrace(VALUE); https://github.com/ruby/ruby/blob/trunk/ruby_2_4/internal.h#L1015 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)) Index: ruby_2_4/version.h =================================================================== --- ruby_2_4/version.h (revision 61366) +++ ruby_2_4/version.h (revision 61367) @@ -1,10 +1,10 @@ https://github.com/ruby/ruby/blob/trunk/ruby_2_4/version.h#L1 #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" Index: ruby_2_4/error.c =================================================================== --- ruby_2_4/error.c (revision 61366) +++ ruby_2_4/error.c (revision 61367) @@ -217,62 +217,52 @@ warning_string(rb_encoding *enc, const c https://github.com/ruby/ruby/blob/trunk/ruby_2_4/error.c#L217 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, https://github.com/ruby/ruby/blob/trunk/ruby_2_4/error.c#L2384 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 Index: ruby_2_4 =================================================================== --- ruby_2_4 (revision 61366) +++ ruby_2_4 (revision 61367) Property changes on: ruby_2_4 ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /trunk:r58146,58150,58156 -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/