Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 48 additions & 35 deletions enumerator.c
Original file line number Diff line number Diff line change
Expand Up @@ -3952,53 +3952,24 @@ 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");
}
if (n == 0) {
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)) {
Expand Down Expand Up @@ -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
Expand Down
65 changes: 30 additions & 35 deletions lib/fileutils.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
Expand All @@ -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

Expand Down Expand Up @@ -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:
#
# - <tt>dereference_root: true</tt> - dereferences +src+ if it is a symbolic link.
# - <tt>remove_destination: true</tt> - 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 <tt>remove_destination: true</tt> is not given.
# and optional argument +remove_destination+ is not given.
#
# Related: FileUtils.ln (has different options).
#
Expand Down Expand Up @@ -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:
#
# - <tt>dereference_root: true</tt> - if +src+ is a symbolic link,
# follows the link.
# - <tt>preserve: true</tt> - preserves file times.
# - <tt>remove_destination: true</tt> - 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].
#
Expand Down Expand Up @@ -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:
#
# - <tt>dereference: false</tt> - if +src+ is a symbolic link,
# does not follow the link.
# - <tt>preserve: true</tt> - preserves file times.
# - <tt>remove_destination: true</tt> - 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].
#
Expand Down Expand Up @@ -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)
Expand Down
71 changes: 56 additions & 15 deletions test/fileutils/test_fileutils.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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?

Expand Down Expand Up @@ -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
Expand Down