Skip to content
Merged
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
167 changes: 93 additions & 74 deletions lib/fileutils.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,46 +24,56 @@
#
# require 'fileutils'
#
# FileUtils.cd(dir, options)
# FileUtils.cd(dir, options) {|dir| block }
# FileUtils.cd(dir, **options)
# FileUtils.cd(dir, **options) {|dir| block }
# FileUtils.pwd()
# FileUtils.mkdir(dir, options)
# FileUtils.mkdir(list, options)
# FileUtils.mkdir_p(dir, options)
# FileUtils.mkdir_p(list, options)
# FileUtils.rmdir(dir, options)
# FileUtils.rmdir(list, options)
# FileUtils.ln(target, link, options)
# FileUtils.ln(targets, dir, options)
# FileUtils.ln_s(target, link, options)
# FileUtils.ln_s(targets, dir, options)
# FileUtils.ln_sf(target, link, options)
# FileUtils.cp(src, dest, options)
# FileUtils.cp(list, dir, options)
# FileUtils.cp_r(src, dest, options)
# FileUtils.cp_r(list, dir, options)
# FileUtils.mv(src, dest, options)
# FileUtils.mv(list, dir, options)
# FileUtils.rm(list, options)
# FileUtils.rm_r(list, options)
# FileUtils.rm_rf(list, options)
# FileUtils.install(src, dest, options)
# FileUtils.chmod(mode, list, options)
# FileUtils.chmod_R(mode, list, options)
# FileUtils.chown(user, group, list, options)
# FileUtils.chown_R(user, group, list, options)
# FileUtils.touch(list, options)
# FileUtils.mkdir(dir, **options)
# FileUtils.mkdir(list, **options)
# FileUtils.mkdir_p(dir, **options)
# FileUtils.mkdir_p(list, **options)
# FileUtils.rmdir(dir, **options)
# FileUtils.rmdir(list, **options)
# FileUtils.ln(target, link, **options)
# FileUtils.ln(targets, dir, **options)
# FileUtils.ln_s(target, link, **options)
# FileUtils.ln_s(targets, dir, **options)
# FileUtils.ln_sf(target, link, **options)
# FileUtils.cp(src, dest, **options)
# FileUtils.cp(list, dir, **options)
# FileUtils.cp_r(src, dest, **options)
# FileUtils.cp_r(list, dir, **options)
# FileUtils.mv(src, dest, **options)
# FileUtils.mv(list, dir, **options)
# FileUtils.rm(list, **options)
# FileUtils.rm_r(list, **options)
# FileUtils.rm_rf(list, **options)
# FileUtils.install(src, dest, **options)
# FileUtils.chmod(mode, list, **options)
# FileUtils.chmod_R(mode, list, **options)
# FileUtils.chown(user, group, list, **options)
# FileUtils.chown_R(user, group, list, **options)
# FileUtils.touch(list, **options)
#
# The <tt>options</tt> parameter is a hash of options, taken from the list
# <tt>:force</tt>, <tt>:noop</tt>, <tt>:preserve</tt>, and <tt>:verbose</tt>.
# <tt>:noop</tt> means that no changes are made. The other three are obvious.
# Each method documents the options that it honours.
# Possible <tt>options</tt> are:
#
# <tt>:force</tt> :: forced operation (rewrite files if exist, remove
# directories if not empty, etc.);
# <tt>:verbose</tt> :: print command to be run, in bash syntax, before
# performing it;
# <tt>:preserve</tt> :: preserve object's group, user and modification
# time on copying;
# <tt>:noop</tt> :: no changes are made (usable in combination with
# <tt>:verbose</tt> which will print the command to run)
#
# Each method documents the options that it honours. See also ::commands,
# ::options and ::options_of methods to introspect which command have which
# options.
#
# All methods that have the concept of a "source" file or directory can take
# either one file or a list of files in that argument. See the method
# documentation for examples.
#
# There are some `low level' methods, which do not accept any option:
# There are some `low level' methods, which do not accept keyword arguments:
#
# FileUtils.copy_entry(src, dest, preserve = false, dereference_root = false, remove_destination = false)
# FileUtils.copy_file(src, dest, preserve = false, dereference = true)
Expand Down Expand Up @@ -119,7 +129,7 @@ def pwd
#
# FileUtils.cd('/') # change directory
#
# FileUtils.cd('/', :verbose => true) # change directory and report it
# FileUtils.cd('/', verbose: true) # change directory and report it
#
# FileUtils.cd('/') do # change directory
# # ... # do something
Expand Down Expand Up @@ -164,9 +174,9 @@ def remove_trailing_slash(dir) #:nodoc:
# Creates one or more directories.
#
# FileUtils.mkdir 'test'
# FileUtils.mkdir %w( tmp data )
# FileUtils.mkdir 'notexist', :noop => true # Does not really create.
# FileUtils.mkdir 'tmp', :mode => 0700
# FileUtils.mkdir %w(tmp data)
# FileUtils.mkdir 'notexist', noop: true # Does not really create.
# FileUtils.mkdir 'tmp', mode: 0700
#
def mkdir(list, mode: nil, noop: nil, verbose: nil)
list = fu_list(list)
Expand All @@ -185,7 +195,7 @@ def mkdir(list, mode: nil, noop: nil, verbose: nil)
#
# FileUtils.mkdir_p '/usr/local/lib/ruby'
#
# causes to make following directories, if it does not exist.
# causes to make following directories, if they do not exist.
#
# * /usr
# * /usr/local
Expand Down Expand Up @@ -249,7 +259,7 @@ def fu_mkdir(path, mode) #:nodoc:
# FileUtils.rmdir 'somedir'
# FileUtils.rmdir %w(somedir anydir otherdir)
# # Does not really remove directory; outputs message.
# FileUtils.rmdir 'somedir', :verbose => true, :noop => true
# FileUtils.rmdir 'somedir', verbose: true, noop: true
#
def rmdir(list, parents: nil, noop: nil, verbose: nil)
list = fu_list(list)
Expand Down Expand Up @@ -278,7 +288,7 @@ def rmdir(list, parents: nil, noop: nil, verbose: nil)
#
# In the first form, creates a hard link +link+ which points to +target+.
# If +link+ already exists, raises Errno::EEXIST.
# But if the :force option is set, overwrites +link+.
# But if the +force+ option is set, overwrites +link+.
#
# FileUtils.ln 'gcc', 'cc', verbose: true
# FileUtils.ln '/usr/bin/emacs21', '/usr/bin/emacs'
Expand All @@ -304,23 +314,23 @@ def ln(src, dest, force: nil, noop: nil, verbose: nil)
alias link ln
module_function :link

