#!/usr/bin/env ruby
#-*-ruby-*-
require "dpklib/command"
require "dpklib/file"
require "dpklib/mkpasswd"
require "dpklib/parsearg"
require "dpklib/unix"

class DpkrbReleaseProgram < Dpklib::CommandLineProgram
  VERSION_FILE = "VERSION"

  def start
    opts = Dpklib.parse_args(argv, "fTe",
                             *%w'C: d: cvs-clean tarball package: version: keep-tmp')
    opts || usage
    @is_tarball = opts[:tarball] || opts[:T]
    @is_remove_pkgdir = opts[:T]
    @is_force = opts[:f]
    @is_clean_cvs = opts["cvs-clean"]
    @is_keep_tmp = opts["keep-tmp"]
    @chdir = opts[:C]
    @package = opts[:package]
    @version = opts[:version]
    @release_dir_name = opts[:d]
    @release_dir_name &&= exclude_dirpart(@release_dir_name)
    if opts[:e]
      @package || @release_dir_name ||
        die("Option -e always must be with -d or --package.")
      @checkout_prog_and_args = opts.args
    else
      @srctree_dir = File.expand_path(opts.shift || usage)
    end
    
    Dir.chdir(@chdir) if @chdir

    tmpdir = temp_release_dir
    surely_clean(tmpdir)
    begin
      if @srctree_dir
        @package ||= File.basename(@srctree_dir)
        system_or_die("copy entire source tree", "cp", "-a",
                      @srctree_dir, tmpdir)
      elsif @checkout_prog_and_args
        execute_checkout(tmpdir)
      else
        die("Can't happen.")
      end

      @version ||= get_default_version(tmpdir)
      @release_dir_name ||= get_release_dir_name
      
      surely_clean(@release_dir_name)
      File.rename(tmpdir, @release_dir_name)
    ensure
      @is_keep_tmp || system("rm", "-rf", tmpdir)
    end
    clean_cvs if @is_clean_cvs
    output = if @is_tarball
               create_tarball               
             else
               @release_dir_name
             end
    system("rm", "-rf", @release_dir_name) if @is_remove_pkgdir
    stdout.puts(output)
  end

  def exclude_dirpart(dir)
    dirpart = File.dirname(dir)
    if dirpart == "."
      dir
    else
      @chdir = @chdir ? File.expand_path(dirpart, @chdir) : dirpart
      File.basename(dir)
    end
  end

  def execute_checkout(release_dir)
    system_or_die("create release directory", "mkdir", release_dir)
    pid = Dpklib.fork_with_exit! {
      Dir.chdir(release_dir)
      Kernel.exec(*@checkout_prog_and_args)
    }
    Process.waitpid(pid)
    ($? == 0) || die("Checkout failed.")
  end

  def surely_clean(file)
    if File.exist?(file)
      if @is_force
        system_or_die("clean #{file}", "rm", "-rf", file)
      else
        die("Cannot overwrite #{file}.")
      end
    end
  end

  def clean_cvs
    files = []
    for cvspat in Dpklib::CvsIgnore::PATTERNS
      Dpklib.system_popen("r", "find", @release_dir_name,
                          "-name", cvspat) { |input|
        while line = input.gets
          files << line.chomp
        end
      }
    end
    system_or_die("delete cvs-clean files", "rm", "-rf", *files)
  end

  def create_tarball
    tarball_name = "#{@release_dir_name}.tar.gz"
    surely_clean(tarball_name)
    system_or_die("create tarball", "tar", "-zcf",
                  tarball_name, @release_dir_name)
    tarball_name
  end

  def system_or_die(task, *execargs)
    system(*execargs) || die("Failed to #{task}.")
  end

  def get_release_dir_name
    @version ? "#{@package}-#{@version}" : @package
  end

  def get_default_version(release_dir)
    verfile = File.expand_path(VERSION_FILE, release_dir)
    if File.exist?(verfile)
      Dpklib.read_file(verfile)
    else
      nil
    end
  end

  def temp_release_dir
    "dpkrbrel.#{Dpklib.mkpasswd_alphanum(10)}"
  end
    
  def usage
    cvsignores = Dpklib::CvsIgnore::PATTERNS.join(" ")

    die "
usage: #{progname} [OPTIONS] SRCTREE_DIR
OPTIONS:
  -C OUTPUT_DIR: (default: .)
    Releases files or directories on OUTPUT_DIR.
  -d RELEASE_DIRECTORY: (default: PACKAGE_NAME-VERSION)
    Specifies release directory. If RELEASE_DIRECTORY contains
    directory part, #{progname} concats that to end of OUTPUT_DIR.
  -e:
    This option must be with PROGRAM [ARGS ...] instead of
    SRCTREE_DIR.  If specified, #{progname} executes program at the
    release directory for obtaining source tree. This is useful when
    you are going to checkout source from CVS.
  -f:
    Overwrites existing files or directories.
  -T:
    Same as --tarball, but removes released directory.
  --cvs-clean:
    Removes all files or directory matches such patterns:
      #{cvsignores}
  --tarball:
    Creates tarball. (tar+gz)
  --package PACKAGE_NAME: (default: basename of SRCTREE_DIR)
    The name of package to release.
  --version VERSION: (default: See below.)
    Specifies version part. If not specified, #{progname} tries to
    fetch one from #{VERSION_FILE} file on the top of source
    tree. When it does not exist, version decolation is turned
    disabled.
  --keep-tmp:
    Don't clean temporary directory on failure.
"
  end

  execute
end #/DpkrbReleaseProgram
