#!/usr/bin/env ruby
# -*- ruby -*-
#
# Copyright (c) 2000-2001 Akinori MUSHA
#
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.
#

RCS_ID = %q$Idaemons: /home/cvs/pkgtools/portversion,v 1.25 2001/08/15 17:29:57 knu Exp $
RCS_REVISION = RCS_ID.split[2]
MYNAME = File.basename($0)

require "optparse"
require "pkgtools"
require "thread"

$command_output = false
$command = 'portupgrade'
$regex = false
$inv_limit_chars = nil
$limit_chars = nil
$test_version = false

COLUMNSIZE = 24
NEXTLINE = "\n%*s" % [5 + COLUMNSIZE, '']

INEQ_SIGNS = [
  '<',
  '=',
  '>',
  '?',
]

SUGGESTIONS = [
  'needs updating (port has %s)',
  'up-to-date with port',
  'succeeds port (port has %s)',
  'origin unknown',
]

def main(argv)
  usage = "usage: #{MYNAME} [-hEv] [-l limit_chars] [-L inv_limit_chars]\n" +
    "         [pkgname_glob ...]\n" +
    "       #{MYNAME} [-v] -t ver0 ver1 [ver2 ...]"

  banner = <<-"EOF"
#{MYNAME} rev.#{RCS_REVISION}

#{usage}
    EOF

  OptionParser.new(banner, COLUMNSIZE) do |opts|
    opts.on("-h", "--help", "Show this message") {
      print opts
      return 0
    }

    opts.on("-c", "--command-output", "Enable command output") {
      |$command_output|
    }

    opts.on("-C", "--command-args=ARGS", "Specify the arguments for portupgrade in command#{NEXTLINE}output (default: none)") {
      |command_args|
      $command = 'portupgrade ' + command_args
    }

    opts.on("-E", "--regex", "Use extended regular expression for pkg_glob") {
      |$regex|
    }

    opts.on("-l", "--limit=CHARS", "Only include the packages with the specified#{NEXTLINE}status flags") {
      |$limit_chars|
    }

    opts.on("-L", "--inv-limit=CHARS", "Exclude the packages with the specified#{NEXTLINE}status flags") {
      |$inv_limit_chars|
    }

    opts.on("-t", "--test=VERSION", "Compare the version with the following one(s),#{NEXTLINE}and print the result(s)") {
      |$test_version|
      $test_version = PkgVersion.new($test_version)
    }

    opts.on("-v", "--verbose", "Be verbose") {
      |$verbose|
    }

    opts.on_tail <<-"EOF".chomp

pkgname_glob is one of these: a full pkgname, a pkgname w/o version,
a shell glob pattern in which you can use the wildcards *, ?, and [..],
or an extended regular expression if -E/--regex is specified.
If none is given, all the installed packages are checked.
      EOF

    dummy = '*'
    argv.push dummy

    begin
      opts.order(*argv) do |arg|
	next if arg.empty?

	unless arg.equal? dummy
	  dummy.replace ''
	end

	if $test_version
	  version = PkgVersion.new(arg)
	  x = $test_version <=> version
	  sign = INEQ_SIGNS[if x < 0 then 0 elsif x == 0 then 1 else 2 end]

	  if $verbose
	    puts [$test_version, sign, version].join(' ')
	  else
	    puts sign
	  end
      
	  next
	end

	pattern = $regex ? Regexp.new(arg) : arg

	pkgs = $pkgdb.glob(pattern)

	if pkgs.empty?
	  warning_message "No such package '#{arg}' is found."
	  next
	end

	check_pkgs(pkgs)
      end
    rescue OptionParser::ParseError => e
      STDERR.puts "#{MYNAME}: #{e}", usage
      exit 64
    rescue ArgumentError => e
      STDERR.puts "#{MYNAME}: #{e}", usage
      exit 64
    end

    if $command_output
      printf %Q`\nif [ X"$pkgs" != X"" ]; then\n  %s $pkgs\nfi\n`, $command
    end
  end

  0
end

def check_pkg(pkg)
  pkgname = pkg.fullname
  origin = pkg.origin

  if origin.nil?
    return [3, nil]	# ? (origin unknown)
  end

  portinfo = $portsdb[origin]

  if portinfo.nil?
    return [3, nil]	# ? (origin not found)
  end

  newpkg = portinfo.pkgname

  cmp = newpkg.version <=> pkg.version

  if cmp > 0 then
    return [0, newpkg]	# >
  elsif cmp < 0 then
    return [2, newpkg]	# <
  end

  return [1, newpkg]	# =
end

def check_pkgs(pkgs)
  pkgs.each do |pkg|
    ineq, newpkg = check_pkg(pkg)

    unless $command_output && ineq != 0
      sign = INEQ_SIGNS[ineq]

      unless ($limit_chars && !$limit_chars.include?(sign)) ||
	  ($inv_limit_chars && $inv_limit_chars.include?(sign))
	if $command_output
	  printf %Q`#\n#  %s\n#  %s\n#\npkgs="$pkgs %s"\n\n`,
	    pkg.name,
	    SUGGESTIONS[ineq] % [ newpkg ? newpkg.version : nil ],
	    pkg.fullname
	else
	  if $verbose then
	    printf "%-26s  %s  %s\n",
	      pkg.fullname,
	      INEQ_SIGNS[ineq],
	      SUGGESTIONS[ineq] % [ newpkg ? newpkg.version : nil ]
	  else
	    printf "%-26s  %s\n",
	      pkg.name,
	      INEQ_SIGNS[ineq]
	  end
	end
      end
    end
  end
end

def signal_handler(sig)
  puts "\nInterrupted."
  exit
end

if $0 == __FILE__
  for sig in [2, 3, 15]
    trap(sig) do
      signal_handler(sig)
    end
  end

  exit main(ARGV)
end
