ruby-changes:48816
From: nobu <ko1@a...>
Date: Wed, 29 Nov 2017 18:59:27 +0900 (JST)
Subject: [ruby-changes:48816] nobu:r60933 (trunk): file.c: File.lutime
nobu 2017-11-29 18:59:20 +0900 (Wed, 29 Nov 2017) New Revision: 60933 https://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=revision&revision=60933 Log: file.c: File.lutime * file.c (utime_internal): add File.lutime. [Feature #4052] Modified files: trunk/NEWS trunk/configure.ac trunk/file.c trunk/test/ruby/test_file_exhaustive.rb Index: configure.ac =================================================================== --- configure.ac (revision 60932) +++ configure.ac (revision 60933) @@ -2328,6 +2328,7 @@ AC_CHECK_FUNCS(llabs) https://github.com/ruby/ruby/blob/trunk/configure.ac#L2328 AC_CHECK_FUNCS(lockf) AC_CHECK_FUNCS(log2) AC_CHECK_FUNCS(lstat) +AC_CHECK_FUNCS(lutime) AC_CHECK_FUNCS(malloc_usable_size) AC_CHECK_FUNCS(malloc_size) AC_CHECK_FUNCS(mblen) Index: NEWS =================================================================== --- NEWS (revision 60932) +++ NEWS (revision 60933) @@ -46,6 +46,7 @@ with all sufficient information, see the https://github.com/ruby/ruby/blob/trunk/NEWS#L46 * File.stat, File.exist?, and other rb_stat()-using methods release GVL [Bug #13941] * File.rename releases GVL [Feature #13951] + * Add File.lutime [Feature #4052] * Hash Index: test/ruby/test_file_exhaustive.rb =================================================================== --- test/ruby/test_file_exhaustive.rb (revision 60932) +++ test/ruby/test_file_exhaustive.rb (revision 60933) @@ -647,6 +647,36 @@ class TestFileExhaustive < Test::Unit::T https://github.com/ruby/ruby/blob/trunk/test/ruby/test_file_exhaustive.rb#L647 assert_equal(t + 2, File.mtime(zerofile)) end + def test_utime_symlinkfile + return unless symlinkfile + t = Time.local(2000) + stat = File.lstat(symlinkfile) + assert_equal(File.utime(t, t, symlinkfile), 1) + assert_equal(File.stat(regular_file).atime, t) + assert_equal(File.stat(regular_file).mtime, t) + assert_equal(File.lstat(symlinkfile).atime, stat.atime) + assert_equal(File.lstat(symlinkfile).mtime, stat.mtime) + end + + def test_lutime + return unless File.respond_to?(:lutime) + return unless symlinkfile + + r = File.stat(regular_file) + t = Time.local(2000) + File.lutime(t + 1, t + 2, symlinkfile) + rescue NotImplementedError => e + skip(e.message) + else + stat = File.stat(regular_file) + assert_equal(r.atime, stat.atime) + assert_equal(r.mtime, stat.mtime) + + stat = File.lstat(symlinkfile) + assert_equal(t + 1, stat.atime) + assert_equal(t + 2, stat.mtime) + end + def test_hardlink return unless hardlinkfile assert_equal("file", File.ftype(hardlinkfile)) Index: file.c =================================================================== --- file.c (revision 60932) +++ file.c (revision 60933) @@ -2662,6 +2662,7 @@ rb_file_s_lchown(int argc, VALUE *argv) https://github.com/ruby/ruby/blob/trunk/file.c#L2662 struct utime_args { const struct timespec* tsp; VALUE atime, mtime; + int follow; /* Whether to act on symlinks (1) or their referent (0) */ }; #ifdef UTIME_EINVAL @@ -2717,11 +2718,27 @@ utime_internal(const char *path, void *a https://github.com/ruby/ruby/blob/trunk/file.c#L2718 #if defined(HAVE_UTIMENSAT) static int try_utimensat = 1; +# ifdef AT_SYMLINK_NOFOLLOW + static int try_utimensat_follow = 1; +# else + const int try_utimensat_follow = 0; +# endif + int flags = 0; + + if (v->follow ? try_utimensat_follow : try_utimensat) { +# ifdef AT_SYMLINK_NOFOLLOW + if (v->follow) { + flags = AT_SYMLINK_NOFOLLOW; + } +# endif - if (try_utimensat) { - if (utimensat(AT_FDCWD, path, tsp, 0) < 0) { + if (utimensat(AT_FDCWD, path, tsp, flags) < 0) { if (errno == ENOSYS) { - try_utimensat = 0; +# ifdef AT_SYMLINK_NOFOLLOW + try_utimensat_follow = 0; +# endif + if (!v->follow) + try_utimensat = 0; goto no_utimensat; } return -1; /* calls utime_failed */ @@ -2738,6 +2755,9 @@ no_utimensat: https://github.com/ruby/ruby/blob/trunk/file.c#L2755 tvbuf[1].tv_usec = (int)(tsp[1].tv_nsec / 1000); tvp = tvbuf; } +#ifdef HAVE_LUTIMES + if (v->follow) return lutimes(path, tvp); +#endif return utimes(path, tvp); } @@ -2766,17 +2786,8 @@ utime_internal(const char *path, void *a https://github.com/ruby/ruby/blob/trunk/file.c#L2786 #endif -/* - * call-seq: - * File.utime(atime, mtime, file_name,...) -> integer - * - * Sets the access and modification times of each - * named file to the first two arguments. Returns - * the number of file names in the argument list. - */ - static VALUE -rb_file_s_utime(int argc, VALUE *argv) +utime_internal_i(int argc, VALUE *argv, int follow) { struct utime_args args; struct timespec tss[2], *tsp = NULL; @@ -2785,6 +2796,8 @@ rb_file_s_utime(int argc, VALUE *argv) https://github.com/ruby/ruby/blob/trunk/file.c#L2796 args.atime = *argv++; args.mtime = *argv++; + args.follow = follow; + if (!NIL_P(args.atime) || !NIL_P(args.mtime)) { tsp = tss; tsp[0] = rb_time_timespec(args.atime); @@ -2798,6 +2811,45 @@ rb_file_s_utime(int argc, VALUE *argv) https://github.com/ruby/ruby/blob/trunk/file.c#L2811 return apply2files(utime_internal, argc, argv, &args); } +/* + * call-seq: + * File.utime(atime, mtime, file_name,...) -> integer + * + * Sets the access and modification times of each named file to the + * first two arguments. If a file is a symlink, this method acts upon + * its referent rather than the link itself; for the inverse + * behavior see <code>File.lutime</code>. Returns the number of file + * names in the argument list. + */ + +static VALUE +rb_file_s_utime(int argc, VALUE *argv) +{ + return utime_internal_i(argc, argv, FALSE); +} + +#if defined(HAVE_UTIMES) && (defined(HAVE_LUTIMES) || (defined(HAVE_UTIMENSAT) && defined(AT_SYMLINK_NOFOLLOW))) + +/* + * call-seq: + * File.lutime(atime, mtime, file_name,...) -> integer + * + * Sets the access and modification times of each named file to the + * first two arguments. If a file is a symlink, this method acts upon + * the link itself as opposed to its referent; for the inverse + * behavior, see <code>File.utime</code>. Returns the number of file + * names in the argument list. + */ + +static VALUE +rb_file_s_lutime(int argc, VALUE *argv) +{ + return utime_internal_i(argc, argv, TRUE); +} +#else +#define rb_file_s_lutime rb_f_notimplement +#endif + #ifdef RUBY_FUNCTION_NAME_STRING # define syserr_fail2(e, s1, s2) syserr_fail2_in(RUBY_FUNCTION_NAME_STRING, e, s1, s2) #else @@ -6259,6 +6311,7 @@ Init_File(void) https://github.com/ruby/ruby/blob/trunk/file.c#L6311 rb_define_singleton_method(rb_cFile, "chown", rb_file_s_chown, -1); rb_define_singleton_method(rb_cFile, "lchmod", rb_file_s_lchmod, -1); rb_define_singleton_method(rb_cFile, "lchown", rb_file_s_lchown, -1); + rb_define_singleton_method(rb_cFile, "lutime", rb_file_s_lutime, -1); rb_define_singleton_method(rb_cFile, "link", rb_file_s_link, 2); rb_define_singleton_method(rb_cFile, "symlink", rb_file_s_symlink, 2); -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/