summaryrefslogtreecommitdiff
path: root/lib/fileutils.rb
diff options
context:
space:
mode:
Diffstat (limited to 'lib/fileutils.rb')
-rw-r--r--lib/fileutils.rb206
1 files changed, 144 insertions, 62 deletions
diff --git a/lib/fileutils.rb b/lib/fileutils.rb
index 74bb904e28..e8cc355760 100644
--- a/lib/fileutils.rb
+++ b/lib/fileutils.rb
@@ -3,7 +3,7 @@
begin
require 'rbconfig'
rescue LoadError
- # for make mjit-headers
+ # for make rjit-headers
end
# Namespace for file utility methods for copying, moving, removing, etc.
@@ -12,8 +12,8 @@ end
#
# First, what’s elsewhere. \Module \FileUtils:
#
-# - Inherits from {class Object}[https://docs.ruby-lang.org/en/master/Object.html].
-# - Supplements {class File}[https://docs.ruby-lang.org/en/master/File.html]
+# - Inherits from {class Object}[rdoc-ref:Object].
+# - Supplements {class File}[rdoc-ref:File]
# (but is not included or extended there).
#
# Here, module \FileUtils provides methods that are useful for:
@@ -36,6 +36,7 @@ end
# - ::ln, ::link: Creates hard links.
# - ::ln_s, ::symlink: Creates symbolic links.
# - ::ln_sf: Creates symbolic links, overwriting if necessary.
+# - ::ln_sr: Creates symbolic links relative to targets
#
# === Deleting
#
@@ -162,8 +163,8 @@ end
# by applying a special pre-process:
#
# - If the target path points to a directory, this method uses methods
-# {File#chown}[https://docs.ruby-lang.org/en/master/File.html#method-i-chown]
-# and {File#chmod}[https://docs.ruby-lang.org/en/master/File.html#method-i-chmod]
+# {File#chown}[rdoc-ref:File#chown]
+# and {File#chmod}[rdoc-ref:File#chmod]
# in removing directories.
# - The owner of the target directory should be either the current process
# or the super user (root).
@@ -179,7 +180,8 @@ end
# - {CVE-2004-0452}[https://cve.mitre.org/cgi-bin/cvename.cgi?name=CAN-2004-0452].
#
module FileUtils
- VERSION = "1.6.0"
+ # The version number.
+ VERSION = "1.7.2"
def self.private_module_function(name) #:nodoc:
module_function name
@@ -191,8 +193,6 @@ module FileUtils
#
# FileUtils.pwd # => "/rdoc/fileutils"
#
- # FileUtils.getwd is an alias for FileUtils.pwd.
- #
# Related: FileUtils.cd.
#
def pwd
@@ -234,8 +234,6 @@ module FileUtils
# cd ..
# cd fileutils
#
- # FileUtils.chdir is an alias for FileUtils.cd.
- #
# Related: FileUtils.pwd.
#
def cd(dir, verbose: nil, &block) # :yield: dir
@@ -291,7 +289,7 @@ module FileUtils
#
# With no keyword arguments, creates a directory at each +path+ in +list+
# by calling: <tt>Dir.mkdir(path, mode)</tt>;
- # see {Dir.mkdir}[https://docs.ruby-lang.org/en/master/Dir.html#method-c-mkdir]:
+ # see {Dir.mkdir}[rdoc-ref:Dir.mkdir]:
#
# FileUtils.mkdir(%w[tmp0 tmp1]) # => ["tmp0", "tmp1"]
# FileUtils.mkdir('tmp4') # => ["tmp4"]
@@ -299,7 +297,7 @@ module FileUtils
# Keyword arguments:
#
# - <tt>mode: <i>mode</i></tt> - also calls <tt>File.chmod(mode, path)</tt>;
- # see {File.chmod}[https://docs.ruby-lang.org/en/master/File.html#method-c-chmod].
+ # see {File.chmod}[rdoc-ref:File.chmod].
# - <tt>noop: true</tt> - does not create directories.
# - <tt>verbose: true</tt> - prints an equivalent command:
#
@@ -339,7 +337,7 @@ module FileUtils
# With no keyword arguments, creates a directory at each +path+ in +list+,
# along with any needed ancestor directories,
# by calling: <tt>Dir.mkdir(path, mode)</tt>;
- # see {Dir.mkdir}[https://docs.ruby-lang.org/en/master/Dir.html#method-c-mkdir]:
+ # see {Dir.mkdir}[rdoc-ref:Dir.mkdir]:
#
# FileUtils.mkdir_p(%w[tmp0/tmp1 tmp2/tmp3]) # => ["tmp0/tmp1", "tmp2/tmp3"]
# FileUtils.mkdir_p('tmp4/tmp5') # => ["tmp4/tmp5"]
@@ -347,7 +345,7 @@ module FileUtils
# Keyword arguments:
#
# - <tt>mode: <i>mode</i></tt> - also calls <tt>File.chmod(mode, path)</tt>;
- # see {File.chmod}[https://docs.ruby-lang.org/en/master/File.html#method-c-chmod].
+ # see {File.chmod}[rdoc-ref:File.chmod].
# - <tt>noop: true</tt> - does not create directories.
# - <tt>verbose: true</tt> - prints an equivalent command:
#
@@ -417,7 +415,7 @@ module FileUtils
#
# With no keyword arguments, removes the directory at each +path+ in +list+,
# by calling: <tt>Dir.rmdir(path)</tt>;
- # see {Dir.rmdir}[https://docs.ruby-lang.org/en/master/Dir.html#method-c-rmdir]:
+ # see {Dir.rmdir}[rdoc-ref:Dir.rmdir]:
#
# FileUtils.rmdir(%w[tmp0/tmp1 tmp2/tmp3]) # => ["tmp0/tmp1", "tmp2/tmp3"]
# FileUtils.rmdir('tmp4/tmp5') # => ["tmp4/tmp5"]
@@ -514,8 +512,6 @@ module FileUtils
# Raises an exception if +dest+ is the path to an existing file
# and keyword argument +force+ is not +true+.
#
- # FileUtils#link is an alias for FileUtils#ln.
- #
# Related: FileUtils.link_entry (has different options).
#
def ln(src, dest, force: nil, noop: nil, verbose: nil)
@@ -690,6 +686,7 @@ module FileUtils
# Keyword arguments:
#
# - <tt>force: true</tt> - overwrites +dest+ if it exists.
+ # - <tt>relative: false</tt> - create links relative to +dest+.
# - <tt>noop: true</tt> - does not create links.
# - <tt>verbose: true</tt> - prints an equivalent command:
#
@@ -705,11 +702,12 @@ module FileUtils
# ln -sf src2.txt dest2.txt
# ln -s srcdir3/src0.txt srcdir3/src1.txt destdir3
#
- # FileUtils.symlink is an alias for FileUtils.ln_s.
- #
# Related: FileUtils.ln_sf.
#
- def ln_s(src, dest, force: nil, noop: nil, verbose: nil)
+ def ln_s(src, dest, force: nil, relative: false, target_directory: true, noop: nil, verbose: nil)
+ 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
return if noop
fu_each_src_dest0(src, dest) do |s,d|
@@ -729,6 +727,48 @@ module FileUtils
end
module_function :ln_sf
+ # 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)
+ 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)
+ s = File.join(*base)
+ 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)
+ 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
+
# Creates {hard links}[https://en.wikipedia.org/wiki/Hard_link]; returns +nil+.
#
# Arguments +src+ and +dest+
@@ -829,8 +869,6 @@ module FileUtils
#
# Raises an exception if +src+ is a directory.
#
- # FileUtils.copy is an alias for FileUtils.cp.
- #
# Related: {methods for copying}[rdoc-ref:FileUtils@Copying].
#
def cp(src, dest, preserve: nil, noop: nil, verbose: nil)
@@ -1044,7 +1082,7 @@ module FileUtils
module_function :copy_file
# Copies \IO stream +src+ to \IO stream +dest+ via
- # {IO.copy_stream}[https://docs.ruby-lang.org/en/master/IO.html#method-c-copy_stream].
+ # {IO.copy_stream}[rdoc-ref:IO.copy_stream].
#
# Related: {methods for copying}[rdoc-ref:FileUtils@Copying].
#
@@ -1117,8 +1155,6 @@ module FileUtils
# mv src0 dest0
# mv src1.txt src1 dest1
#
- # FileUtils.move is an alias for FileUtils.mv.
- #
def mv(src, dest, force: nil, noop: nil, verbose: nil, secure: nil)
fu_output_message "mv#{force ? ' -f' : ''} #{[src,dest].flatten.join ' '}" if verbose
return if noop
@@ -1165,7 +1201,7 @@ module FileUtils
#
# Keyword arguments:
#
- # - <tt>force: true</tt> - ignores raised exceptions of Errno::ENOENT
+ # - <tt>force: true</tt> - ignores raised exceptions of StandardError
# and its descendants.
# - <tt>noop: true</tt> - does not remove files; returns +nil+.
# - <tt>verbose: true</tt> - prints an equivalent command:
@@ -1176,8 +1212,6 @@ module FileUtils
#
# rm src0.dat src0.txt
#
- # FileUtils.remove is an alias for FileUtils.rm.
- #
# Related: {methods for deleting}[rdoc-ref:FileUtils@Deleting].
#
def rm(list, force: nil, noop: nil, verbose: nil)
@@ -1203,8 +1237,6 @@ module FileUtils
#
# See FileUtils.rm for keyword arguments.
#
- # FileUtils.safe_unlink is an alias for FileUtils.rm_f.
- #
# Related: {methods for deleting}[rdoc-ref:FileUtils@Deleting].
#
def rm_f(list, noop: nil, verbose: nil)
@@ -1248,7 +1280,7 @@ module FileUtils
#
# Keyword arguments:
#
- # - <tt>force: true</tt> - ignores raised exceptions of Errno::ENOENT
+ # - <tt>force: true</tt> - ignores raised exceptions of StandardError
# and its descendants.
# - <tt>noop: true</tt> - does not remove entries; returns +nil+.
# - <tt>secure: true</tt> - removes +src+ securely;
@@ -1292,8 +1324,6 @@ module FileUtils
#
# See FileUtils.rm_r for keyword arguments.
#
- # FileUtils.rmtree is an alias for FileUtils.rm_rf.
- #
# Related: {methods for deleting}[rdoc-ref:FileUtils@Deleting].
#
def rm_rf(list, noop: nil, verbose: nil, secure: nil)
@@ -1315,7 +1345,7 @@ module FileUtils
# see {Avoiding the TOCTTOU Vulnerability}[rdoc-ref:FileUtils@Avoiding+the+TOCTTOU+Vulnerability].
#
# Optional argument +force+ specifies whether to ignore
- # raised exceptions of Errno::ENOENT and its descendants.
+ # raised exceptions of StandardError and its descendants.
#
# Related: {methods for deleting}[rdoc-ref:FileUtils@Deleting].
#
@@ -1384,12 +1414,10 @@ module FileUtils
ent.remove
rescue
raise unless force
- raise unless Errno::ENOENT === $!
end
end
rescue
raise unless force
- raise unless Errno::ENOENT === $!
end
module_function :remove_entry_secure
@@ -1415,7 +1443,7 @@ module FileUtils
# should be {interpretable as a path}[rdoc-ref:FileUtils@Path+Arguments].
#
# Optional argument +force+ specifies whether to ignore
- # raised exceptions of Errno::ENOENT and its descendants.
+ # raised exceptions of StandardError and its descendants.
#
# Related: FileUtils.remove_entry_secure.
#
@@ -1425,12 +1453,10 @@ module FileUtils
ent.remove
rescue
raise unless force
- raise unless Errno::ENOENT === $!
end
end
rescue
raise unless force
- raise unless Errno::ENOENT === $!
end
module_function :remove_entry
@@ -1441,7 +1467,7 @@ module FileUtils
# should be {interpretable as a path}[rdoc-ref:FileUtils@Path+Arguments].
#
# Optional argument +force+ specifies whether to ignore
- # raised exceptions of Errno::ENOENT and its descendants.
+ # raised exceptions of StandardError and its descendants.
#
# Related: {methods for deleting}[rdoc-ref:FileUtils@Deleting].
#
@@ -1449,7 +1475,6 @@ module FileUtils
Entry_.new(path).remove_file
rescue
raise unless force
- raise unless Errno::ENOENT === $!
end
module_function :remove_file
@@ -1461,7 +1486,7 @@ module FileUtils
# should be {interpretable as a path}[rdoc-ref:FileUtils@Path+Arguments].
#
# Optional argument +force+ specifies whether to ignore
- # raised exceptions of Errno::ENOENT and its descendants.
+ # raised exceptions of StandardError and its descendants.
#
# Related: {methods for deleting}[rdoc-ref:FileUtils@Deleting].
#
@@ -1560,14 +1585,14 @@ module FileUtils
# Keyword arguments:
#
# - <tt>group: <i>group</i></tt> - changes the group if not +nil+,
- # using {File.chown}[https://docs.ruby-lang.org/en/master/File.html#method-c-chown].
+ # using {File.chown}[rdoc-ref:File.chown].
# - <tt>mode: <i>permissions</i></tt> - changes the permissions.
- # using {File.chmod}[https://docs.ruby-lang.org/en/master/File.html#method-c-chmod].
+ # using {File.chmod}[rdoc-ref:File.chmod].
# - <tt>noop: true</tt> - does not copy entries; returns +nil+.
# - <tt>owner: <i>owner</i></tt> - changes the owner if not +nil+,
- # using {File.chown}[https://docs.ruby-lang.org/en/master/File.html#method-c-chown].
+ # using {File.chown}[rdoc-ref:File.chown].
# - <tt>preserve: true</tt> - preserve timestamps
- # using {File.utime}[https://docs.ruby-lang.org/en/master/File.html#method-c-utime].
+ # using {File.utime}[rdoc-ref:File.utime].
# - <tt>verbose: true</tt> - prints an equivalent command:
#
# FileUtils.install('src0.txt', 'dest0.txt', noop: true, verbose: true)
@@ -1600,7 +1625,13 @@ module FileUtils
st = File.stat(s)
unless File.exist?(d) and compare_file(s, d)
remove_file d, true
- copy_file s, d
+ if d.end_with?('/')
+ mkdir_p d
+ copy_file s, d + File.basename(s)
+ else
+ mkdir_p File.expand_path('..', d)
+ copy_file s, d
+ end
File.utime st.atime, st.mtime, d if preserve
File.chmod fu_mode(mode, st), d if mode
File.chown uid, gid, d if uid or gid
@@ -1621,7 +1652,7 @@ module FileUtils
when "a"
mask | 07777
else
- raise ArgumentError, "invalid `who' symbol in file mode: #{chr}"
+ raise ArgumentError, "invalid 'who' symbol in file mode: #{chr}"
end
end
end
@@ -1675,7 +1706,7 @@ module FileUtils
copy_mask = user_mask(chr)
(current_mode & copy_mask) / (copy_mask & 0111) * (user_mask & 0111)
else
- raise ArgumentError, "invalid `perm' symbol in file mode: #{chr}"
+ raise ArgumentError, "invalid 'perm' symbol in file mode: #{chr}"
end
end
@@ -1704,9 +1735,9 @@ module FileUtils
# returns +list+ if it is an array, <tt>[list]</tt> otherwise:
#
# - Modifies each entry that is a regular file using
- # {File.chmod}[https://docs.ruby-lang.org/en/master/File.html#method-c-chmod].
+ # {File.chmod}[rdoc-ref:File.chmod].
# - Modifies each entry that is a symbolic link using
- # {File.lchmod}[https://docs.ruby-lang.org/en/master/File.html#method-c-lchmod].
+ # {File.lchmod}[rdoc-ref:File.lchmod].
#
# Argument +list+ or its elements
# should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments].
@@ -1806,9 +1837,9 @@ module FileUtils
# returns +list+ if it is an array, <tt>[list]</tt> otherwise:
#
# - Modifies each entry that is a regular file using
- # {File.chown}[https://docs.ruby-lang.org/en/master/File.html#method-c-chown].
+ # {File.chown}[rdoc-ref:File.chown].
# - Modifies each entry that is a symbolic link using
- # {File.lchown}[https://docs.ruby-lang.org/en/master/File.html#method-c-lchown].
+ # {File.lchown}[rdoc-ref:File.lchown].
#
# Argument +list+ or its elements
# should be {interpretable as paths}[rdoc-ref:FileUtils@Path+Arguments].
@@ -1998,21 +2029,22 @@ module FileUtils
private
- module StreamUtils_
+ module StreamUtils_ # :nodoc:
+
private
case (defined?(::RbConfig) ? ::RbConfig::CONFIG['host_os'] : ::RUBY_PLATFORM)
when /mswin|mingw/
- def fu_windows?; true end
+ def fu_windows?; true end #:nodoc:
else
- def fu_windows?; false end
+ def fu_windows?; false end #:nodoc:
end
def fu_copy_stream0(src, dest, blksize = nil) #:nodoc:
IO.copy_stream(src, dest)
end
- def fu_stream_blksize(*streams)
+ def fu_stream_blksize(*streams) #:nodoc:
streams.each do |s|
next unless s.respond_to?(:stat)
size = fu_blksize(s.stat)
@@ -2021,14 +2053,14 @@ module FileUtils
fu_default_blksize()
end
- def fu_blksize(st)
+ def fu_blksize(st) #:nodoc:
s = st.blksize
return nil unless s
return nil if s == 0
s
end
- def fu_default_blksize
+ def fu_default_blksize #:nodoc:
1024
end
end
@@ -2441,15 +2473,15 @@ module FileUtils
end
private_module_function :fu_each_src_dest
- def fu_each_src_dest0(src, dest) #:nodoc:
+ def fu_each_src_dest0(src, dest, target_directory = true) #:nodoc:
if tmp = Array.try_convert(src)
tmp.each do |s|
s = File.path(s)
- yield s, File.join(dest, File.basename(s))
+ yield s, (target_directory ? File.join(dest, File.basename(s)) : dest)
end
else
src = File.path(src)
- if File.directory?(dest)
+ if target_directory and File.directory?(dest)
yield src, File.join(dest, File.basename(src))
else
yield src, File.path(dest)
@@ -2473,6 +2505,56 @@ module FileUtils
end
private_module_function :fu_output_message
+ def fu_split_path(path) #:nodoc:
+ 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
+ private_module_function :fu_split_path
+
+ 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
+ private_module_function :fu_relative_components_from
+
+ def fu_clean_components(*comp) #:nodoc:
+ 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
+ path.chomp!(%r((?<=\A|/)[^/]+/\z), "")
+ else
+ clean << c
+ path << c << "/"
+ end
+ end
+ clean
+ end
+ private_module_function :fu_clean_components
+
+ if fu_windows?
+ def fu_starting_path?(path) #:nodoc:
+ path&.start_with?(%r(\w:|/))
+ end
+ else
+ def fu_starting_path?(path) #:nodoc:
+ path&.start_with?("/")
+ end
+ end
+ private_module_function :fu_starting_path?
+
# This hash table holds command options.
OPT_TABLE = {} #:nodoc: internal use only
(private_instance_methods & methods(false)).inject(OPT_TABLE) {|tbl, name|