Advertisement
LiTTleDRAgo

[Ruby] Fileutils.rb (untouched)

Oct 2nd, 2013
158
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Ruby 23.56 KB | None | 0 0
  1. #
  2. # = fileutils.rb
  3. #
  4. # Copyright (c) 2000-2003 Minero Aoki <aamine@loveruby.net>
  5. #
  6. # This program is free software.
  7. # You can distribute/modify this program under the same terms of ruby.
  8. #
  9. # == module FileUtils
  10. #
  11. # Namespace for several file utility methods for copying, moving, removing, etc.
  12. #
  13. # === Module Functions
  14. #
  15. #   cd( dir, options )
  16. #   cd( dir, options ) {|dir| .... }
  17. #   pwd()
  18. #   mkdir( dir, options )
  19. #   mkdir_p( dir, options )
  20. #   rmdir( dir, options )
  21. #   ln( old, new, options )
  22. #   ln( list, destdir, options )
  23. #   ln_s( old, new, options )
  24. #   ln_s( list, destdir, options )
  25. #   ln_sf( src, dest, options )
  26. #   cp( src, dest, options )
  27. #   cp( list, dir, options )
  28. #   cp_r( src, dest, options )
  29. #   cp_r( list, dir, options )
  30. #   mv( src, dest, options )
  31. #   mv( list, dir, options )
  32. #   rm( list, options )
  33. #   rm_r( list, options )
  34. #   rm_rf( list, options )
  35. #   install( src, dest, mode = <src's>, options )
  36. #   chmod( mode, list, options )
  37. #   touch( list, options )
  38. #
  39. # The <tt>options</tt> parameter is a hash of options, taken from the list
  40. # +:force+, +:noop+, +:preserve+, and +:verbose+.  +:noop+ means that no changes
  41. # are made.  The other two are obvious.  Each method documents the options that
  42. # it honours.
  43. #
  44. # All methods that have the concept of a "source" file or directory can take
  45. # either one file or a list of files in that argument.  See the method
  46. # documentation for examples.
  47. #
  48. # There are some `low level' methods, which does not accept any option:
  49. #
  50. #   uptodate?( file, cmp_list )
  51. #   copy_file( srcfilename, destfilename )
  52. #   copy_stream( srcstream, deststream )
  53. #   compare_file( file_a, file_b )
  54. #   compare_stream( stream_a, stream_b )
  55. #
  56. # == module FileUtils::Verbose
  57. #
  58. # This module has all methods of FileUtils module, but it outputs messages
  59. # before acting.  This equates to passing the +:verbose+ flag to methods in
  60. # FileUtils.
  61. #
  62. # == module FileUtils::NoWrite
  63. #
  64. # This module has all methods of FileUtils module, but never changes
  65. # files/directories.  This equates to passing the +:noop+ flag to methods in
  66. # FileUtils.
  67. #
  68.  
  69.  
  70. module FileUtils
  71.  
  72.   # All methods are module_function.
  73.  
  74.   #
  75.   # Options: (none)
  76.   #
  77.   # Returns the name of the current directory.
  78.   #
  79.   def pwd
  80.     Dir.pwd
  81.   end
  82.  
  83.   alias getwd pwd
  84.  
  85.   #
  86.   # Options: noop verbose
  87.   #
  88.   # Changes the current directory to the directory +dir+.
  89.   #
  90.   # If this method is called with block, resumes to the old
  91.   # working directory after the block execution finished.
  92.   #
  93.   #   FileUtils.cd('/', :verbose => true)   # chdir and report it
  94.   #
  95.   def cd( dir, options = {}, &block ) # :yield: dir
  96.     fu_check_options options, :noop, :verbose
  97.     fu_output_message "cd #{dir}" if options[:verbose]
  98.     Dir.chdir(dir, &block) unless options[:noop]
  99.     fu_output_message 'cd -' if options[:verbose] and block
  100.   end
  101.  
  102.   alias chdir cd
  103.  
  104.  
  105.   #
  106.   # Options: (none)
  107.   #
  108.   # Returns true if +newer+ is newer than all +old_list+.
  109.   # Non-existent files are older than any file.
  110.   #
  111.   #   FileUtils.uptodate?('hello.o', %w(hello.c hello.h)) or \
  112.   #       system 'make hello.o'
  113.   #
  114.   def uptodate?( new, old_list, options = nil )
  115.     raise ArgumentError, 'uptodate? does not accept any option' if options
  116.  
  117.     return false unless FileTest.exist?(new)
  118.     new_time = File.mtime(new)
  119.     old_list.each do |old|
  120.       if FileTest.exist?(old)
  121.         return false unless new_time > File.mtime(old)
  122.       end
  123.     end
  124.     true
  125.   end
  126.  
  127.  
  128.   #
  129.   # Options: mode noop verbose
  130.   #
  131.   # Creates one or more directories.
  132.   #
  133.   #   FileUtils.mkdir 'test'
  134.   #   FileUtils.mkdir %w( tmp data )
  135.   #   FileUtils.mkdir 'notexist', :noop => true  # Does not really create.
  136.   #   FileUtils.mkdir 'tmp', :mode => 0700
  137.   #
  138.   def mkdir( list, options = {} )
  139.     fu_check_options options, :mode, :noop, :verbose
  140.     list = fu_list(list)
  141.     fu_output_message "mkdir #{options[:mode] ? ('-m %03o ' % options[:mode]) : ''}#{list.join ' '}" if options[:verbose]
  142.     return if options[:noop]
  143.  
  144.     mode = options[:mode] || (0777 & ~File.umask)
  145.     list.each do |dir|
  146.       Dir.mkdir dir, mode
  147.     end
  148.   end
  149.  
  150.   #
  151.   # Options: mode noop verbose
  152.   #
  153.   # Creates a directory and all its parent directories.
  154.   # For example,
  155.   #
  156.   #   FileUtils.mkdir_p '/usr/local/lib/ruby'
  157.   #
  158.   # causes to make following directories, if it does not exist.
  159.   #     * /usr
  160.   #     * /usr/local
  161.   #     * /usr/local/lib
  162.   #     * /usr/local/lib/ruby
  163.   #
  164.   # You can pass several directories at a time in a list.
  165.   #
  166.   def mkdir_p( list, options = {} )
  167.     fu_check_options options, :mode, :noop, :verbose
  168.     list = fu_list(list)
  169.     fu_output_message "mkdir -p #{options[:mode] ? ('-m %03o ' % options[:mode]) : ''}#{list.join ' '}" if options[:verbose]
  170.     return *list if options[:noop]
  171.  
  172.     mode = options[:mode] || (0777 & ~File.umask)
  173.     list.map {|n| File.expand_path(n) }.each do |dir|
  174.       stack = []
  175.       until FileTest.directory?(dir)
  176.         stack.push dir
  177.         dir = File.dirname(dir)
  178.       end
  179.       stack.reverse_each do |n|
  180.         Dir.mkdir n, mode
  181.       end
  182.     end
  183.  
  184.     return *list
  185.   end
  186.  
  187.   alias mkpath    mkdir_p
  188.   alias makedirs  mkdir_p
  189.  
  190.  
  191.   #
  192.   # Options: noop, verbose
  193.   #
  194.   # Removes one or more directories.
  195.   #
  196.   #   FileUtils.rmdir 'somedir'
  197.   #   FileUtils.rmdir %w(somedir anydir otherdir)
  198.   #   # Does not really remove directory; outputs message.
  199.   #   FileUtils.rmdir 'somedir', :verbose => true, :noop => true
  200.   #
  201.   def rmdir( list, options = {} )
  202.     fu_check_options options, :noop, :verbose
  203.     list = fu_list(list)
  204.     fu_output_message "rmdir #{list.join ' '}" if options[:verbose]
  205.     return if options[:noop]
  206.  
  207.     list.each do |dir|
  208.       Dir.rmdir dir
  209.     end
  210.   end
  211.  
  212.  
  213.   #
  214.   # Options: force noop verbose
  215.   #
  216.   # <b><tt>ln( old, new, options = {} )</tt></b>
  217.   #
  218.   # Creates a hard link +new+ which points to +old+.
  219.   # If +new+ already exists and it is a directory, creates a symbolic link +new/old+.
  220.   # If +new+ already exists and it is not a directory, raises Errno::EEXIST.
  221.   # But if :force option is set, overwrite +new+.
  222.   #
  223.   #   FileUtils.ln 'gcc', 'cc', :verbose => true
  224.   #   FileUtils.ln '/usr/bin/emacs21', '/usr/bin/emacs'
  225.   #
  226.   # <b><tt>ln( list, destdir, options = {} )</tt></b>
  227.   #
  228.   # Creates several hard links in a directory, with each one pointing to the
  229.   # item in +list+.  If +destdir+ is not a directory, raises Errno::ENOTDIR.
  230.   #
  231.   #   include FileUtils
  232.   #   cd '/bin'
  233.   #   ln %w(cp mv mkdir), '/usr/bin'   # Now /usr/bin/cp and /bin/cp are linked.
  234.   #
  235.   def ln( src, dest, options = {} )
  236.     fu_check_options options, :force, :noop, :verbose
  237.     fu_output_message "ln#{options[:force] ? ' -f' : ''} #{[src,dest].flatten.join ' '}" if options[:verbose]
  238.     return if options[:noop]
  239.  
  240.     fu_each_src_dest(src, dest) do |s,d|
  241.       remove_file d, true if options[:force]
  242.       File.link s, d
  243.     end
  244.   end
  245.  
  246.   alias link ln
  247.  
  248.   #
  249.   # Options: force noop verbose
  250.   #
  251.   # <b><tt>ln_s( old, new, options = {} )</tt></b>
  252.   #
  253.   # Creates a symbolic link +new+ which points to +old+.  If +new+ already
  254.   # exists and it is a directory, creates a symbolic link +new/old+.  If +new+
  255.   # already exists and it is not a directory, raises Errno::EEXIST.  But if
  256.   # :force option is set, overwrite +new+.
  257.   #
  258.   #   FileUtils.ln_s '/usr/bin/ruby', '/usr/local/bin/ruby'
  259.   #   FileUtils.ln_s 'verylongsourcefilename.c', 'c', :force => true
  260.   #
  261.   # <b><tt>ln_s( list, destdir, options = {} )</tt></b>
  262.   #
  263.   # Creates several symbolic links in a directory, with each one pointing to the
  264.   # item in +list+.  If +destdir+ is not a directory, raises Errno::ENOTDIR.
  265.   #
  266.   # If +destdir+ is not a directory, raises Errno::ENOTDIR.
  267.   #
  268.   #   FileUtils.ln_s Dir.glob('bin/*.rb'), '/home/aamine/bin'
  269.   #
  270.   def ln_s( src, dest, options = {} )
  271.     fu_check_options options, :force, :noop, :verbose
  272.     fu_output_message "ln -s#{options[:force] ? 'f' : ''} #{[src,dest].flatten.join ' '}" if options[:verbose]
  273.     return if options[:noop]
  274.  
  275.     fu_each_src_dest(src, dest) do |s,d|
  276.       remove_file d, true if options[:force]
  277.       File.symlink s, d
  278.     end
  279.   end
  280.  
  281.   alias symlink ln_s
  282.  
  283.   #
  284.   # Options: noop verbose
  285.   #
  286.   # Same as
  287.   #   #ln_s(src, dest, :force)
  288.   #
  289.   def ln_sf( src, dest, options = {} )
  290.     fu_check_options options, :noop, :verbose
  291.     options = options.dup
  292.     options[:force] = true
  293.     ln_s src, dest, options
  294.   end
  295.  
  296.  
  297.   #
  298.   # Options: preserve noop verbose
  299.   #
  300.   # Copies a file +src+ to +dest+. If +dest+ is a directory, copies
  301.   # +src+ to +dest/src+.
  302.   #
  303.   # If +src+ is a list of files, then +dest+ must be a directory.
  304.   #
  305.   #   FileUtils.cp 'eval.c', 'eval.c.org'
  306.   #   FileUtils.cp %w(cgi.rb complex.rb date.rb), '/usr/lib/ruby/1.6'
  307.   #   FileUtils.cp %w(cgi.rb complex.rb date.rb), '/usr/lib/ruby/1.6', :verbose => true
  308.   #
  309.   def cp( src, dest, options = {} )
  310.     fu_check_options options, :preserve, :noop, :verbose
  311.     fu_output_message "cp#{options[:preserve] ? ' -p' : ''} #{[src,dest].flatten.join ' '}" if options[:verbose]
  312.     return if options[:noop]
  313.  
  314.     fu_each_src_dest(src, dest) do |s,d|
  315.       fu_preserve_attr(options[:preserve], s, d) {
  316.           copy_file s, d
  317.       }
  318.     end
  319.   end
  320.  
  321.   alias copy cp
  322.  
  323.   #
  324.   # Options: preserve noop verbose
  325.   #
  326.   # Copies +src+ to +dest+. If +src+ is a directory, this method copies
  327.   # all its contents recursively. If +dest+ is a directory, copies
  328.   # +src+ to +dest/src+.
  329.   #
  330.   # +src+ can be a list of files.
  331.   #
  332.   #   # Installing ruby library "mylib" under the site_ruby
  333.   #   FileUtils.rm_r site_ruby + '/mylib', :force
  334.   #   FileUtils.cp_r 'lib/', site_ruby + '/mylib'
  335.   #
  336.   #   # Examples of copying several files to target directory.
  337.   #   FileUtils.cp_r %w(mail.rb field.rb debug/), site_ruby + '/tmail'
  338.   #   FileUtils.cp_r Dir.glob('*.rb'), '/home/aamine/lib/ruby', :noop, :verbose
  339.   #
  340.   def cp_r( src, dest, options = {} )
  341.     fu_check_options options, :preserve, :noop, :verbose
  342.     fu_output_message "cp -r#{options[:preserve] ? 'p' : ''} #{[src,dest].flatten.join ' '}" if options[:verbose]
  343.     return if options[:noop]
  344.  
  345.     fu_each_src_dest(src, dest) do |s,d|
  346.       if FileTest.directory?(s)
  347.         fu_copy_dir s, d, '.', options[:preserve]
  348.       else
  349.         fu_p_copy s, d, options[:preserve]
  350.       end
  351.     end
  352.   end
  353.  
  354.   def fu_copy_dir( src, dest, rel, preserve ) #:nodoc:
  355.     fu_preserve_attr(preserve, "#{src}/#{rel}", "#{dest}/#{rel}") {|s,d|
  356.         dir = File.expand_path(d)   # to remove '/./'
  357.         Dir.mkdir dir unless FileTest.directory? dir
  358.     }
  359.     Dir.entries("#{src}/#{rel}").each do |fname|
  360.       if FileTest.directory? File.join(src,rel,fname)
  361.         next if /\A\.\.?\z/ === fname
  362.         fu_copy_dir src, dest, "#{rel}/#{fname}", preserve
  363.       else
  364.         fu_p_copy File.join(src,rel,fname), File.join(dest,rel,fname), preserve
  365.       end
  366.     end
  367.   end
  368.   private :fu_copy_dir
  369.  
  370.   def fu_p_copy( src, dest, really ) #:nodoc:
  371.     fu_preserve_attr(really, src, dest) {
  372.         copy_file src, dest
  373.     }
  374.   end
  375.   private :fu_p_copy
  376.  
  377.   def fu_preserve_attr( really, src, dest ) #:nodoc:
  378.     unless really
  379.       yield src, dest
  380.       return
  381.     end
  382.  
  383.     st = File.stat(src)
  384.     yield src, dest
  385.     File.utime st.atime, st.mtime, dest
  386.     begin
  387.       File.chown st.uid, st.gid, dest
  388.     rescue Errno::EPERM
  389.       File.chmod st.mode & 01777, dest   # clear setuid/setgid
  390.     else
  391.       File.chmod st.mode, dest
  392.     end
  393.   end
  394.   private :fu_preserve_attr
  395.  
  396.   #
  397.   # Copies file +src+ to +dest+.
  398.   # Both of +src+ and +dest+ must be a filename.
  399.   #
  400.   def copy_file( src, dest )
  401.     File.open(src,  'rb') {|r|
  402.     File.open(dest, 'wb') {|w|
  403.         copy_stream r, w
  404.     } }
  405.   end
  406.  
  407.   #
  408.   # Copies stream +src+ to +dest+.
  409.   # Both of +src+ and +dest+ must be a IO.
  410.   #
  411.   def copy_stream( src, dest )
  412.     bsize = fu_stream_blksize(src, dest)
  413.     begin
  414.       while true
  415.         dest.syswrite src.sysread(bsize)
  416.       end
  417.     rescue EOFError
  418.     end
  419.   end
  420.  
  421.   #
  422.   # Options: noop verbose
  423.   #
  424.   # Moves file(s) +src+ to +dest+.  If +file+ and +dest+ exist on the different
  425.   # disk partition, the file is copied instead.
  426.   #
  427.   #   FileUtils.mv 'badname.rb', 'goodname.rb'
  428.   #   FileUtils.mv 'stuff.rb', 'lib/ruby', :force => true
  429.   #
  430.   #   FileUtils.mv %w(junk.txt dust.txt), '/home/aamine/.trash/'
  431.   #   FileUtils.mv Dir.glob('test*.rb'), 'test', :noop, :verbose => true
  432.   #
  433.   def mv( src, dest, options = {} )
  434.     fu_check_options options, :noop, :verbose
  435.     fu_output_message "mv #{[src,dest].flatten.join ' '}" if options[:verbose]
  436.     return if options[:noop]
  437.  
  438.     fu_each_src_dest(src, dest) do |s,d|
  439.       if cannot_overwrite_file? and FileTest.file?(d)
  440.         File.unlink d
  441.       end
  442.  
  443.       begin
  444.         File.rename s, d
  445.       rescue
  446.         if FileTest.symlink?(s)
  447.           File.symlink File.readlink(s), dest
  448.           File.unlink s
  449.         else
  450.           st = File.stat(s)
  451.           copy_file s, d
  452.           File.unlink s
  453.           File.utime st.atime, st.mtime, d
  454.           begin
  455.             File.chown st.uid, st.gid, d
  456.           rescue
  457.             # ignore
  458.           end
  459.         end
  460.       end
  461.     end
  462.   end
  463.  
  464.   alias move mv
  465.  
  466.   def cannot_overwrite_file? #:nodoc:
  467.     /djgpp|cygwin|mswin32/ === RUBY_PLATFORM
  468.   end
  469.   private :cannot_overwrite_file?
  470.  
  471.  
  472.   #
  473.   # Options: force noop verbose
  474.   #
  475.   # Remove file(s) specified in +list+.  This method cannot remove directories.
  476.   # All errors are ignored when the :force option is set.
  477.   #
  478.   #   FileUtils.rm %w( junk.txt dust.txt )
  479.   #   FileUtils.rm Dir.glob('*.so')
  480.   #   FileUtils.rm 'NotExistFile', :force => true   # never raises exception
  481.   #
  482.   def rm( list, options = {} )
  483.     fu_check_options options, :force, :noop, :verbose
  484.     list = fu_list(list)
  485.     fu_output_message "rm#{options[:force] ? ' -f' : ''} #{list.join ' '}" if options[:verbose]
  486.     return if options[:noop]
  487.  
  488.     list.each do |fname|
  489.       remove_file fname, options[:force]
  490.     end
  491.   end
  492.  
  493.   alias remove rm
  494.  
  495.   #
  496.   # Options: noop verbose
  497.   #
  498.   # Same as
  499.   #   #rm(list, :force)
  500.   #
  501.   def rm_f( list, options = {} )
  502.     fu_check_options options, :noop, :verbose
  503.     options = options.dup
  504.     options[:force] = true
  505.     rm list, options
  506.   end
  507.  
  508.   alias safe_unlink rm_f
  509.  
  510.   #
  511.   # Options: force noop verbose
  512.   #
  513.   # remove files +list+[0] +list+[1]... If +list+[n] is a directory,
  514.   # removes its all contents recursively. This method ignores
  515.   # StandardError when :force option is set.
  516.   #
  517.   #   FileUtils.rm_r Dir.glob('/tmp/*')
  518.   #   FileUtils.rm_r '/', :force => true          #  :-)
  519.   #
  520.   def rm_r( list, options = {} )
  521.     fu_check_options options, :force, :noop, :verbose
  522.     list = fu_list(list)
  523.     fu_output_message "rm -r#{options[:force] ? 'f' : ''} #{list.join ' '}" if options[:verbose]
  524.     return if options[:noop]
  525.  
  526.     list.each do |fname|
  527.       begin
  528.         st = File.lstat(fname)
  529.       rescue
  530.         next if options[:force]
  531.         raise
  532.       end
  533.       if    st.symlink?   then remove_file fname, options[:force]
  534.       elsif st.directory? then remove_dir fname, options[:force]
  535.       else                     remove_file fname, options[:force]
  536.       end
  537.     end
  538.   end
  539.  
  540.   #
  541.   # Options: noop verbose
  542.   #
  543.   # Same as
  544.   #   #rm_r(list, :force => true)
  545.   #
  546.   def rm_rf( list, options = {} )
  547.     fu_check_options options, :noop, :verbose
  548.     options = options.dup
  549.     options[:force] = true
  550.     rm_r list, options
  551.   end
  552.  
  553.   alias rmtree rm_rf
  554.  
  555.   def remove_file( fname, force = false ) #:nodoc:
  556.     first_time_p = true
  557.     begin
  558.       File.unlink fname
  559.     rescue Errno::ENOENT
  560.       raise unless force
  561.     rescue
  562.       if first_time_p
  563.         # try once more for Windows
  564.         first_time_p = false
  565.         File.chmod 0777, fname
  566.         retry
  567.       end
  568.       raise
  569.     end
  570.   end
  571.  
  572.   def remove_dir( dir, force = false ) #:nodoc:
  573.     Dir.foreach(dir) do |file|
  574.       next if /\A\.\.?\z/ === file
  575.       path = "#{dir}/#{file}"
  576.       if FileTest.directory? path
  577.         remove_dir path, force
  578.       else
  579.         remove_file path, force
  580.       end
  581.     end
  582.     begin
  583.       Dir.rmdir dir
  584.     rescue Errno::ENOENT
  585.       raise unless force
  586.     end
  587.   end
  588.  
  589.  
  590.   #
  591.   # Returns true if the contents of a file A and a file B are identical.
  592.   #
  593.   #   FileUtils.compare_file('somefile', 'somefile')  #=> true
  594.   #   FileUtils.compare_file('/bin/cp', '/bin/mv')    #=> maybe false
  595.   #
  596.   def compare_file( a, b )
  597.     return false unless File.size(a) == File.size(b)
  598.     File.open(a, 'rb') {|fa|
  599.     File.open(b, 'rb') {|fb|
  600.         return compare_stream(fa, fb)
  601.     } }
  602.   end
  603.  
  604.   alias identical? compare_file
  605.   alias cmp compare_file
  606.  
  607.   #
  608.   # Returns true if the contents of a stream +a+ and +b+ are identical.
  609.   #
  610.   def compare_stream( a, b )
  611.     bsize = fu_stream_blksize(a, b)
  612.     sa = sb = nil
  613.     while sa == sb
  614.       sa = a.read(bsize)
  615.       sb = b.read(bsize)
  616.       unless sa and sb
  617.         if sa.nil? and sb.nil?
  618.           return true
  619.         end
  620.       end
  621.     end
  622.     false
  623.   end
  624.  
  625.  
  626.   #
  627.   # Options: mode noop verbose
  628.   #
  629.   # If +src+ is not same as +dest+, copies it and changes the permission
  630.   # mode to +mode+.  If +dest+ is a directory, destination is +dest+/+src+.
  631.   #
  632.   #   FileUtils.install 'ruby', '/usr/local/bin/ruby', :mode => 0755, :verbose => true
  633.   #   FileUtils.install 'lib.rb', '/usr/local/lib/ruby/site_ruby', :verbose => true
  634.   #
  635.   def install( src, dest, options = {} )
  636.     fu_check_options options, :mode, :preserve, :noop, :verbose
  637.     fu_output_message "install -c#{options[:preserve] && ' -p'}#{options[:mode] ? (' -m 0%o' % options[:mode]) : ''} #{[src,dest].flatten.join ' '}" if options[:verbose]
  638.     return if options[:noop]
  639.  
  640.     fu_each_src_dest(src, dest) do |s,d|
  641.       unless FileTest.exist?(d) and compare_file(s,d)
  642.         fu_preserve_attr(options[:preserve], s, d) {
  643.       remove_file d, true
  644.       copy_file s, d
  645.     }
  646.     File.chmod options[:mode], d if options[:mode]
  647.       end
  648.     end
  649.   end
  650.  
  651.  
  652.   #
  653.   # Options: noop verbose
  654.   #
  655.   # Changes permission bits on the named files (in +list+) to the bit pattern
  656.   # represented by +mode+.
  657.   #
  658.   #   FileUtils.chmod 0755, 'somecommand'
  659.   #   FileUtils.chmod 0644, %w(my.rb your.rb his.rb her.rb)
  660.   #   FileUtils.chmod 0755, '/usr/bin/ruby', :verbose => true
  661.   #
  662.   def chmod( mode, list, options = {} )
  663.     fu_check_options options, :noop, :verbose
  664.     list = fu_list(list)
  665.     fu_output_message sprintf('chmod %o %s', mode, list.join(' ')) if options[:verbose]
  666.     return if options[:noop]
  667.     File.chmod mode, *list
  668.   end
  669.  
  670.  
  671.   #
  672.   # Options: noop verbose
  673.   #
  674.   # Updates modification time (mtime) and access time (atime) of file(s) in
  675.   # +list+.  Files are created if they don't exist.
  676.   #
  677.   #   FileUtils.touch 'timestamp'
  678.   #   FileUtils.touch Dir.glob('*.c');  system 'make'
  679.   #
  680.   def touch( list, options = {} )
  681.     fu_check_options options, :noop, :verbose
  682.     list = fu_list(list)
  683.     fu_output_message "touch #{list.join ' '}" if options[:verbose]
  684.     return if options[:noop]
  685.  
  686.     t = Time.now
  687.     list.each do |fname|
  688.       begin
  689.         File.utime(t, t, fname)
  690.       rescue Errno::ENOENT
  691.         File.open(fname, 'a') {
  692.             ;
  693.         }
  694.       end
  695.     end
  696.   end
  697.  
  698.   private
  699.  
  700.   def fu_check_options( options, *optdecl )
  701.     h = options.dup
  702.     optdecl.each do |name|
  703.       h.delete name
  704.     end
  705.     raise ArgumentError, "no such option: #{h.keys.join(' ')}" unless h.empty?
  706.   end
  707.  
  708.   def fu_list( arg )
  709.     Array === arg ? arg : [arg]
  710.   end
  711.  
  712.   def fu_each_src_dest( src, dest )
  713.     unless Array === src
  714.       yield src, fu_dest_filename(src, dest)
  715.     else
  716.       dir = dest
  717.       # FileTest.directory? dir or raise ArgumentError, "must be dir: #{dir}"
  718.       dir += (dir[-1,1] == '/') ? '' : '/'
  719.       src.each do |fname|
  720.         yield fname, dir + File.basename(fname)
  721.       end
  722.     end
  723.   end
  724.  
  725.   def fu_dest_filename( src, dest )
  726.     if FileTest.directory? dest
  727.       (dest[-1,1] == '/' ? dest : dest + '/') + File.basename(src)
  728.     else
  729.       dest
  730.     end
  731.   end
  732.  
  733.   def fu_stream_blksize( *streams )
  734.     streams.each do |s|
  735.       next unless s.respond_to?(:stat)
  736.       size = s.stat.blksize
  737.       return size unless size.nil? or size.zero?
  738.     end
  739.     1024
  740.   end
  741.  
  742.   @fileutils_output = $stderr
  743.   @fileutils_label  = ''
  744.  
  745.   def fu_output_message( msg )
  746.     @fileutils_output ||= $stderr
  747.     @fileutils_label  ||= ''
  748.     @fileutils_output.puts @fileutils_label + msg
  749.   end
  750.  
  751.   def fu_update_option( args, new )
  752.     if Hash === args.last
  753.       args.last.update new
  754.     else
  755.       args.push new
  756.     end
  757.     args
  758.   end
  759.  
  760.  
  761.   extend self
  762.  
  763.  
  764.   OPT_TABLE = {
  765.     'pwd'          => %w(),
  766.     'cd'           => %w( noop verbose ),
  767.     'chdir'        => %w( noop verbose ),
  768.     'chmod'        => %w( noop verbose ),
  769.     'copy'         => %w( preserve noop verbose ),
  770.     'cp'           => %w( preserve noop verbose ),
  771.     'cp_r'         => %w( preserve noop verbose ),
  772.     'install'      => %w( mode noop verbose ),
  773.     'link'         => %w( force noop verbose ),
  774.     'ln'           => %w( force noop verbose ),
  775.     'ln_s'         => %w( force noop verbose ),
  776.     'ln_sf'        => %w( noop verbose ),
  777.     'makedirs'     => %w( noop verbose ),
  778.     'mkdir'        => %w( mode noop verbose ),
  779.     'mkdir_p'      => %w( mode noop verbose ),
  780.     'mkpath'       => %w( noop verbose ),
  781.     'move'         => %w( noop verbose ),
  782.     'mv'           => %w( noop verbose ),
  783.     'remove'       => %w( force noop verbose ),
  784.     'rm'           => %w( force noop verbose ),
  785.     'rm_f'         => %w( noop verbose ),
  786.     'rm_r'         => %w( force noop verbose ),
  787.     'rm_rf'        => %w( noop verbose ),
  788.     'rmtree'       => %w( noop verbose ),
  789.     'rmdir'        => %w( noop verbose ),
  790.     'safe_unlink'  => %w( noop verbose ),
  791.     'symlink'      => %w( force noop verbose ),
  792.     'touch'        => %w( noop verbose )
  793.   }
  794.  
  795.  
  796.   #
  797.   # This module has all methods of FileUtils module, but it outputs messages
  798.   # before acting.  This equates to passing the +:verbose+ flag to methods in
  799.   # FileUtils.
  800.   #
  801.   module Verbose
  802.  
  803.     include FileUtils
  804.  
  805.     @fileutils_output  = $stderr
  806.     @fileutils_label   = ''
  807.     @fileutils_verbose = true
  808.  
  809.     FileUtils::OPT_TABLE.each do |name, opts|
  810.       next unless opts.include?('verbose')
  811.       module_eval(<<-EOS, __FILE__, __LINE__ + 1)
  812.         def #{name}( *args )
  813.           @fileutils_verbose = true unless defined?(@fileutils_verbose)
  814.           super(*fu_update_option(args, :verbose => @fileutils_verbose))
  815.         end
  816.       EOS
  817.     end
  818.  
  819.     extend self
  820.  
  821.   end
  822.  
  823.  
  824.   #
  825.   # This module has all methods of FileUtils module, but never changes
  826.   # files/directories.  This equates to passing the +:noop+ flag to methods in
  827.   # FileUtils.
  828.   #
  829.   module NoWrite
  830.  
  831.     include FileUtils
  832.  
  833.     @fileutils_output  = $stderr
  834.     @fileutils_label   = ''
  835.     @fileutils_nowrite = true
  836.  
  837.     FileUtils::OPT_TABLE.each do |name, opts|
  838.       next unless opts.include? 'noop'
  839.       module_eval(<<-EOS, __FILE__, __LINE__ + 1)
  840.         def #{name}( *args )
  841.           unless defined?(@fileutils_nowrite)
  842.             @fileutils_nowrite ||= true
  843.           end
  844.           super(*fu_update_option(args, :noop => true))
  845.         end
  846.       EOS
  847.     end
  848.  
  849.     extend self
  850.  
  851.   end
  852.  
  853. end
  854.  
  855.  
  856. # Documentation comments:
  857. #  - Some RDoc markup used here doesn't work (namely, +file1+, +:noop+,
  858. #    +dir/file+).  I consider this a bug and expect that these will be valid in
  859. #    the near future.
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement