diff --git a/enumerator.c b/enumerator.c index b91b2eb940d445..3b69778f074246 100644 --- a/enumerator.c +++ b/enumerator.c @@ -3952,46 +3952,14 @@ rb_arithmetic_sequence_beg_len_step(VALUE obj, long *begp, long *lenp, long *ste return Qnil; } -/* - * call-seq: - * aseq.first -> num or nil - * aseq.first(n) -> an_array - * - * Returns the first number in this arithmetic sequence, - * or an array of the first +n+ elements. - */ static VALUE -arith_seq_first(int argc, VALUE *argv, VALUE self) +arith_seq_take(VALUE self, VALUE num) { VALUE b, e, s, ary; long n; int x; - rb_check_arity(argc, 0, 1); - - b = arith_seq_begin(self); - e = arith_seq_end(self); - s = arith_seq_step(self); - if (argc == 0) { - if (NIL_P(b)) { - return Qnil; - } - if (!NIL_P(e)) { - VALUE zero = INT2FIX(0); - int r = rb_cmpint(rb_num_coerce_cmp(s, zero, idCmp), s, zero); - if (r > 0 && RTEST(rb_funcall(b, '>', 1, e))) { - return Qnil; - } - if (r < 0 && RTEST(rb_funcall(b, '<', 1, e))) { - return Qnil; - } - } - return b; - } - - // TODO: the following code should be extracted as arith_seq_take - - n = NUM2LONG(argv[0]); + n = NUM2LONG(num); if (n < 0) { rb_raise(rb_eArgError, "attempt to take negative size"); } @@ -3999,6 +3967,9 @@ arith_seq_first(int argc, VALUE *argv, VALUE self) return rb_ary_new_capa(0); } + b = arith_seq_begin(self); + e = arith_seq_end(self); + s = arith_seq_step(self); x = arith_seq_exclude_end_p(self); if (FIXNUM_P(b) && NIL_P(e) && FIXNUM_P(s)) { @@ -4093,7 +4064,49 @@ arith_seq_first(int argc, VALUE *argv, VALUE self) return ary; } - return rb_call_super(argc, argv); + { + VALUE argv[1]; + argv[0] = num; + return rb_call_super(1, argv); + } +} + +/* + * call-seq: + * aseq.first -> num or nil + * aseq.first(n) -> an_array + * + * Returns the first number in this arithmetic sequence, + * or an array of the first +n+ elements. + */ +static VALUE +arith_seq_first(int argc, VALUE *argv, VALUE self) +{ + VALUE b, e, s; + + rb_check_arity(argc, 0, 1); + + b = arith_seq_begin(self); + e = arith_seq_end(self); + s = arith_seq_step(self); + if (argc == 0) { + if (NIL_P(b)) { + return Qnil; + } + if (!NIL_P(e)) { + VALUE zero = INT2FIX(0); + int r = rb_cmpint(rb_num_coerce_cmp(s, zero, idCmp), s, zero); + if (r > 0 && RTEST(rb_funcall(b, '>', 1, e))) { + return Qnil; + } + if (r < 0 && RTEST(rb_funcall(b, '<', 1, e))) { + return Qnil; + } + } + return b; + } + + return arith_seq_take(self, argv[0]); } static inline VALUE diff --git a/lib/fileutils.rb b/lib/fileutils.rb index b9d683797ab03b..5a4fc6e23bf5f2 100644 --- a/lib/fileutils.rb +++ b/lib/fileutils.rb @@ -708,9 +708,10 @@ def ln_s(src, dest, force: nil, relative: false, target_directory: true, noop: n if relative return ln_sr(src, dest, force: force, noop: noop, verbose: verbose) end - fu_output_message "ln -s#{force ? 'f' : ''} #{[src,dest].flatten.join ' '}" if verbose + fu_output_message "ln -s#{force ? 'f' : ''}#{ + target_directory ? '' : 'T'} #{[src,dest].flatten.join ' '}" if verbose return if noop - fu_each_src_dest0(src, dest) do |s,d| + fu_each_src_dest0(src, dest, target_directory) do |s,d| remove_file d, true if force File.symlink s, d end @@ -730,17 +731,17 @@ def ln_sf(src, dest, noop: nil, verbose: nil) # Like FileUtils.ln_s, but create links relative to +dest+. # def ln_sr(src, dest, target_directory: true, force: nil, noop: nil, verbose: nil) - options = "#{force ? 'f' : ''}#{target_directory ? '' : 'T'}" - dest = File.path(dest) - srcs = Array(src) - link = proc do |s, target_dir_p = true| - s = File.path(s) - if target_dir_p - d = File.join(destdirs = dest, File.basename(s)) - else - destdirs = File.dirname(d = dest) + fu_output_message "ln -sr#{force ? 'f' : ''}#{ + target_directory ? '' : 'T'} #{[src,dest].flatten.join ' '}" if verbose + return if noop + unless target_directory + destdirs = fu_split_path(File.realdirpath(dest)) + end + fu_each_src_dest0(src, dest, target_directory) do |s,d| + if target_directory + destdirs = fu_split_path(File.realdirpath(File.dirname(d))) + # else d == dest end - destdirs = fu_split_path(File.realpath(destdirs)) if fu_starting_path?(s) srcdirs = fu_split_path((File.realdirpath(s) rescue File.expand_path(s))) base = fu_relative_components_from(srcdirs, destdirs) @@ -754,18 +755,9 @@ def ln_sr(src, dest, target_directory: true, force: nil, noop: nil, verbose: nil end s = File.join(*base, *srcdirs) end - fu_output_message "ln -s#{options} #{s} #{d}" if verbose - next if noop remove_file d, true if force File.symlink s, d end - case srcs.size - when 0 - when 1 - link[srcs[0], target_directory && File.directory?(dest)] - else - srcs.each(&link) - end end module_function :ln_sr @@ -800,13 +792,13 @@ def ln_sr(src, dest, target_directory: true, force: nil, noop: nil, verbose: nil # File.file?('dest1/dir1/t2.txt') # => true # File.file?('dest1/dir1/t3.txt') # => true # - # Keyword arguments: + # Optional arguments: # - # - dereference_root: true - dereferences +src+ if it is a symbolic link. - # - remove_destination: true - removes +dest+ before creating links. + # - +dereference_root+ - dereferences +src+ if it is a symbolic link (+false+ by default). + # - +remove_destination+ - removes +dest+ before creating links (+false+ by default). # # Raises an exception if +dest+ is the path to an existing file or directory - # and keyword argument remove_destination: true is not given. + # and optional argument +remove_destination+ is not given. # # Related: FileUtils.ln (has different options). # @@ -1029,12 +1021,12 @@ def cp_r(src, dest, preserve: nil, noop: nil, verbose: nil, # directories, and symbolic links; # other file types (FIFO streams, device files, etc.) are not supported. # - # Keyword arguments: + # Optional arguments: # - # - dereference_root: true - if +src+ is a symbolic link, - # follows the link. - # - preserve: true - preserves file times. - # - remove_destination: true - removes +dest+ before copying files. + # - +dereference_root+ - if +src+ is a symbolic link, + # follows the link (+false+ by default). + # - +preserve+ - preserves file times (+false+ by default). + # - +remove_destination+ - removes +dest+ before copying files (+false+ by default). # # Related: {methods for copying}[rdoc-ref:FileUtils@Copying]. # @@ -1065,12 +1057,12 @@ def copy_entry(src, dest, preserve = false, dereference_root = false, remove_des # FileUtils.copy_file('src0.txt', 'dest0.txt') # File.file?('dest0.txt') # => true # - # Keyword arguments: + # Optional arguments: # - # - dereference: false - if +src+ is a symbolic link, - # does not follow the link. - # - preserve: true - preserves file times. - # - remove_destination: true - removes +dest+ before copying files. + # - +dereference+ - if +src+ is a symbolic link, + # follows the link (+true+ by default). + # - +preserve+ - preserves file times (+false+ by default). + # - +remove_destination+ - removes +dest+ before copying files (+false+ by default). # # Related: {methods for copying}[rdoc-ref:FileUtils@Copying]. # @@ -2475,6 +2467,9 @@ def fu_each_src_dest(src, dest) #:nodoc: def fu_each_src_dest0(src, dest, target_directory = true) #:nodoc: if tmp = Array.try_convert(src) + unless target_directory or tmp.size <= 1 + raise ArgumentError, "extra target #{tmp}" + end tmp.each do |s| s = File.path(s) yield s, (target_directory ? File.join(dest, File.basename(s)) : dest) diff --git a/test/fileutils/test_fileutils.rb b/test/fileutils/test_fileutils.rb index 1d7be692f52c04..c9d8d0c7d05175 100644 --- a/test/fileutils/test_fileutils.rb +++ b/test/fileutils/test_fileutils.rb @@ -955,16 +955,27 @@ def test_ln_pathname def test_ln_s check_singleton :ln_s + ln_s TARGETS, 'tmp' + each_srcdest do |fname, lnfname| + assert_equal fname, File.readlink(lnfname) + ensure + rm_f lnfname + end + + lnfname = 'symlink' + assert_raise(Errno::ENOENT, "multiple targets need a destination directory") { + ln_s TARGETS, lnfname + } + assert_file.not_exist?(lnfname) + TARGETS.each do |fname| - begin - fname = "../#{fname}" - lnfname = 'tmp/lnsdest' - ln_s fname, lnfname - assert FileTest.symlink?(lnfname), 'not symlink' - assert_equal fname, File.readlink(lnfname) - ensure - rm_f lnfname - end + fname = "../#{fname}" + lnfname = 'tmp/lnsdest' + ln_s fname, lnfname + assert_file.symlink?(lnfname) + assert_equal fname, File.readlink(lnfname) + ensure + rm_f lnfname end end if have_symlink? and !no_broken_symlink? @@ -1017,22 +1028,52 @@ def test_ln_sf_pathname def test_ln_sr check_singleton :ln_sr - TARGETS.each do |fname| - begin - lnfname = 'tmp/lnsdest' - ln_sr fname, lnfname - assert FileTest.symlink?(lnfname), 'not symlink' - assert_equal "../#{fname}", File.readlink(lnfname), fname + assert_all_assertions_foreach(nil, *TARGETS) do |fname| + lnfname = 'tmp/lnsdest' + ln_sr fname, lnfname + assert FileTest.symlink?(lnfname), 'not symlink' + assert_equal "../#{fname}", File.readlink(lnfname) + ensure + rm_f lnfname + end + + ln_sr TARGETS, 'tmp' + assert_all_assertions do |all| + each_srcdest do |fname, lnfname| + all.for(fname) do + assert_equal "../#{fname}", File.readlink(lnfname) + end ensure rm_f lnfname end end + mkdir 'data/src' File.write('data/src/xxx', 'ok') File.symlink '../data/src', 'tmp/src' ln_sr 'tmp/src/xxx', 'data' assert File.symlink?('data/xxx') assert_equal 'ok', File.read('data/xxx') + end + + def test_ln_sr_not_target_directory + assert_raise(ArgumentError) { + ln_sr TARGETS, 'tmp', target_directory: false + } + assert_empty(Dir.children('tmp')) + + lnfname = 'symlink' + assert_raise(ArgumentError) { + ln_sr TARGETS, lnfname, target_directory: false + } + assert_file.not_exist?(lnfname) + + assert_all_assertions_foreach(nil, *TARGETS) do |fname| + assert_raise(Errno::EEXIST, Errno::EACCES) { + ln_sr fname, 'tmp', target_directory: false + } + assert_file.not_exist? File.join('tmp/', File.basename(fname)) + end end if have_symlink? def test_ln_sr_broken_symlink