#
# fileutils.rb
#
#   Copyright (c) 2000 Minero Aoki <aamine@dp.u-netsurf.ne.jp>
#
#   This program is free software.
#   You can distribute/modify this program under the terms of
#   the GNU General Public License version 2 or later.
#

module FILEUTILS

  extend FileTest

  Separator = File::SEPARATOR

  BSIZE = 1024 * 2  # 2KB


  class << self


  def join( *args )
    args.flatten!
    args.join( Separator )
  end


  def cd( dn )
    if iterator? then
      pre = Dir.pwd
      begin
        Dir.chdir dn
        yield
      ensure
        Dir.chdir pre
      end
    else
      Dir.chdir dn
    end
  end
  alias chdir cd

  def is_newer?( new, old )
    return false unless exist? new
    return true unless exist? old
    File.ctime(new) >= File.ctime(old)
  end
  alias uptodate? is_newer?

  def is_older?( old, new )
    return false unless exist? new
    return true unless exist? old
    File.ctime(new) >= File.ctime(old)
  end


  def mkdir( dn )
    return if directory? dn
    Dir.mkdir dn
  end

  def mkdir_p( dn )
    return if directory? dn
    mkdir_p File.dirname dn
    Dir.mkdir dn unless File.basename(dn) == ''
  end


  def ln( old, new )
    dest = fu_make_dest_fname( old, new )
    File.link old, dest
  end

  def ln_s( old, new )
    dest = fu_make_dest_fname( old, new )
    File.symlink old, dest
  end


  def cp( from, to )
    dest = fu_make_dest_fname( from, to )

    mod = File.stat( from ).mode
    rf = File.open( from )     ; rf.binmode
    wf = File.open( dest, 'w' ); wf.binmode
    s = nil

    begin
      while s = rf.sysread( BSIZE * 16 ) do
        wf.syswrite s
      end
    rescue EOFError
      File.chmod mod, dest
    ensure
      rf.close
      wf.close
    end
  end

  def cp_r( from, to )
    if directory? from then
      from = File.expand_path(from)
      to = File.expand_path(to)
      unless directory? to then
        mkdir to
      end
      do_cp_r from, '.', to
    else
      cp from, to
    end
  end

  def do_cp_r( base, abs, to )
    dirs = nil
    Dir.open( join base, abs ) {|d| dirs = d.to_a }
    dirs.each do |fn|
      if directory? fn then
        next if /\A\.\.?\z/o === fn
        mkdir join(to, abs, fn)
        do_cp_r base, join(abs, fn), to
      else
        cp join(base, abs, fn), join(to, abs, fn)
      end
    end
  end
  private :do_cp_r


  def mv( from, to )
    dest = fu_make_dest_fname( from, to )
    st = File.stat( from )
    unless defined? File::ALT_SEPARATOR then
      File.unlink dest
    end

    begin
      File.rename from, dest
    rescue
      if symlink? from then
        File.symlink File.readlink(from), dest
        File.unlink from
      else
        cp from, dest
        File.unlink from
        File.utime st.atime, st.mtime, dest
        begin
          File.chown st.uid, st.gid, dest
        rescue
        end
      end
    end
  end


  def rm( fn )
    File.unlink fn
  end

  def rm_f( fn )
    begin
      st = File.lstat(fn)
    rescue Errno::ENOENT
      return
    end
    return if st.directory?
    File.chmod 0777, fn unless st.symlink?
    File.unlink fn
  end

  def rm_rf( *args )
    args.flatten!
    args.each do |i|
      if directory? i then
        do_rmrf i
      else
        rm_f i
      end
    end
  end

  def do_rmrf( dn )
    Dir.foreach( dn ) do |fn|
      next if /\A\.\.?\z/ === fn
      fn = join( dn, fn )
      if directory? fn then
        do_rmrf fn
      else
        rm_f fn
      end
    end
    Dir.rmdir dn
  end
  private :do_rmrf


  def cmp( filea, fileb )
    a = File.open( filea ); a.binmode
    b = File.open( fileb ); b.binmode

    stra = strb = ''
    begin
      while stra == strb do
        stra = a.read( BSIZE )
        strb = b.read( BSIZE )
        unless stra and strb then
          if stra.nil? and strb.nil? then
            return true
          end
        end
      end
    rescue EOFError
      ;
    ensure
      a.close
      b.close
    end

    false
  end


  def install( from, to, mode = nil )
    dest = fu_make_dest_fname( from, to )
    unless exist? dest and cmp(from, dest) then
      rm_f dest
      cp from, dest
      File.chmod mode, dest if mode
    end
  end


  def fu_make_dest_fname( from, to )
    if directory? to then
      (to[-1,1] == Separator ? to : to + Separator) + File.basename(from)
    else
      to
    end
  end
  private :fu_make_dest_fname


  def touch( fname )
    File.open( fname, File.exist?(fname) ? 'r' : 'w' ) {}
  end


  end   # class << self

end
