#!/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/portcvsweb,v 1.6 2001/08/25 16:35:51 knu Exp $
RCS_REVISION = RCS_ID.split[2]
MYNAME = File.basename($0)

require 'pkgtools'

BROWSER = ENV['BROWSER'] || 'lynx'

CVSWEB_URI = {
  'FreeBSD' => 'http://www.FreeBSD.org/cgi/cvsweb.cgi/',
  'NetBSD' => 'http://cvsweb.netbsd.org/bsdweb.cgi/',
  'OpenBSD' => 'http://www.openbsd.org/cgi-bin/cvsweb/',
}

CVSTAGS = CVSWEB_URI.keys

CVSTAG_RE = /\$(#{CVSTAGS.join('|')}):\s+(\S+),v\s*(\d[\d.]*)/

MAKEFILE = 'Makefile'

$tag = nil
$path = nil
$anchor = nil
$file = nil

def main(argv)
  unless argv.empty?
    $file = argv[0]

    retried = false

    loop do
      if File.directory?($file)
	$file = File.expand_path($file)

	Dir.chdir($file)
	$file = nil

	break
      elsif File.exists?($file)
	$file = File.expand_path($file)

	dir, $file = File.split(File.expand_path($file))
	Dir.chdir(dir)

	break
      elsif retried
	STDERR.puts MYNAME + ': file not found.'
	exit 1
      else
	$file = File.expand_path(File.join($ports_dir, $file))
	retried = true
      end
    end
  end

  if $file && !File.file?($file)
    STDERR.puts MYNAME + ': file not found.'
    exit 1
  end

  check_cvs()

  if $file
    if $tag.nil? || $path.nil?
      check_file($file, true) || check_makefile
    end
  else
    check_makefile
  end

  if $path.nil?
    dir, rel = File.split(Dir.pwd)

    until dir == '/'
      Dir.chdir(dir)
      check_makefile(rel) and break

      dir, last = File.split(dir)

      rel = File.join(last, rel)
    end

    if $path.nil?
      STDERR.puts MYNAME + ': CVS tag not found.'
      exit 2
    end
  end

  if $tag.nil?
    $tag = guess_tag(Dir.pwd) || guess_tag(`uname`) || 'FreeBSD'
  end

  uri = CVSWEB_URI[$tag] + $path + ($anchor || '')

  open_browser(uri)
end

def open_browser(uri)
  BROWSER.split(':').each do |s|
    s.strip!

    next if s.empty?

    s << ' %s' if not s.include?('%s')

    exec format(s, uri) rescue nil
  end

  STDERR.puts 'No browser is available - please define BROWSER.'
  exit 1
end

def check_cvs
  File.directory?('CVS') or return

  open('CVS/Repository') do |f|
    $path = File.join(f.gets.chomp, $file || '')
  end rescue nil

  open('CVS/Root') do |f|
    $tag = guess_tag(f.gets)
  end rescue nil

  if $file
    open('CVS/Entries') do |f|
      f.each do |line|
	ary = line.split('/')

	if ary[1] == $file
	  $anchor = '#rev' + ary[2]
	  break
	end
      end
    end rescue nil
  end
end

def check_makefile(subdir = nil)
  ret = check_file(MAKEFILE)

  if $path
    if subdir
      $path = File.join(File.dirname($path), subdir, $file || '')
    else
      $path = File.join(File.dirname($path), $file || '')
    end

    $anchor = nil if $file.nil?
  end

  ret
end

def check_file(file, set_anchor = false)
  IO::popen("/usr/bin/ident #{file}").each do |line|
    if CVSTAG_RE =~ line
      $tag = $1
      $anchor = '#rev' + $3 if set_anchor

      if $path.nil?
	case $tag
	when 'FreeBSD'
	  $path = $2
	when 'NetBSD'
	  if %r:/([^/]+/[^/]+)$: =~ Dir.pwd
	    $path = File.join('pkgsrc', $1, file)
	  end
	when 'OpenBSD'
	  category = `make -V CATEGORIES`.split[0]
	  if %r"/(#{Regexp.quote(category)}(?:/[^/]+){1,3})$" =~ Dir.pwd
	    $path = File.join('ports', $1, file)
	  end
	end
	break
      end
    end
  end

  true
rescue
  return false
end

def guess_tag(hint)
  hint = hint.downcase	# dup

  CVSTAGS.each do |name|
    if hint.include?(name.downcase)
      return name
    end
  end

  nil
end

main(ARGV)
