From: Minero Aoki Date: 2000-07-31T19:29:46+09:00 Subject: [ruby-dev:10465] fileutils.rb あおきです。 ちょっと前に話が出た fileutils.rb を書きました。試してみてください。 # きっと、バグがある ------------------------------------------------------------------- あおきみねろう =begin = fileutils.rb Copyright (c) 2000 Minero Aoki This program is free software. You can distribute/modify this program under the terms of the Ruby Distribute Lisence. = Classes == module FileUtils, FileUtils::Normal === Module Functions ---- cd( dirname ) ---- cd( dirname ) {|dirname| .... } chdir(2) to DIRNAME. If this method is called as iterator, chdir(2) to old dirctory after the block execution finished. ---- pwd ---- getwd gets name of working dirctory. same to Dir.pwd ---- is_newer?( newer, *older ) ---- uptodate?( newer, *older ) checks if "newer" is newer than all "older". ---- is_older?( older, *newer ) checks if "older" is older than all "newer". ---- mkdir( *dirs ) makes directories "dirs". ---- mkdir_p( *dirs ) makes dirctories "dirs" and all its path directories. For expample, mkdir_p '/usr/local/bin/ruby' causes to make * /usr * /usr/local * /usr/local/bin * /usr/local/bin/ruby if it is not exist. ---- ln( old, new ) ---- ln( old, old ..., dir ) makes hard link "new" which links to "old". ---- ln_s( file, file ..., dir ) makes symbolic link "new" which links to "old". ---- cp( file, file ..., dir ) copies file(s) "file" to "to". ---- cp_r( file, file ..., dir ) copies file(s) "file" to "to". If "file" is a directory, this method copies its all contents recursively. ---- mv( file, file ..., dir ) moves file(s) "file" to "to". ---- rm( *files ) removes "files". This method can't remove directory. ---- rm_f( *files ) forced to remove "files". ---- rm_rf( *files ) forced to remove "file". If "file" is a directory, this method removes its all contents recursively. ---- cmp( a, b ) ---- identical?( a, b ) returns true if file "a" and "b" is identical. ---- install( from, to, mode = ) If "from" is not same to "to", copy it. ---- chmod( mode, *files ) changes mode of "files" to "mode". == FileUtils::Verbose This class has all methods of FileUtils::Normal and it works as same, but outputs messages before action. You can also pass verbose flag to all methods, as last argument (default is "true"). == FileUtils::NoWrite This class has all methods of FileUtils::Normal but never changes files. =end module FileUtils # # FileUtils::Normal # Normal = self module_function def cd( dn ) if iterator? then pre = Dir.pwd begin Dir.chdir dn yield dn ensure Dir.chdir pre end else Dir.chdir dn end end alias chdir cd def pwd Dir.pwd end alias getwd pwd def is_newer?( new, *fnames ) fnames.each do |old| return false unless FileTest.exist? new return false unless File.ctime(new) >= File.ctime(old) end true end alias uptodate? is_newer? def is_older?( old, *fnames ) fnames.each do |old| return true unless FileTest.exist? old return false unless File.ctime(new) >= File.ctime(old) end true end def mkdir( *args ) args.flatten! args.each do |dn| Dir.mkdir dn unless FileTest.directory? dn end end def mkdir_p( *args ) args.flatten! stack = [] dn = nil args.each do |dn| until FileTest.directory? dn do stack.push dn dn = File.dirname(dn) end stack.reverse_each do |dn| Dir.mkdir dn end stack.clear end end def ln( *args ) FUPrivate.each_src_dest( args ) do |from, to| File.link from, to end end def ln_s( old, new ) FUPrivate.each_src_dest( args ) do |from, to| File.symlink from, to end end def cp( *args ) size = s = w = nil FUPrivate.each_src_dest( args ) do |from, to| FUPrivate.copy from, to end end def cp_r( *args ) FUPrivate.each_src_dest( args ) do |from, to| if FileTest.directory? from then unless FileTest.directory? to then Dir.mkdir to end FUPrivate.do_cp_r from, '.', to else FUPrivate.copy from, to end end end def mv( *args ) FUPrivate.each_src_dest( args ) do |from, to| st = File.stat( from ) if File::ALT_SEPARATOR then File.unlink to end begin File.rename from, to rescue if FileTest.symlink? from then File.symlink File.readlink(from), to File.unlink from else FUPrivate.copy from, to File.unlink from File.utime st.atime, st.mtime, to begin File.chown st.uid, st.gid, to rescue ; end end end end end def rm( *files ) files.flatten! files.each do |fn| File.unlink fn end end def rm_f( *files ) files.flatten! files.each do |fn| FUPrivate.rmf fn end end def rm_rf( *files ) files.flatten! files.each do |fn| if FileTest.directory? fn then FUPrivate.do_rm_rf fn else FUPrivate.rmf fn end end end def cmp( filea, fileb ) stra = strb = nil size = File.size( filea ) if File.size( fileb ) != size then return true end if size > FUPrivate::TOO_BIG then size = FUPrivate::TOO_BIG elsif size < 512 then size = 1024 end File.open( filea ) {|a| a.binmode File.open( fileb ) {|b| b.binmode begin while stra == strb do stra = a.read( size ) strb = b.read( size ) unless stra and strb then if stra.nil? and strb.nil? then return true end end end rescue EOFError ; end } } false end alias identical? cmp def install( from, to, mode = nil ) dest = FUPrivate.destfn( from, to ) unless FileTest.exist? dest and cmp( from, dest ) then File.chmod 0777, fn File.unlink fn FUPrivate.copy from, dest File.chmod mode, dest if mode end end def update_file( fname, upd ) str = nil if FileTest.file? fname then File.open( fname ) {|f| str = f.read } end if not str or str != upd then File.open( fname, 'w' ) do |f| f.write upd end end end def chmod( mode, *args ) args.flatten! File.chmod mode, *args end def touch( *args ) args.flatten! t = Time.now File.utime t, t, *args end #end # module Normal module FUPrivate TOO_BIG = 2 * 1024 * 1024 # 2 MB class << self def each_src_dest( args ) args.flatten! case args.size when 2 from, to = args if FileTest.directory? to then yield from, (to[-1,1] == '/' ? to : to + '/') + File.basename(from) else yield args end when 0, 1 raise ArgumentError, "wrong # of arguments #{args.size} for >=2" else dir = args.pop unless FileTest.directory? dir then raise ArgumentError, "must be dir: #{dir}" end unless dir[-1,1] == '/' then dir << '/' end args.each do |fn| yield fn, dir + File.basename(fn) end end end def destfn( from, to ) if FileTest.directory? to then (to[-1,1] == '/' ? to : to + '/') + File.basename(from) else to end end def copy( from, to ) mod = File.stat( from ).mode size = s = w = nil File.open( from ) {|rf| rf.binmode File.open( to, 'w' ) {|wf| wf.binmode size = File.size( from ) if size > TOO_BIG then size = TOO_BIG elsif size < 512 then size = 1024 end s = nil begin while true do s = rf.sysread( size ) w = wf.syswrite( s ) while w < s.size do w += wf.syswrite( s[ w, s.size - w ] ) end end rescue EOFError File.chmod mod, to end } } end def do_cp_r( base, abs, to ) dirs = nil Dir.open( "#{base}/#{abs}" ) {|d| dirs = d.to_a } dirs.each do |fn| if FileTest.directory? fn then next if /\A\.\.?\z/ === fn Dir.mkdir "#{to}/#{abs}/#{fn}" do_cp_r base, "#{abs}/#{fn}", to else copy "#{base}/#{abs}/#{fn}", "#{to}/#{abs}/#{fn}" end end end def rmf( fn ) if FileTest.exist? fn and not FileTest.directory? fn then File.chmod 0777, fn File.unlink fn end end def do_rm_rf( dn ) Dir.foreach( dn ) do |fn| next if /\A\.\.?\z/ === fn fn = "#{dn}/#{fn}" if FileTest.directory? fn then do_rm_rf fn else rmf fn end end Dir.rmdir dn end def getflag( arr ) arr.flatten! if arr.last == true or arr.last == false then arr.pop else true end end end; end # # FileUtils::Verbose # module Verbose @fileutils_op = FileUtils::Normal @fileutils_output = $stderr @fileutils_label = 'futils: ' def futilsmsg( msg ) @fileutils_label ||= 'futils: ' @fileutils_output ||= $stderr @fileutils_output.puts @fileutils_label + msg end module_function :futilsmsg private_class_method :futilsmsg attr_accessor :fileutils_op attr_accessor :fileutils_output attr_accessor :fileutils_label module_function def cd( dirname, verbose = true, &block ) futilsmsg "cd #{dirname}" if verbose (@fileutils_op ||= FileUtils::Normal).cd dirname, &block futilsmsg "cd #{Dir.pwd}" if verbose and block end alias chdir cd def pwd( verbose = true ) futilsmsg 'pwd' if verbose Dir.pwd end alias getwd pwd class << self def delegate( name ) name = name.id2name if name[-1,1] != '?' then outname = name.sub( '_', ' -' ) else outname = name end module_eval %^ def #{name}( *args ) futilsmsg "#{outname} \#{args.join ' '}" if FUPrivate.getflag(args) (@fileutils_op ||= FileUtils::Normal).#{name} *args end module_function :#{name} ^ end end delegate :is_newer? delegate :is_older? alias uptodate? is_newer? delegate :mkdir delegate :mkdir_p delegate :rmdir alias mkpath mkdir_p delegate :ln delegate :ln_s delegate :cp delegate :cp_r delegate :mv delegate :rm delegate :rm_f delegate :rm_rf def cmp( a, b, verbose = true ) futilsmsg "cmp #{a} #{b}" if verbose (@fileutils_op ||= FileUtils::Normal).cmp a, b end alias identical? cmp def install( from, to, mode = nil, verbose = true ) futilsmsg "install #{mode ? '%o ' % mode : ''}#{from} #{to}" if verbose (@fileutils_op ||= FileUtils::Normal).install from, to, mode end def update_file( fname, upd, verbose = true ) futilsmsg "update #{to}" if verbose (@fileutils_op ||= FileUtils::Normal).update_file fname, upd end def chmod( mode, *args ) futilsmsg sprintf('chmod %o %s', mode, args.join(' ')) if FUPrivate.getflag(args) (@fileutils_op ||= FileUtils::Normal).chmod mode, *args end delegate :touch end # module Verbose # # FileUtils::NoWrite # module NoWrite extend FileTest include FileTest class << self def delegate( fname, exec ) str = %- def #{fname.id2name}( *args, &block ) #{if exec then 'FileUtils::Normal.' + fname.id2name + '( *args, &block )' else '' end} end - module_eval str, __FILE__, @line @line += 1 end end @line = __LINE__ delegate :cd ,true delegate :pwd ,true delegate :getwd ,true delegate :uptodate? ,true delegate :is_newer? ,true delegate :is_older? ,true delegate :mkdir ,false delegate :mkdir_p ,false delegate :ln ,false delegate :ln_s ,false delegate :cp ,false delegate :cp_r ,false delegate :mv ,false delegate :rm ,false delegate :rm_f ,false delegate :rm_rf ,false delegate :cmp ,true delegate :identical? ,true delegate :install ,false delegate :update_file ,false delegate :chmod ,false delegate :touch ,false end # module Noop end = fileutils.rb Copyright (c) 2000 Minero Aoki This program is free software. You can distribute/modify this program under the terms of the Ruby Distribute Lisence. == module FileUtils::Simple === モジュールメソッド ---- cd( dirname ) ---- cd( dirname ) {|dirname| .... } ディレクトリ dirname に移動します。 イテレータとして呼ばれた時はブロック終了後に 元のディレクトリに戻ります。 ---- pwd ---- getwd カレントディレクトリの名前を返します。 Dir.pwd と同じです。 ---- is_newer?( newer, *older ) newer が全ての older より新しいとき真。 存在しないファイルは無限に古いとみなされます。 ---- is_older?( older, *newer ) older が全ての newer より古いとき真。 存在しないファイルは無限に古いとみなされます。 ---- mkdir( *dirs ) ディレクトリ dirs を作成します。 ---- mkdir_p( *dirs ) ディレクトリ *dirs とそのパスのディレクトリを全て作成します。 例えば mkdir_p '/usr/local/bin/ruby' は /、/usr、/usr/local、 /usr/local/bin、/usr/local/bi/ruby の全てを(なければ)作成します。 ---- ln( old, new ) ---- ln( file, file ..., dir ) old へのハードリンク new を作成します。 三つ以上の引数を与えたときはディレクトリ dir の中に その他のファイルへのリンクを作成します。 ---- ln_s( old, new ) ---- ln_s( file, file ..., dir ) old へのシンボリックリンク new を作成します。 三つ以上の引数を与えたときはディレクトリ dir の中に その他のファイルへのリンクを作成します。 ---- cp( from, to ) ---- cp( file, file ..., dir ) from を to にコピーします。to がディレクトリなら to/from に コピーします。三つ以上の引数を与えたときはディレクトリ dir の 中にその他のファイルをコピーします。 ---- cp_r( from, to ) ---- cp_r( file, file ..., dir ) from を to にコピーします。to がディレクトリなら to/from に コピーします。ディレクトリも再帰的にコピーできます。また、 三つ以上の引数を与えたときはディレクトリ dir の中にその他の ファイルをコピーします。 ---- mv( from, to ) ---- mv( file, file ..., dir ) from を to に移動します。to がディレクトリなら to/from に 移動します。三つ以上の引数を与えたときはディレクトリ dir の 中にその他のファイルを移動します。 ---- rm( file, file ... ) 引数に与えたファイルを消去します。 ---- rm_f( file. file ... ) 引数に与えたファイルを所有しているなら rm より確実に消去します。 また、ファイルが存在しなくても例外を発生しません。 ---- cmp( a, b ) ---- identical?( a, b ) ファイル a と b が同じなら真。 ---- install( from, to, mode = ) from と to の内容が違うときだけ from を to にコピーします。 そのさいモードを mode に設定します。 ---- chmod( mode, file, file ... ) ファイル file のモードを mode に変えます。 mode は八進数を使って chmod 0644, fname のように与えます。 == FileUtils::Verbose FileUtils::Normal と同じメソッドが定義されており全く同じ 動作をしますが、実行前にしようとしていることを表示します。 === メソッド ---- fileutils_op ---- fileutils_op=( operator ) メソッドを転送するオブジェクトです。 デフォルトは FileUtils::Normal です。 ---- fileutils_output ---- fileutils_output=( out ) メッセージを出力するオブジェクトです。 out は puts を持っていなければいけません。 デフォルトは stderr です。 ---- fileutils_label ---- fileutils_label=( str ) メッセージの最初につくラベルです。 デフォルトは 'futils: ' です。 == FileUtils::NoWrite FileUtils::Normal と同じメソッドが定義されていますが、 実際にファイルを変更する操作は実行しません。