diff --git a/lib/fileutils.rb b/lib/fileutils.rb index d07c608..a0d666b 100644 --- a/lib/fileutils.rb +++ b/lib/fileutils.rb @@ -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 @@ -2504,7 +2508,11 @@ 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 @@ -2512,14 +2520,14 @@ def fu_split_path(path) #:nodoc: 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 == "." @@ -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 << "/" diff --git a/test/fileutils/test_fileutils.rb b/test/fileutils/test_fileutils.rb index c9d8d0c..500c47b 100644 --- a/test/fileutils/test_fileutils.rb +++ b/test/fileutils/test_fileutils.rb @@ -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 @@ -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