lib/fileutils.rb


DEFINITIONS

This source file includes following functions.


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