#
# :call-seq:
# FileUtils.cp_lr(src, dest, noop: nil, verbose: nil, dereference_root: true, remove_destination: false)
#
# Hard link +src+ to +dest+. If +src+ is a directory, this method links
# all its contents recursively. If +dest+ is a directory, links
# +src+ to +dest/src+.
#
# +src+ can be a list of files.
#
# # Installing the library "mylib" under the site_ruby directory.
# FileUtils.rm_r site_ruby + '/mylib', :force => true
# If +dereference_root+ is true, this method dereference tree root.
#
# If +remove_destination+ is true, this method removes each destination file before copy.
#
# FileUtils.rm_r site_ruby + '/mylib', force: true
# FileUtils.cp_lr 'lib/', site_ruby + '/mylib'
#
# # Examples of linking several files to target directory.
# FileUtils.cp_lr %w(mail.rb field.rb debug/), site_ruby + '/tmail'
# FileUtils.cp_lr Dir.glob('*.rb'), '/home/aamine/lib/ruby', :noop => true, :verbose => true
# FileUtils.cp_lr Dir.glob('*.rb'), '/home/aamine/lib/ruby', noop: true, verbose: true
#
# # If you want to link all contents of a directory instead of the
# # directory itself, c.f. src/x -> dest/x, src/y -> dest/y,
Expand All @@ -345,7 +355,7 @@ def cp_lr(src, dest, noop: nil, verbose: nil,
#
# In the first form, creates a symbolic link +link+ which points to +target+.
# If +link+ already exists, raises Errno::EEXIST.
# But if the :force option is set, overwrites +link+.
# But if the <tt>force</tt> option is set, overwrites +link+.
#
# FileUtils.ln_s '/usr/bin/ruby', '/usr/local/bin/ruby'
# FileUtils.ln_s 'verylongsourcefilename.c', 'c', force: true
Expand Down Expand Up @@ -411,7 +421,7 @@ def link_entry(src, dest, dereference_root = false, remove_destination = false)
#
# FileUtils.cp 'eval.c', 'eval.c.org'
# FileUtils.cp %w(cgi.rb complex.rb date.rb), '/usr/lib/ruby/1.6'
# FileUtils.cp %w(cgi.rb complex.rb date.rb), '/usr/lib/ruby/1.6', :verbose => true
# FileUtils.cp %w(cgi.rb complex.rb date.rb), '/usr/lib/ruby/1.6', verbose: true
# FileUtils.cp 'symlink', 'dest' # copy content, "dest" is not a symlink
#
def cp(src, dest, preserve: nil, noop: nil, verbose: nil)
Expand All @@ -433,13 +443,17 @@ def cp(src, dest, preserve: nil, noop: nil, verbose: nil)
#
# +src+ can be a list of files.
#
# If +dereference_root+ is true, this method dereference tree root.
#
# If +remove_destination+ is true, this method removes each destination file before copy.
#
# # Installing Ruby library "mylib" under the site_ruby
# FileUtils.rm_r site_ruby + '/mylib', :force
# FileUtils.rm_r site_ruby + '/mylib', force: true
# FileUtils.cp_r 'lib/', site_ruby + '/mylib'
#
# # Examples of copying several files to target directory.
# FileUtils.cp_r %w(mail.rb field.rb debug/), site_ruby + '/tmail'
# FileUtils.cp_r Dir.glob('*.rb'), '/home/foo/lib/ruby', :noop => true, :verbose => true
# FileUtils.cp_r Dir.glob('*.rb'), '/home/foo/lib/ruby', noop: true, verbose: true
#
# # If you want to copy all contents of a directory instead of the
# # directory itself, c.f. src/x -> dest/x, src/y -> dest/y,
Expand Down Expand Up @@ -511,10 +525,10 @@ def copy_stream(src, dest)
# disk partition, the file is copied then the original file is removed.
#
# FileUtils.mv 'badname.rb', 'goodname.rb'
# FileUtils.mv 'stuff.rb', '/notexist/lib/ruby', :force => true # no error
# FileUtils.mv 'stuff.rb', '/notexist/lib/ruby', force: true # no error
#
# FileUtils.mv %w(junk.txt dust.txt), '/home/foo/.trash/'
# FileUtils.mv Dir.glob('test*.rb'), 'test', :noop => true, :verbose => true
# FileUtils.mv Dir.glob('test*.rb'), 'test', noop: true, verbose: true
#
def mv(src, dest, force: nil, noop: nil, verbose: nil, secure: nil)
fu_output_message "mv#{force ? ' -f' : ''} #{[src,dest].flatten.join ' '}" if verbose
Expand Down Expand Up @@ -553,7 +567,7 @@ def mv(src, dest, force: nil, noop: nil, verbose: nil, secure: nil)
#
# FileUtils.rm %w( junk.txt dust.txt )
# FileUtils.rm Dir.glob('*.so')
# FileUtils.rm 'NotExistFile', :force => true # never raises exception
# FileUtils.rm 'NotExistFile', force: true # never raises exception
#
def rm(list, force: nil, noop: nil, verbose: nil)
list = fu_list(list)
Expand All @@ -572,7 +586,7 @@ def rm(list, force: nil, noop: nil, verbose: nil)
#
# Equivalent to
#
# FileUtils.rm(list, :force => true)
# FileUtils.rm(list, force: true)
#
def rm_f(list, noop: nil, verbose: nil)
rm list, force: true, noop: noop, verbose: verbose
Expand All @@ -588,18 +602,18 @@ def rm_f(list, noop: nil, verbose: nil)
# StandardError when :force option is set.
#
# FileUtils.rm_r Dir.glob('/tmp/*')
# FileUtils.rm_r 'some_dir', :force => true
# FileUtils.rm_r 'some_dir', force: true
#
# WARNING: This method causes local vulnerability
# if one of parent directories or removing directory tree are world
# writable (including /tmp, whose permission is 1777), and the current
# process has strong privilege such as Unix super user (root), and the
# system has symbolic link. For secure removing, read the documentation
# of #remove_entry_secure carefully, and set :secure option to true.
# Default is :secure=>false.
# of remove_entry_secure carefully, and set :secure option to true.
# Default is <tt>secure: false</tt>.
#
# NOTE: This method calls #remove_entry_secure if :secure option is set.
# See also #remove_entry_secure.
# NOTE: This method calls remove_entry_secure if :secure option is set.
# See also remove_entry_secure.
#
def rm_r(list, force: nil, noop: nil, verbose: nil, secure: nil)
list = fu_list(list)
Expand All @@ -618,10 +632,10 @@ def rm_r(list, force: nil, noop: nil, verbose: nil, secure: nil)
#
# Equivalent to
#
# FileUtils.rm_r(list, :force => true)
# FileUtils.rm_r(list, force: true)
#
# WARNING: This method causes local vulnerability.
# Read the documentation of #rm_r first.
# Read the documentation of rm_r first.
#
def rm_rf(list, noop: nil, verbose: nil, secure: nil)
rm_r list, force: true, noop: noop, verbose: verbose, secure: secure
Expand All @@ -635,7 +649,7 @@ def rm_rf(list, noop: nil, verbose: nil, secure: nil)
# This method removes a file system entry +path+. +path+ shall be a
# regular file, a directory, or something. If +path+ is a directory,
# remove it recursively. This method is required to avoid TOCTTOU
# (time-of-check-to-time-of-use) local security vulnerability of #rm_r.
# (time-of-check-to-time-of-use) local security vulnerability of rm_r.
# #rm_r causes security hole when:
#
# * Parent directory is world writable (including /tmp).
Expand Down Expand Up @@ -754,7 +768,7 @@ def fu_stat_identical_entry?(a, b) #:nodoc:
# +path+ might be a regular file, a directory, or something.
# If +path+ is a directory, remove it recursively.
#
# See also #remove_entry_secure.
# See also remove_entry_secure.
#
def remove_entry(path, force = false)
Entry_.new(path).postorder_traverse do |ent|
Expand Down Expand Up @@ -838,8 +852,8 @@ def compare_stream(a, b)
# mode to +mode+. If +dest+ is a directory, destination is +dest+/+src+.
# This method removes destination before copy.
#
# FileUtils.install 'ruby', '/usr/local/bin/ruby', :mode => 0755, :verbose => true
# FileUtils.install 'lib.rb', '/usr/local/lib/ruby/site_ruby', :verbose => true
# FileUtils.install 'ruby', '/usr/local/bin/ruby', mode: 0755, verbose: true
# FileUtils.install 'lib.rb', '/usr/local/lib/ruby/site_ruby', verbose: true
#
def install(src, dest, mode: nil, owner: nil, group: nil, preserve: nil,
noop: nil, verbose: nil)
Expand Down Expand Up @@ -969,12 +983,12 @@ def mode_to_s(mode) #:nodoc:
# Absolute mode is
# FileUtils.chmod 0755, 'somecommand'
# FileUtils.chmod 0644, %w(my.rb your.rb his.rb her.rb)
# FileUtils.chmod 0755, '/usr/bin/ruby', :verbose => true
# FileUtils.chmod 0755, '/usr/bin/ruby', verbose: true
#
# Symbolic mode is
# FileUtils.chmod "u=wrx,go=rx", 'somecommand'
# FileUtils.chmod "u=wr,go=rr", %w(my.rb your.rb his.rb her.rb)
# FileUtils.chmod "u=wrx,go=rx", '/usr/bin/ruby', :verbose => true
# FileUtils.chmod "u=wrx,go=rx", '/usr/bin/ruby', verbose: true
#
# "a" :: is user, group, other mask.
# "u" :: is user's mask.
Expand Down Expand Up @@ -1034,7 +1048,7 @@ def chmod_R(mode, list, noop: nil, verbose: nil, force: nil)
# the attribute.
#
# FileUtils.chown 'root', 'staff', '/usr/local/bin/ruby'
# FileUtils.chown nil, 'bin', Dir.glob('/usr/bin/*'), :verbose => true
# FileUtils.chown nil, 'bin', Dir.glob('/usr/bin/*'), verbose: true
#
def chown(user, group, list, noop: nil, verbose: nil)
list = fu_list(list)
Expand All @@ -1058,7 +1072,7 @@ def chown(user, group, list, noop: nil, verbose: nil)
# method does not change the attribute.
#
# FileUtils.chown_R 'www', 'www', '/var/www/htdocs'
# FileUtils.chown_R 'cvs', 'cvs', '/var/cvs', :verbose => true
# FileUtils.chown_R 'cvs', 'cvs', '/var/cvs', verbose: true
#
def chown_R(user, group, list, noop: nil, verbose: nil, force: nil)
list = fu_list(list)
Expand Down Expand Up @@ -1607,8 +1621,11 @@ def fu_output_message(msg) #:nodoc:
tbl
}

public

#
# Returns an Array of method names which have any options.
# Returns an Array of names of high-level methods that accept any keyword
# arguments.
#
# p FileUtils.commands #=> ["chmod", "cp", "cp_r", "install", ...]
#
Expand Down Expand Up @@ -1647,22 +1664,24 @@ def self.options_of(mid)
end

#
# Returns an Array of method names which have the option +opt+.
# Returns an Array of methods names which have the option +opt+.
#
# p FileUtils.collect_method(:preserve) #=> ["cp", "cp_r", "copy", "install"]
#
def self.collect_method(opt)
OPT_TABLE.keys.select {|m| OPT_TABLE[m].include?(opt) }
end

LOW_METHODS = singleton_methods(false) - collect_method(:noop).map(&:intern)
module LowMethods
private

LOW_METHODS = singleton_methods(false) - collect_method(:noop).map(&:intern) # :nodoc:
module LowMethods # :nodoc: internal use only
private
def _do_nothing(*)end
::FileUtils::LOW_METHODS.map {|name| alias_method name, :_do_nothing}
end

METHODS = singleton_methods() - [:private_module_function,
METHODS = singleton_methods() - [:private_module_function, # :nodoc:
:commands, :options, :have_option?, :options_of, :collect_method]

#
Expand Down