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
56 changes: 32 additions & 24 deletions lib/fileutils.rb
Original file line number Diff line number Diff line change
Expand Up @@ -731,30 +731,34 @@ 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)
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
cmd = "ln -s#{force ? 'f' : ''}#{target_directory ? '' : 'T'}" if verbose
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
parent = File.dirname(d)
destdirs = fu_split_path(parent)
real_ddirs = fu_split_path(File.realpath(parent))
else
destdirs ||= fu_split_path(dest)
real_ddirs ||= fu_split_path(File.realpath(dest))
end
if fu_starting_path?(s)
srcdirs = fu_split_path((File.realdirpath(s) rescue File.expand_path(s)))
base = fu_relative_components_from(srcdirs, destdirs)
s = File.join(*base)
srcdirs = fu_split_path(s)
i = fu_common_components(srcdirs, destdirs)
n = destdirs.size - i
n -= 1 unless target_directory
link1 = fu_clean_components(*Array.new([n, 0].max, '..'), *srcdirs[i..-1])
begin
real_sdirs = fu_split_path(File.realdirpath(s)) rescue nil
rescue
else
srcdirs = fu_clean_components(*fu_split_path(s))
base = fu_relative_components_from(fu_split_path(Dir.pwd), destdirs)
while srcdirs.first&. == ".." and base.last&.!=("..") and !fu_starting_path?(base.last)
srcdirs.shift
base.pop
end
s = File.join(*base, *srcdirs)
i = fu_common_components(real_sdirs, real_ddirs)
n = real_ddirs.size - i
n -= 1 unless target_directory
link2 = fu_clean_components(*Array.new([n, 0].max, '..'), *real_sdirs[i..-1])
link1 = link2 if link1.size > link2.size
end
s = File.join(link1)
fu_output_message [cmd, s, d].flatten.join(' ') if verbose
next if noop
remove_file d, true if force
File.symlink s, d
end
Expand Down Expand Up @@ -2504,22 +2508,26 @@ def fu_split_path(path) #:nodoc:
path = File.path(path)
list = []
until (parent, base = File.split(path); parent == path or parent == ".")
list << base
if base != '..' and list.last == '..' and !(fu_have_symlink? && File.symlink?(path))
list.pop
else
list << base
end
path = parent
end
list << path
list.reverse!
end
private_module_function :fu_split_path

def fu_relative_components_from(target, base) #:nodoc:
def fu_common_components(target, base) #:nodoc:
i = 0
while target[i]&.== base[i]
i += 1
end
Array.new(base.size-i, '..').concat(target[i..-1])
i
end
private_module_function :fu_relative_components_from
private_module_function :fu_common_components

def fu_clean_components(*comp) #:nodoc:
comp.shift while comp.first == "."
Expand All @@ -2529,7 +2537,7 @@ def fu_clean_components(*comp) #:nodoc:
while c = comp.shift
if c == ".." and clean.last != ".." and !(fu_have_symlink? && File.symlink?(path))
clean.pop
path.chomp!(%r((?<=\A|/)[^/]+/\z), "")
path.sub!(%r((?<=\A|/)[^/]+/\z), "")
else
clean << c
path << c << "/"
Expand Down
12 changes: 10 additions & 2 deletions test/fileutils/test_fileutils.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1031,7 +1031,8 @@ def test_ln_sr
assert_all_assertions_foreach(nil, *TARGETS) do |fname|
lnfname = 'tmp/lnsdest'
ln_sr fname, lnfname
assert FileTest.symlink?(lnfname), 'not symlink'
assert_file.symlink?(lnfname)
assert_file.identical?(lnfname, fname)
assert_equal "../#{fname}", File.readlink(lnfname)
ensure
rm_f lnfname
Expand All @@ -1048,12 +1049,19 @@ def test_ln_sr
end
end

File.symlink 'data', 'link'
mkdir 'link/d1'
mkdir 'link/d2'
ln_sr 'link/d1/z', 'link/d2'
assert_equal '../d1/z', File.readlink('data/d2/z')

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_file.symlink?('data/xxx')
assert_equal 'ok', File.read('data/xxx')
assert_equal 'src/xxx', File.readlink('data/xxx')
end

def test_ln_sr_not_target_directory
Expand Down
Loading