diff options
Diffstat (limited to 'lib/fileutils.rb')
-rw-r--r-- | lib/fileutils.rb | 206 |
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| |