2022-07-14 05:59:46 +03:00
|
|
|
#!/usr/bin/ruby
|
|
|
|
|
2022-07-16 10:37:26 +03:00
|
|
|
target_directory = true
|
|
|
|
noop = false
|
|
|
|
force = false
|
2022-09-20 06:54:08 +03:00
|
|
|
quiet = false
|
2022-07-16 10:37:26 +03:00
|
|
|
|
2022-07-14 05:59:46 +03:00
|
|
|
until ARGV.empty?
|
|
|
|
case ARGV[0]
|
|
|
|
when '-n'
|
|
|
|
noop = true
|
|
|
|
when '-f'
|
|
|
|
force = true
|
2022-07-16 10:37:26 +03:00
|
|
|
when '-T'
|
|
|
|
target_directory = false
|
2022-09-20 06:54:08 +03:00
|
|
|
when '-q'
|
|
|
|
quiet = true
|
2022-07-14 05:59:46 +03:00
|
|
|
else
|
|
|
|
break
|
|
|
|
end
|
|
|
|
ARGV.shift
|
|
|
|
end
|
|
|
|
|
|
|
|
unless ARGV.size == 2
|
|
|
|
abort "usage: #{$0} src destdir"
|
|
|
|
end
|
|
|
|
src, dest = ARGV
|
|
|
|
|
|
|
|
require 'fileutils'
|
|
|
|
|
|
|
|
include FileUtils
|
|
|
|
unless respond_to?(:ln_sr)
|
2022-07-16 10:37:26 +03:00
|
|
|
def ln_sr(src, dest, target_directory: true, force: nil, noop: nil, verbose: nil)
|
|
|
|
options = "#{force ? 'f' : ''}#{target_directory ? '' : 'T'}"
|
2022-07-14 05:59:46 +03:00
|
|
|
dest = File.path(dest)
|
2022-07-16 11:58:52 +03:00
|
|
|
srcs = Array(src)
|
2022-07-16 10:37:26 +03:00
|
|
|
link = proc do |s, target_dir_p = true|
|
2022-07-14 05:59:46 +03:00
|
|
|
s = File.path(s)
|
2022-07-16 11:58:52 +03:00
|
|
|
if target_dir_p
|
|
|
|
d = File.join(destdirs = dest, File.basename(s))
|
|
|
|
else
|
|
|
|
destdirs = File.dirname(d = dest)
|
|
|
|
end
|
|
|
|
destdirs = fu_split_path(File.realpath(destdirs))
|
2022-07-14 05:59:46 +03:00
|
|
|
if fu_starting_path?(s)
|
|
|
|
srcdirs = fu_split_path((File.realdirpath(s) rescue File.expand_path(s)))
|
2022-07-16 11:58:52 +03:00
|
|
|
base = fu_relative_components_from(srcdirs, destdirs)
|
|
|
|
s = File.join(*base)
|
2022-07-14 05:59:46 +03:00
|
|
|
else
|
|
|
|
srcdirs = fu_clean_components(*fu_split_path(s))
|
2022-07-16 11:58:52 +03:00
|
|
|
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)
|
2022-07-14 05:59:46 +03:00
|
|
|
end
|
2022-07-16 10:37:26 +03:00
|
|
|
fu_output_message "ln -s#{options} #{s} #{d}" if verbose
|
2022-07-14 05:59:46 +03:00
|
|
|
next if noop
|
|
|
|
remove_file d, true if force
|
|
|
|
File.symlink s, d
|
|
|
|
end
|
|
|
|
case srcs.size
|
|
|
|
when 0
|
|
|
|
when 1
|
2022-07-16 10:37:26 +03:00
|
|
|
link[srcs[0], target_directory && File.directory?(dest)]
|
2022-07-14 05:59:46 +03:00
|
|
|
else
|
|
|
|
srcs.each(&link)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def fu_split_path(path)
|
|
|
|
path = File.path(path)
|
|
|
|
list = []
|
|
|
|
until (parent, base = File.split(path); parent == path or parent == ".")
|
|
|
|
list << base
|
|
|
|
path = parent
|
|
|
|
end
|
|
|
|
list << path
|
|
|
|
list.reverse!
|
|
|
|
end
|
|
|
|
|
|
|
|
def fu_relative_components_from(target, base) #:nodoc:
|
|
|
|
i = 0
|
|
|
|
while target[i]&.== base[i]
|
|
|
|
i += 1
|
|
|
|
end
|
|
|
|
Array.new(base.size-i, '..').concat(target[i..-1])
|
|
|
|
end
|
|
|
|
|
|
|
|
def fu_clean_components(*comp)
|
|
|
|
comp.shift while comp.first == "."
|
|
|
|
return comp if comp.empty?
|
|
|
|
clean = [comp.shift]
|
|
|
|
path = File.join(*clean, "") # ending with File::SEPARATOR
|
|
|
|
while c = comp.shift
|
|
|
|
if c == ".." and clean.last != ".." and !(fu_have_symlink? && File.symlink?(path))
|
|
|
|
clean.pop
|
2023-12-09 03:39:41 +03:00
|
|
|
path.sub!(%r((?<=\A|/)[^/]+/\z), "")
|
2022-07-14 05:59:46 +03:00
|
|
|
else
|
|
|
|
clean << c
|
|
|
|
path << c << "/"
|
|
|
|
end
|
|
|
|
end
|
|
|
|
clean
|
|
|
|
end
|
|
|
|
|
|
|
|
if fu_windows?
|
|
|
|
def fu_starting_path?(path)
|
|
|
|
path&.start_with?(%r(\w:|/))
|
|
|
|
end
|
|
|
|
else
|
|
|
|
def fu_starting_path?(path)
|
|
|
|
path&.start_with?("/")
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
if File.respond_to?(:symlink)
|
2022-09-20 06:54:08 +03:00
|
|
|
if quiet and File.identical?(src, dest)
|
|
|
|
exit
|
|
|
|
end
|
2022-07-14 05:59:46 +03:00
|
|
|
begin
|
2022-07-16 10:37:26 +03:00
|
|
|
ln_sr(src, dest, verbose: true, target_directory: target_directory, force: force, noop: noop)
|
2022-09-09 05:35:47 +03:00
|
|
|
rescue NotImplementedError, Errno::EPERM, Errno::EACCES
|
2022-07-14 05:59:46 +03:00
|
|
|
else
|
|
|
|
exit
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
cp_r(src, dest)
|