diff options
Diffstat (limited to 'code/extensions/fileutils.rb')
-rw-r--r-- | code/extensions/fileutils.rb | 1594 |
1 files changed, 0 insertions, 1594 deletions
diff --git a/code/extensions/fileutils.rb b/code/extensions/fileutils.rb index 701fcde7..7920e4f2 100644 --- a/code/extensions/fileutils.rb +++ b/code/extensions/fileutils.rb @@ -1,391 +1,4 @@ -# -# = fileutils.rb -# -# Copyright (c) 2000-2006 Minero Aoki -# -# This program is free software. -# You can distribute/modify this program under the same terms of ruby. -# -# == module FileUtils -# -# Namespace for several file utility methods for copying, moving, removing, etc. -# -# === Module Functions -# -# cd(dir, options) -# cd(dir, options) {|dir| .... } -# pwd() -# mkdir(dir, options) -# mkdir(list, options) -# mkdir_p(dir, options) -# mkdir_p(list, options) -# rmdir(dir, options) -# rmdir(list, options) -# ln(old, new, options) -# ln(list, destdir, options) -# ln_s(old, new, options) -# ln_s(list, destdir, options) -# ln_sf(src, dest, options) -# cp(src, dest, options) -# cp(list, dir, options) -# cp_r(src, dest, options) -# cp_r(list, dir, options) -# mv(src, dest, options) -# mv(list, dir, options) -# rm(list, options) -# rm_r(list, options) -# rm_rf(list, options) -# install(src, dest, mode = <src's>, options) -# chmod(mode, list, options) -# chmod_R(mode, list, options) -# chown(user, group, list, options) -# chown_R(user, group, list, options) -# 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 two are obvious. -# Each method documents the options that it honours. -# -# 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: -# -# copy_entry(src, dest, preserve = false, dereference = false) -# copy_file(src, dest, preserve = false, dereference = true) -# copy_stream(srcstream, deststream) -# remove_entry(path, force = false) -# remove_entry_secure(path, force = false) -# remove_file(path, force = false) -# compare_file(path_a, path_b) -# compare_stream(stream_a, stream_b) -# uptodate?(file, cmp_list) -# -# == module FileUtils::Verbose -# -# This module has all methods of FileUtils module, but it outputs messages -# before acting. This equates to passing the <tt>:verbose</tt> flag to methods -# in FileUtils. -# -# == module FileUtils::NoWrite -# -# This module has all methods of FileUtils module, but never changes -# files/directories. This equates to passing the <tt>:noop</tt> flag to methods -# in FileUtils. -# -# == module FileUtils::DryRun -# -# This module has all methods of FileUtils module, but never changes -# files/directories. This equates to passing the <tt>:noop</tt> and -# <tt>:verbose</tt> flags to methods in FileUtils. -# - module FileUtils - - def self.private_module_function(name) #:nodoc: - module_function name - private_class_method name - end - - # This hash table holds command options. - OPT_TABLE = {} #:nodoc: internal use only - - # - # Options: (none) - # - # Returns the name of the current directory. - # - def pwd - Dir.pwd - end - module_function :pwd - - alias getwd pwd - module_function :getwd - - # - # Options: verbose - # - # Changes the current directory to the directory +dir+. - # - # If this method is called with block, resumes to the old - # working directory after the block execution finished. - # - # FileUtils.cd('/', :verbose => true) # chdir and report it - # - def cd(dir, options = {}, &block) # :yield: dir - fu_check_options options, OPT_TABLE['cd'] - fu_output_message "cd #{dir}" if options[:verbose] - Dir.chdir(dir, &block) - fu_output_message 'cd -' if options[:verbose] and block - end - module_function :cd - - alias chdir cd - module_function :chdir - - OPT_TABLE['cd'] = - OPT_TABLE['chdir'] = [:verbose] - - # - # Options: (none) - # - # Returns true if +newer+ is newer than all +old_list+. - # Non-existent files are older than any file. - # - # FileUtils.uptodate?('hello.o', %w(hello.c hello.h)) or \ - # system 'make hello.o' - # - def uptodate?(new, old_list, options = nil) - raise ArgumentError, 'uptodate? does not accept any option' if options - - return false unless File.exist?(new) - new_time = File.mtime(new) - old_list.each do |old| - if File.exist?(old) - return false unless new_time > File.mtime(old) - end - end - true - end - module_function :uptodate? - - # - # Options: mode noop verbose - # - # 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 - # - def mkdir(list, options = {}) - fu_check_options options, OPT_TABLE['mkdir'] - list = fu_list(list) - fu_output_message "mkdir #{options[:mode] ? ('-m %03o ' % options[:mode]) : ''}#{list.join ' '}" if options[:verbose] - return if options[:noop] - - list.each do |dir| - fu_mkdir dir, options[:mode] - end - end - module_function :mkdir - - OPT_TABLE['mkdir'] = [:mode, :noop, :verbose] - - # - # Options: mode noop verbose - # - # Creates a directory and all its parent directories. - # For example, - # - # FileUtils.mkdir_p '/usr/local/lib/ruby' - # - # causes to make following directories, if it does not exist. - # * /usr - # * /usr/local - # * /usr/local/lib - # * /usr/local/lib/ruby - # - # You can pass several directories at a time in a list. - # - def mkdir_p(list, options = {}) - fu_check_options options, OPT_TABLE['mkdir_p'] - list = fu_list(list) - fu_output_message "mkdir -p #{options[:mode] ? ('-m %03o ' % options[:mode]) : ''}#{list.join ' '}" if options[:verbose] - return *list if options[:noop] - - list.map {|path| path.sub(%r</\z>, '') }.each do |path| - # optimize for the most common case - begin - fu_mkdir path, options[:mode] - next - rescue SystemCallError - next if File.directory?(path) - end - - stack = [] - until path == stack.last # dirname("/")=="/", dirname("C:/")=="C:/" - stack.push path - path = File.dirname(path) - end - stack.reverse_each do |path| - begin - fu_mkdir path, options[:mode] - rescue SystemCallError => err - raise unless File.directory?(path) - end - end - end - - return *list - end - module_function :mkdir_p - - alias mkpath mkdir_p - alias makedirs mkdir_p - module_function :mkpath - module_function :makedirs - - OPT_TABLE['mkdir_p'] = - OPT_TABLE['mkpath'] = - OPT_TABLE['makedirs'] = [:mode, :noop, :verbose] - - def fu_mkdir(path, mode) #:nodoc: - path = path.sub(%r</\z>, '') - if mode - Dir.mkdir path, mode - File.chmod mode, path - else - Dir.mkdir path - end - end - private_module_function :fu_mkdir - - # - # Options: noop, verbose - # - # Removes one or more directories. - # - # FileUtils.rmdir 'somedir' - # FileUtils.rmdir %w(somedir anydir otherdir) - # # Does not really remove directory; outputs message. - # FileUtils.rmdir 'somedir', :verbose => true, :noop => true - # - def rmdir(list, options = {}) - fu_check_options options, OPT_TABLE['rmdir'] - list = fu_list(list) - fu_output_message "rmdir #{list.join ' '}" if options[:verbose] - return if options[:noop] - list.each do |dir| - Dir.rmdir dir.sub(%r</\z>, '') - end - end - module_function :rmdir - - OPT_TABLE['rmdir'] = [:noop, :verbose] - - # - # Options: force noop verbose - # - # <b><tt>ln(old, new, options = {})</tt></b> - # - # Creates a hard link +new+ which points to +old+. - # If +new+ already exists and it is a directory, creates a link +new/old+. - # If +new+ already exists and it is not a directory, raises Errno::EEXIST. - # But if :force option is set, overwrite +new+. - # - # FileUtils.ln 'gcc', 'cc', :verbose => true - # FileUtils.ln '/usr/bin/emacs21', '/usr/bin/emacs' - # - # <b><tt>ln(list, destdir, options = {})</tt></b> - # - # Creates several hard links in a directory, with each one pointing to the - # item in +list+. If +destdir+ is not a directory, raises Errno::ENOTDIR. - # - # include FileUtils - # cd '/sbin' - # FileUtils.ln %w(cp mv mkdir), '/bin' # Now /sbin/cp and /bin/cp are linked. - # - def ln(src, dest, options = {}) - fu_check_options options, OPT_TABLE['ln'] - fu_output_message "ln#{options[:force] ? ' -f' : ''} #{[src,dest].flatten.join ' '}" if options[:verbose] - return if options[:noop] - fu_each_src_dest0(src, dest) do |s,d| - remove_file d, true if options[:force] - File.link s, d - end - end - module_function :ln - - alias link ln - module_function :link - - OPT_TABLE['ln'] = - OPT_TABLE['link'] = [:force, :noop, :verbose] - - # - # Options: force noop verbose - # - # <b><tt>ln_s(old, new, options = {})</tt></b> - # - # Creates a symbolic link +new+ which points to +old+. If +new+ already - # exists and it is a directory, creates a symbolic link +new/old+. If +new+ - # already exists and it is not a directory, raises Errno::EEXIST. But if - # :force option is set, overwrite +new+. - # - # FileUtils.ln_s '/usr/bin/ruby', '/usr/local/bin/ruby' - # FileUtils.ln_s 'verylongsourcefilename.c', 'c', :force => true - # - # <b><tt>ln_s(list, destdir, options = {})</tt></b> - # - # Creates several symbolic links in a directory, with each one pointing to the - # item in +list+. If +destdir+ is not a directory, raises Errno::ENOTDIR. - # - # If +destdir+ is not a directory, raises Errno::ENOTDIR. - # - # FileUtils.ln_s Dir.glob('bin/*.rb'), '/home/aamine/bin' - # - def ln_s(src, dest, options = {}) - fu_check_options options, OPT_TABLE['ln_s'] - fu_output_message "ln -s#{options[:force] ? 'f' : ''} #{[src,dest].flatten.join ' '}" if options[:verbose] - return if options[:noop] - fu_each_src_dest0(src, dest) do |s,d| - remove_file d, true if options[:force] - File.symlink s, d - end - end - module_function :ln_s - - alias symlink ln_s - module_function :symlink - - OPT_TABLE['ln_s'] = - OPT_TABLE['symlink'] = [:force, :noop, :verbose] - - # - # Options: noop verbose - # - # Same as - # #ln_s(src, dest, :force) - # - def ln_sf(src, dest, options = {}) - fu_check_options options, OPT_TABLE['ln_sf'] - options = options.dup - options[:force] = true - ln_s src, dest, options - end - module_function :ln_sf - - OPT_TABLE['ln_sf'] = [:noop, :verbose] - - # - # Options: preserve noop verbose - # - # Copies a file content +src+ to +dest+. If +dest+ is a directory, - # copies +src+ to +dest/src+. - # - # If +src+ is a list of files, then +dest+ must be a directory. - # - # 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 'symlink', 'dest' # copy content, "dest" is not a symlink - # - def cp(src, dest, options = {}) - fu_check_options options, OPT_TABLE['cp'] - fu_output_message "cp#{options[:preserve] ? ' -p' : ''} #{[src,dest].flatten.join ' '}" if options[:verbose] - return if options[:noop] - fu_each_src_dest(src, dest) do |s, d| - copy_file s, d, options[:preserve] - end - end - module_function :cp - - # changed def cp_in_bar(bar, src, dest, options = {}) fu_check_options options, OPT_TABLE['cp'] fu_output_message "cp#{options[:preserve] ? ' -p' : ''} #{[src,dest].flatten.join ' '}" if options[:verbose] @@ -396,47 +9,6 @@ module FileUtils end module_function :cp_in_bar - alias copy cp - module_function :copy - - OPT_TABLE['cp'] = - OPT_TABLE['copy'] = [:preserve, :noop, :verbose] - - # - # Options: preserve noop verbose dereference_root remove_destination - # - # Copies +src+ to +dest+. If +src+ is a directory, this method copies - # all its contents recursively. If +dest+ is a directory, copies - # +src+ to +dest/src+. - # - # +src+ can be a list of files. - # - # # Installing ruby library "mylib" under the site_ruby - # FileUtils.rm_r site_ruby + '/mylib', :force - # 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/aamine/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, - # # use following code. - # FileUtils.cp_r 'src/.', 'dest' # cp_r('src', 'dest') makes src/dest, - # # but this doesn't. - # - def cp_r(src, dest, options = {}) - fu_check_options options, OPT_TABLE['cp_r'] - fu_output_message "cp -r#{options[:preserve] ? 'p' : ''}#{options[:remove_destination] ? ' --remove-destination' : ''} #{[src,dest].flatten.join ' '}" if options[:verbose] - return if options[:noop] - options[:dereference_root] = true unless options.key?(:dereference_root) - fu_each_src_dest(src, dest) do |s, d| - copy_entry s, d, options[:preserve], options[:dereference_root], options[:remove_destination] - end - end - module_function :cp_r - - #changed def cp_r_in_bar(bar, src, dest, options = {}) fu_check_options options, OPT_TABLE['cp_r'] fu_output_message "cp -r#{options[:preserve] ? 'p' : ''}#{options[:remove_destination] ? ' --remove-destination' : ''} #{[src,dest].flatten.join ' '}" if options[:verbose] @@ -448,36 +20,6 @@ module FileUtils end module_function :cp_r_in_bar - OPT_TABLE['cp_r'] = [:preserve, :noop, :verbose, - :dereference_root, :remove_destination] - - # - # Copies a file system entry +src+ to +dest+. - # If +src+ is a directory, this method copies its contents recursively. - # This method preserves file types, c.f. symlink, directory... - # (FIFO, device files and etc. are not supported yet) - # - # Both of +src+ and +dest+ must be a path name. - # +src+ must exist, +dest+ must not exist. - # - # If +preserve+ is true, this method preserves owner, group, permissions - # and modified time. - # - # If +dereference_root+ is true, this method dereference tree root. - # - # If +remove_destination+ is true, this method removes each destination file before copy. - # - def copy_entry(src, dest, preserve = false, dereference_root = false, remove_destination = false) - Entry_.new(src, nil, dereference_root).traverse do |ent| - destent = Entry_.new(dest, ent.rel, false) - File.unlink destent.path if remove_destination && File.file?(destent.path) - ent.copy destent.path - ent.copy_metadata destent.path if preserve - end - end - module_function :copy_entry - - #changed def copy_entry_in_bar(bar, src, dest, preserve = false, dereference_root = false, remove_destination = false) Entry_.new(src, nil, dereference_root).traverse do |ent| destent = Entry_.new(dest, ent.rel, false) @@ -488,18 +30,6 @@ module FileUtils end module_function :copy_entry_in_bar - # - # Copies file contents of +src+ to +dest+. - # Both of +src+ and +dest+ must be a path name. - # - def copy_file(src, dest, preserve = false, dereference = true) - ent = Entry_.new(src, nil, dereference) - ent.copy_file dest - ent.copy_metadata dest if preserve - end - module_function :copy_file - - # changed def copy_file_in_bar(bar, src, dest, preserve = false, dereference = true) ent = Entry_.new(src, nil, dereference) ent.copy_file_in_bar bar, dest @@ -507,59 +37,6 @@ module FileUtils end module_function :copy_file_in_bar - # - # Copies stream +src+ to +dest+. - # +src+ must respond to #read(n) and - # +dest+ must respond to #write(str). - # - def copy_stream(src, dest) - fu_copy_stream0 src, dest, fu_stream_blksize(src, dest) - end - module_function :copy_stream - - # - # Options: force noop verbose - # - # Moves file(s) +src+ to +dest+. If +file+ and +dest+ exist on the different - # disk partition, the file is copied instead. - # - # FileUtils.mv 'badname.rb', 'goodname.rb' - # FileUtils.mv 'stuff.rb', '/notexist/lib/ruby', :force => true # no error - # - # FileUtils.mv %w(junk.txt dust.txt), '/home/aamine/.trash/' - # FileUtils.mv Dir.glob('test*.rb'), 'test', :noop => true, :verbose => true - # - def mv(src, dest, options = {}) - fu_check_options options, OPT_TABLE['mv'] - fu_output_message "mv#{options[:force] ? ' -f' : ''} #{[src,dest].flatten.join ' '}" if options[:verbose] - return if options[:noop] - fu_each_src_dest(src, dest) do |s, d| - destent = Entry_.new(d, nil, true) - begin - if destent.exist? - if destent.directory? - raise Errno::EEXIST, dest - else - destent.remove_file if rename_cannot_overwrite_file? - end - end - begin - File.rename s, d - rescue Errno::EXDEV - copy_entry s, d, true - if options[:secure] - remove_entry_secure s, options[:force] - else - remove_entry s, options[:force] - end - end - rescue SystemCallError - raise unless options[:force] - end - end - end - module_function :mv - def mv_in_bar(bar, src, dest, options = {}) fu_check_options options, OPT_TABLE['mv'] fu_output_message "mv#{options[:force] ? ' -f' : ''} #{[src,dest].flatten.join ' '}" if options[:verbose] @@ -591,539 +68,7 @@ module FileUtils end module_function :mv_in_bar - alias move mv - module_function :move - - OPT_TABLE['mv'] = - OPT_TABLE['move'] = [:force, :noop, :verbose, :secure] - - def rename_cannot_overwrite_file? #:nodoc: - /djgpp|cygwin|mswin|mingw|bccwin|wince|emx/ =~ RUBY_PLATFORM - end - private_module_function :rename_cannot_overwrite_file? - - # - # Options: force noop verbose - # - # Remove file(s) specified in +list+. This method cannot remove directories. - # All StandardErrors are ignored when the :force option is set. - # - # FileUtils.rm %w( junk.txt dust.txt ) - # FileUtils.rm Dir.glob('*.so') - # FileUtils.rm 'NotExistFile', :force => true # never raises exception - # - def rm(list, options = {}) - fu_check_options options, OPT_TABLE['rm'] - list = fu_list(list) - fu_output_message "rm#{options[:force] ? ' -f' : ''} #{list.join ' '}" if options[:verbose] - return if options[:noop] - - list.each do |path| - remove_file path, options[:force] - end - end - module_function :rm - - alias remove rm - module_function :remove - - OPT_TABLE['rm'] = - OPT_TABLE['remove'] = [:force, :noop, :verbose] - - # - # Options: noop verbose - # - # Equivalent to - # - # #rm(list, :force => true) - # - def rm_f(list, options = {}) - fu_check_options options, OPT_TABLE['rm_f'] - options = options.dup - options[:force] = true - rm list, options - end - module_function :rm_f - - alias safe_unlink rm_f - module_function :safe_unlink - - OPT_TABLE['rm_f'] = - OPT_TABLE['safe_unlink'] = [:noop, :verbose] - - # - # Options: force noop verbose secure - # - # remove files +list+[0] +list+[1]... If +list+[n] is a directory, - # removes its all contents recursively. This method ignores - # StandardError when :force option is set. - # - # FileUtils.rm_r Dir.glob('/tmp/*') - # FileUtils.rm_r '/', :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. - # - # NOTE: This method calls #remove_entry_secure if :secure option is set. - # See also #remove_entry_secure. - # - def rm_r(list, options = {}) - fu_check_options options, OPT_TABLE['rm_r'] - # options[:secure] = true unless options.key?(:secure) - list = fu_list(list) - fu_output_message "rm -r#{options[:force] ? 'f' : ''} #{list.join ' '}" if options[:verbose] - return if options[:noop] - list.each do |path| - if options[:secure] - remove_entry_secure path, options[:force] - else - remove_entry path, options[:force] - end - end - end - module_function :rm_r - - OPT_TABLE['rm_r'] = [:force, :noop, :verbose, :secure] - - # - # Options: noop verbose secure - # - # Equivalent to - # - # #rm_r(list, :force => true) - # - # WARNING: This method causes local vulnerability. - # Read the documentation of #rm_r first. - # - def rm_rf(list, options = {}) - fu_check_options options, OPT_TABLE['rm_rf'] - options = options.dup - options[:force] = true - rm_r list, options - end - module_function :rm_rf - - alias rmtree rm_rf - module_function :rmtree - - OPT_TABLE['rm_rf'] = - OPT_TABLE['rmtree'] = [:noop, :verbose, :secure] - - # - # 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. - # #rm_r causes security hole when: - # - # * Parent directory is world writable (including /tmp). - # * Removing directory tree includes world writable directory. - # * The system has symbolic link. - # - # To avoid this security hole, this method applies special preprocess. - # If +path+ is a directory, this method chown(2) and chmod(2) all - # removing directories. This requires the current process is the - # owner of the removing whole directory tree, or is the super user (root). - # - # WARNING: You must ensure that *ALL* parent directories are not - # world writable. Otherwise this method does not work. - # Only exception is temporary directory like /tmp and /var/tmp, - # whose permission is 1777. - # - # WARNING: Only the owner of the removing directory tree, or Unix super - # user (root) should invoke this method. Otherwise this method does not - # work. - # - # For details of this security vulnerability, see Perl's case: - # - # http://www.cve.mitre.org/cgi-bin/cvename.cgi?name=CAN-2005-0448 - # http://www.cve.mitre.org/cgi-bin/cvename.cgi?name=CAN-2004-0452 - # - # For fileutils.rb, this vulnerability is reported in [ruby-dev:26100]. - # - def remove_entry_secure(path, force = false) - unless fu_have_symlink? - remove_entry path, force - return - end - fullpath = File.expand_path(path) - st = File.lstat(fullpath) - unless st.directory? - File.unlink fullpath - return - end - # is a directory. - parent_st = File.stat(File.dirname(fullpath)) - unless fu_world_writable?(parent_st) - remove_entry path, force - return - end - unless parent_st.sticky? - raise ArgumentError, "parent directory is world writable, FileUtils#remove_entry_secure does not work; abort: #{path.inspect} (parent directory mode #{'%o' % parent_st.mode})" - end - # freeze tree root - euid = Process.euid - File.open(fullpath + '/.') {|f| - unless fu_stat_identical_entry?(st, f.stat) - # symlink (TOC-to-TOU attack?) - File.unlink fullpath - return - end - f.chown euid, -1 - f.chmod 0700 - } - # ---- tree root is frozen ---- - root = Entry_.new(path) - root.preorder_traverse do |ent| - if ent.directory? - ent.chown euid, -1 - ent.chmod 0700 - end - end - root.postorder_traverse do |ent| - begin - ent.remove - rescue - raise unless force - end - end - rescue - raise unless force - end - module_function :remove_entry_secure - - def fu_world_writable?(st) - (st.mode & 0002) != 0 - end - private_module_function :fu_world_writable? - - def fu_have_symlink? #:nodoc - File.symlink nil, nil - rescue NotImplementedError - return false - rescue - return true - end - private_module_function :fu_have_symlink? - - def fu_stat_identical_entry?(a, b) #:nodoc: - a.dev == b.dev and a.ino == b.ino - end - private_module_function :fu_stat_identical_entry? - - # - # This method removes a file system entry +path+. - # +path+ might be a regular file, a directory, or something. - # If +path+ is a directory, remove it recursively. - # - # See also #remove_entry_secure. - # - def remove_entry(path, force = false) - Entry_.new(path).postorder_traverse do |ent| - begin - ent.remove - rescue - raise unless force - end - end - rescue - raise unless force - end - module_function :remove_entry - - # - # Removes a file +path+. - # This method ignores StandardError if +force+ is true. - # - def remove_file(path, force = false) - Entry_.new(path).remove_file - rescue - raise unless force - end - module_function :remove_file - - # - # Removes a directory +dir+ and its contents recursively. - # This method ignores StandardError if +force+ is true. - # - def remove_dir(path, force = false) - remove_entry path, force # FIXME?? check if it is a directory - end - module_function :remove_dir - - # - # Returns true if the contents of a file A and a file B are identical. - # - # FileUtils.compare_file('somefile', 'somefile') #=> true - # FileUtils.compare_file('/bin/cp', '/bin/mv') #=> maybe false - # - def compare_file(a, b) - return false unless File.size(a) == File.size(b) - File.open(a, 'rb') {|fa| - File.open(b, 'rb') {|fb| - return compare_stream(fa, fb) - } - } - end - module_function :compare_file - - alias identical? compare_file - alias cmp compare_file - module_function :identical? - module_function :cmp - - # - # Returns true if the contents of a stream +a+ and +b+ are identical. - # - def compare_stream(a, b) - bsize = fu_stream_blksize(a, b) - sa = sb = nil - while sa == sb - sa = a.read(bsize) - sb = b.read(bsize) - unless sa and sb - if sa.nil? and sb.nil? - return true - end - end - end - false - end - module_function :compare_stream - - # - # Options: mode preserve noop verbose - # - # If +src+ is not same as +dest+, copies it and changes the permission - # 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 - # - def install(src, dest, options = {}) - fu_check_options options, OPT_TABLE['install'] - fu_output_message "install -c#{options[:preserve] && ' -p'}#{options[:mode] ? (' -m 0%o' % options[:mode]) : ''} #{[src,dest].flatten.join ' '}" if options[:verbose] - return if options[:noop] - fu_each_src_dest(src, dest) do |s, d| - unless File.exist?(d) and compare_file(s, d) - remove_file d, true - st = File.stat(s) if options[:preserve] - copy_file s, d - File.utime st.atime, st.mtime, d if options[:preserve] - File.chmod options[:mode], d if options[:mode] - end - end - end - module_function :install - - OPT_TABLE['install'] = [:mode, :preserve, :noop, :verbose] - - # - # Options: noop verbose - # - # Changes permission bits on the named files (in +list+) to the bit pattern - # represented by +mode+. - # - # FileUtils.chmod 0755, 'somecommand' - # FileUtils.chmod 0644, %w(my.rb your.rb his.rb her.rb) - # FileUtils.chmod 0755, '/usr/bin/ruby', :verbose => true - # - def chmod(mode, list, options = {}) - fu_check_options options, OPT_TABLE['chmod'] - list = fu_list(list) - fu_output_message sprintf('chmod %o %s', mode, list.join(' ')) if options[:verbose] - return if options[:noop] - list.each do |path| - Entry_.new(path).chmod mode - end - end - module_function :chmod - - OPT_TABLE['chmod'] = [:noop, :verbose] - - # - # Options: noop verbose force - # - # Changes permission bits on the named files (in +list+) - # to the bit pattern represented by +mode+. - # - # FileUtils.chmod_R 0700, "/tmp/app.#{$$}" - # - def chmod_R(mode, list, options = {}) - fu_check_options options, OPT_TABLE['chmod_R'] - list = fu_list(list) - fu_output_message sprintf('chmod -R%s %o %s', - (options[:force] ? 'f' : ''), - mode, list.join(' ')) if options[:verbose] - return if options[:noop] - list.each do |root| - Entry_.new(root).traverse do |ent| - begin - ent.chmod mode - rescue - raise unless options[:force] - end - end - end - end - module_function :chmod_R - - OPT_TABLE['chmod_R'] = [:noop, :verbose, :force] - - # - # Options: noop verbose - # - # Changes owner and group on the named files (in +list+) - # to the user +user+ and the group +group+. +user+ and +group+ - # may be an ID (Integer/String) or a name (String). - # If +user+ or +group+ is nil, this method does not change - # the attribute. - # - # FileUtils.chown 'root', 'staff', '/usr/local/bin/ruby' - # FileUtils.chown nil, 'bin', Dir.glob('/usr/bin/*'), :verbose => true - # - def chown(user, group, list, options = {}) - fu_check_options options, OPT_TABLE['chown'] - list = fu_list(list) - fu_output_message sprintf('chown %s%s', - [user,group].compact.join(':') + ' ', - list.join(' ')) if options[:verbose] - return if options[:noop] - uid = fu_get_uid(user) - gid = fu_get_gid(group) - list.each do |path| - Entry_.new(path).chown uid, gid - end - end - module_function :chown - - OPT_TABLE['chown'] = [:noop, :verbose] - - # - # Options: noop verbose force - # - # Changes owner and group on the named files (in +list+) - # to the user +user+ and the group +group+ recursively. - # +user+ and +group+ may be an ID (Integer/String) or - # a name (String). If +user+ or +group+ is nil, this - # method does not change the attribute. - # - # FileUtils.chown_R 'www', 'www', '/var/www/htdocs' - # FileUtils.chown_R 'cvs', 'cvs', '/var/cvs', :verbose => true - # - def chown_R(user, group, list, options = {}) - fu_check_options options, OPT_TABLE['chown_R'] - list = fu_list(list) - fu_output_message sprintf('chown -R%s %s%s', - (options[:force] ? 'f' : ''), - [user,group].compact.join(':') + ' ', - list.join(' ')) if options[:verbose] - return if options[:noop] - uid = fu_get_uid(user) - gid = fu_get_gid(group) - return unless uid or gid - list.each do |root| - Entry_.new(root).traverse do |ent| - begin - ent.chown uid, gid - rescue - raise unless options[:force] - end - end - end - end - module_function :chown_R - - OPT_TABLE['chown_R'] = [:noop, :verbose, :force] - - begin - require 'etc' - - def fu_get_uid(user) #:nodoc: - return nil unless user - user = user.to_s - if /\A\d+\z/ =~ user - then user.to_i - else Etc.getpwnam(user).uid - end - end - private_module_function :fu_get_uid - - def fu_get_gid(group) #:nodoc: - return nil unless group - if /\A\d+\z/ =~ group - then group.to_i - else Etc.getgrnam(group).gid - end - end - private_module_function :fu_get_gid - - rescue LoadError - # need Win32 support??? - - def fu_get_uid(user) #:nodoc: - user # FIXME - end - private_module_function :fu_get_uid - - def fu_get_gid(group) #:nodoc: - group # FIXME - end - private_module_function :fu_get_gid - end - - # - # Options: noop verbose - # - # Updates modification time (mtime) and access time (atime) of file(s) in - # +list+. Files are created if they don't exist. - # - # FileUtils.touch 'timestamp' - # FileUtils.touch Dir.glob('*.c'); system 'make' - # - def touch(list, options = {}) - fu_check_options options, OPT_TABLE['touch'] - list = fu_list(list) - created = nocreate = options[:nocreate] - t = options[:mtime] - if options[:verbose] - fu_output_message "touch #{nocreate ? ' -c' : ''}#{t ? t.strftime(' -t %Y%m%d%H%M.%S') : ''}#{list.join ' '}" - end - return if options[:noop] - list.each do |path| - created = nocreate - begin - File.utime(t, t, path) - rescue Errno::ENOENT - raise if created - File.open(path, 'a') { - ; - } - created = true - retry if t - end - end - end - module_function :touch - - OPT_TABLE['touch'] = [:noop, :verbose, :mtime, :nocreate] - - private - module StreamUtils_ - private - - def fu_windows? - /mswin|mingw|bccwin|wince|emx/ =~ RUBY_PLATFORM - end - - # changed def fu_copy_stream0_in_bar(bar, src, dest, blksize) #:nodoc: report = false if File.size?(src) @@ -1141,183 +86,9 @@ module FileUtils i.add 1 end end - - def fu_copy_stream0(src, dest, blksize) #:nodoc: - # FIXME: readpartial? - while s = src.read(blksize) - dest.write s - end - end - - def fu_stream_blksize(*streams) - streams.each do |s| - next unless s.respond_to?(:stat) - size = fu_blksize(s.stat) - return size if size - end - fu_default_blksize() - end - - def fu_blksize(st) - s = st.blksize - return nil unless s - return nil if s == 0 - s - end - - def fu_default_blksize - 1024 - end end - include StreamUtils_ - extend StreamUtils_ - class Entry_ #:nodoc: internal use only - include StreamUtils_ - - def initialize(a, b = nil, deref = false) - @prefix = @rel = @path = nil - if b - @prefix = a - @rel = b - else - @path = a - end - @deref = deref - @stat = nil - @lstat = nil - end - - def inspect - "\#<#{self.class} #{path()}>" - end - - def path - if @path - @path.to_str - else - join(@prefix, @rel) - end - end - - def prefix - @prefix || @path - end - - def rel - @rel - end - - def dereference? - @deref - end - - def exist? - lstat! ? true : false - end - - def file? - s = lstat! - s and s.file? - end - - def directory? - s = lstat! - s and s.directory? - end - - def symlink? - s = lstat! - s and s.symlink? - end - - def chardev? - s = lstat! - s and s.chardev? - end - - def blockdev? - s = lstat! - s and s.blockdev? - end - - def socket? - s = lstat! - s and s.socket? - end - - def pipe? - s = lstat! - s and s.pipe? - end - - S_IF_DOOR = 0xD000 - - def door? - s = lstat! - s and (s.mode & 0xF000 == S_IF_DOOR) - end - - def entries - Dir.entries(path())\ - .reject {|n| n == '.' or n == '..' }\ - .map {|n| Entry_.new(prefix(), join(rel(), n.untaint)) } - end - - def stat - return @stat if @stat - if lstat() and lstat().symlink? - @stat = File.stat(path()) - else - @stat = lstat() - end - @stat - end - - def stat! - return @stat if @stat - if lstat! and lstat!.symlink? - @stat = File.stat(path()) - else - @stat = lstat! - end - @stat - rescue SystemCallError - nil - end - - def lstat - if dereference? - @lstat ||= File.stat(path()) - else - @lstat ||= File.lstat(path()) - end - end - - def lstat! - lstat() - rescue SystemCallError - nil - end - - def chmod(mode) - if symlink? - File.lchmod mode, path() if have_lchmod? - else - File.chmod mode, path() - end - end - - def chown(uid, gid) - if symlink? - File.lchown uid, gid, path() if have_lchown? - else - File.chown uid, gid, path() - end - end - - #changed def copy_in_bar(bar, dest) case when file? @@ -1349,47 +120,6 @@ module FileUtils end end - def copy(dest) - case - when file? - copy_file dest - when directory? - begin - Dir.mkdir dest - rescue - raise unless File.directory?(dest) - end - when symlink? - File.symlink File.readlink(path()), dest - when chardev? - raise "cannot handle device file" unless File.respond_to?(:mknod) - mknod dest, ?c, 0666, lstat().rdev - when blockdev? - raise "cannot handle device file" unless File.respond_to?(:mknod) - mknod dest, ?b, 0666, lstat().rdev - when socket? - raise "cannot handle socket" unless File.respond_to?(:mknod) - mknod dest, nil, lstat().mode, 0 - when pipe? - raise "cannot handle FIFO" unless File.respond_to?(:mkfifo) - mkfifo dest, 0666 - when door? - raise "cannot handle door: #{path()}" - else - raise "unknown file type: #{path()}" - end - end - - def copy_file(dest) - st = stat() - File.open(path(), 'rb') {|r| - File.open(dest, 'wb', st.mode) {|w| - fu_copy_stream0 r, w, (fu_blksize(st) || fu_default_blksize()) - } - } - end - - # changed def copy_file_in_bar(bar, dest) st = stat() File.open(path(), 'rb') {|r| @@ -1398,329 +128,5 @@ module FileUtils } } end - - def copy_metadata(path) - st = lstat() - File.utime st.atime, st.mtime, path - begin - File.chown st.uid, st.gid, path - rescue Errno::EPERM - # clear setuid/setgid - File.chmod st.mode & 01777, path - else - File.chmod st.mode, path - end - end - - def remove - if directory? - remove_dir1 - else - remove_file - end - end - - def remove_dir1 - platform_support { - Dir.rmdir path().sub(%r</\z>, '') - } - end - - def remove_file - platform_support { - File.unlink path - } - end - - def platform_support - return yield unless fu_windows? - first_time_p = true - begin - yield - rescue Errno::ENOENT - raise - rescue => err - if first_time_p - first_time_p = false - begin - File.chmod 0700, path() # Windows does not have symlink - retry - rescue SystemCallError - end - end - raise err - end - end - - def preorder_traverse - stack = [self] - while ent = stack.pop - yield ent - stack.concat ent.entries.reverse if ent.directory? - end - end - - alias traverse preorder_traverse - - def postorder_traverse - if directory? - entries().each do |ent| - ent.postorder_traverse do |e| - yield e - end - end - end - yield self - end - - private - - $fileutils_rb_have_lchmod = nil - - def have_lchmod? - # This is not MT-safe, but it does not matter. - if $fileutils_rb_have_lchmod == nil - $fileutils_rb_have_lchmod = check_have_lchmod? - end - $fileutils_rb_have_lchmod - end - - def check_have_lchmod? - return false unless File.respond_to?(:lchmod) - File.lchmod 0 - return true - rescue NotImplementedError - return false - end - - $fileutils_rb_have_lchown = nil - - def have_lchown? - # This is not MT-safe, but it does not matter. - if $fileutils_rb_have_lchown == nil - $fileutils_rb_have_lchown = check_have_lchown? - end - $fileutils_rb_have_lchown - end - - def check_have_lchown? - return false unless File.respond_to?(:lchown) - File.lchown nil, nil - return true - rescue NotImplementedError - return false - end - - def join(dir, base) - return dir.to_str if not base or base == '.' - return base.to_str if not dir or dir == '.' - File.join(dir, base) - end - end # class Entry_ - - def fu_list(arg) #:nodoc: - [arg].flatten.map {|path| path.to_str } - end - private_module_function :fu_list - - def fu_each_src_dest(src, dest) #:nodoc: - fu_each_src_dest0(src, dest) do |s, d| - raise ArgumentError, "same file: #{s} and #{d}" if fu_same?(s, d) - yield s, d - end - end - private_module_function :fu_each_src_dest - - def fu_each_src_dest0(src, dest) #:nodoc: - if src.is_a?(Array) - src.each do |s| - s = s.to_str - yield s, File.join(dest, File.basename(s)) - end - else - src = src.to_str - if File.directory?(dest) - yield src, File.join(dest, File.basename(src)) - else - yield src, dest.to_str - end - end - end - private_module_function :fu_each_src_dest0 - - def fu_same?(a, b) #:nodoc: - if fu_have_st_ino? - st1 = File.stat(a) - st2 = File.stat(b) - st1.dev == st2.dev and st1.ino == st2.ino - else - File.expand_path(a) == File.expand_path(b) - end - rescue Errno::ENOENT - return false - end - private_module_function :fu_same? - - def fu_have_st_ino? #:nodoc: - not fu_windows? - end - private_module_function :fu_have_st_ino? - - def fu_check_options(options, optdecl) #:nodoc: - h = options.dup - optdecl.each do |opt| - h.delete opt - end - raise ArgumentError, "no such option: #{h.keys.join(' ')}" unless h.empty? - end - private_module_function :fu_check_options - - def fu_update_option(args, new) #:nodoc: - if args.last.is_a?(Hash) - args[-1] = args.last.dup.update(new) - else - args.push new - end - args - end - private_module_function :fu_update_option - - @fileutils_output = $stderr - @fileutils_label = '' - - def fu_output_message(msg) #:nodoc: - @fileutils_output ||= $stderr - @fileutils_label ||= '' - @fileutils_output.puts @fileutils_label + msg - end - private_module_function :fu_output_message - - # - # Returns an Array of method names which have any options. - # - # p FileUtils.commands #=> ["chmod", "cp", "cp_r", "install", ...] - # - def FileUtils.commands - OPT_TABLE.keys - end - - # - # Returns an Array of option names. - # - # p FileUtils.options #=> ["noop", "force", "verbose", "preserve", "mode"] - # - def FileUtils.options - OPT_TABLE.values.flatten.uniq.map {|sym| sym.to_s } - end - - # - # Returns true if the method +mid+ have an option +opt+. - # - # p FileUtils.have_option?(:cp, :noop) #=> true - # p FileUtils.have_option?(:rm, :force) #=> true - # p FileUtils.have_option?(:rm, :perserve) #=> false - # - def FileUtils.have_option?(mid, opt) - li = OPT_TABLE[mid.to_s] or raise ArgumentError, "no such method: #{mid}" - li.include?(opt) - end - - # - # Returns an Array of option names of the method +mid+. - # - # p FileUtils.options(:rm) #=> ["noop", "verbose", "force"] - # - def FileUtils.options_of(mid) - OPT_TABLE[mid.to_s].map {|sym| sym.to_s } - end - - # - # Returns an Array of method names which have the option +opt+. - # - # p FileUtils.collect_method(:preserve) #=> ["cp", "cp_r", "copy", "install"] - # - def FileUtils.collect_method(opt) - OPT_TABLE.keys.select {|m| OPT_TABLE[m].include?(opt) } - end - - METHODS = singleton_methods() - %w( private_module_function - commands options have_option? options_of collect_method ) - - # - # This module has all methods of FileUtils module, but it outputs messages - # before acting. This equates to passing the <tt>:verbose</tt> flag to - # methods in FileUtils. - # - module Verbose - include FileUtils - @fileutils_output = $stderr - @fileutils_label = '' - ::FileUtils.collect_method(:verbose).each do |name| - module_eval(<<-EOS, __FILE__, __LINE__ + 1) - def #{name}(*args) - super(*fu_update_option(args, :verbose => true)) - end - private :#{name} - EOS - end - extend self - class << self - ::FileUtils::METHODS.each do |m| - public m - end - end - end - - # - # This module has all methods of FileUtils module, but never changes - # files/directories. This equates to passing the <tt>:noop</tt> flag - # to methods in FileUtils. - # - module NoWrite - include FileUtils - @fileutils_output = $stderr - @fileutils_label = '' - ::FileUtils.collect_method(:noop).each do |name| - module_eval(<<-EOS, __FILE__, __LINE__ + 1) - def #{name}(*args) - super(*fu_update_option(args, :noop => true)) - end - private :#{name} - EOS - end - extend self - class << self - ::FileUtils::METHODS.each do |m| - public m - end - end - end - - # - # This module has all methods of FileUtils module, but never changes - # files/directories, with printing message before acting. - # This equates to passing the <tt>:noop</tt> and <tt>:verbose</tt> flag - # to methods in FileUtils. - # - module DryRun - include FileUtils - @fileutils_output = $stderr - @fileutils_label = '' - ::FileUtils.collect_method(:noop).each do |name| - module_eval(<<-EOS, __FILE__, __LINE__ + 1) - def #{name}(*args) - super(*fu_update_option(args, :noop => true, :verbose => true)) - end - private :#{name} - EOS - end - extend self - class << self - ::FileUtils::METHODS.each do |m| - public m - end - end end end - -# vi: sts=2 ts=2 sw=2 |