#!/usr/local/bin/ruby

# == Synopsis
#
# p4fetch.rb: Uses the web based P4DB tree browser to identify
# and fetch the most recent revision of a tree, using a breadth
# first tree traversal
# This is quite dumb and slow; it fetches one webpage per 
# directory visited (and clearly; one per file as well)
# and doesnt notice when a directory only consists of dead files
#
# == Usage
#
# p4fetch.rb [--server <URI>] [--save-to <dir>] [--verbose] --depot <depot> --repo <repo>
#
# --help, -h:
#       Show help
# 
# --server, -s:
#       Fetch from this P4DB perforce browser
#       (Default: 'http://perforce.freebsd.org/')
#
# --depot, -d:
#       Fetch from this P4DB depot
#       e.g. '//depot/user/benjsc/'
#
# --repo, -r:
#       Fetch this repo from the depot
#       e.g. 'wpi'
#
# --save-to, -D:
#       By default, files will be saved to a directory the
#       same name as the repo. This can be overridden by this switch.
#
# --verbose, -v:
#       Be more verbose when fetching files
#
#
# == Example
# 
# p4fetch.rb --depot //depot/user/benjsc/ --repo wpi 
#

# Copyright (c) 2007, Tom Evans
# 
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
# 
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
# 
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.

require 'rubygems'
require 'hpricot'
require 'open-uri'
require 'uri'
require 'getoptlong'
require 'rdoc/ri/ri_paths'
require 'rdoc/usage'

class P4DBDirectory
    
    attr_reader :path, :subdirs, :files

    def initialize(path, save_to="./")
        @save_to = save_to
        @path = path
        uri = "#{$base_uri}depotTreeBrowser.cgi?FSPC=#{$base_path}#{path}"
        doc = Hpricot(open(uri))
        dirs = doc.search("a[@href^='/depotTreeBrowser.cgi?FSPC=#{$base_path}#{@path}']").delete_if { |x| x.innerHTML == 'Hide deleted files'}
        @subdirs = dirs.collect { |dir| @path + dir.innerHTML }
        files = doc.search("a[@href^='fileViewer.cgi?FSPC=#{$base_path}#{@path}']")
        @files = files.collect do |link|
            html = link.to_s
            uri = html.gsub(/.*"(.*)".*/, "\\1")
            uri.gsub!(/fileViewer.cgi/, 'fileDownLoad.cgi')
            filename = URI.unescape(uri.gsub(/.*\/(.*)&REV=.*/, "\\1"))
            [uri, filename]
        end
    end

    def fetch_files
        @subdirs.each do |sd|
            puts "Making save folder #{@save_to}/#{sd}" if $verbose
            system '/bin/mkdir', '-p', "#{@save_to}/#{sd}"
        end
        @files.each do |fs|
            puts "Saving #{$base_uri}#{fs[0]} => #{@save_to}/#{@path}/#{fs[1]}" if $verbose
            system '/usr/bin/fetch', ($verbose ? '-v' : '-q'), '-o', "#{@save_to}/#{@path}/#{fs[1]}", "#{$base_uri}#{fs[0]}"
        end
        @subdirs.each do |sd|
            d = P4DBDirectory.new(sd, @save_to)
            d.fetch_files
        end
    end
end

if $0 == __FILE__

    $base_uri = "http://perforce.freebsd.org/"
    $base_path = nil 
    $verbose = false
    repo = nil
    dir = nil

    opts = GetoptLong.new(
        [ '--help',     '-h',   GetoptLong::NO_ARGUMENT ],
        [ '--verbose',  '-v',   GetoptLong::NO_ARGUMENT ],
        [ '--server',   '-s',   GetoptLong::REQUIRED_ARGUMENT ],
        [ '--depot',    '-d',   GetoptLong::REQUIRED_ARGUMENT ],
        [ '--repo',     '-r',   GetoptLong::REQUIRED_ARGUMENT ],
        [ '--save-to',  '-D',   GetoptLong::REQUIRED_ARGUMENT ]
    )

    opts.each do | opt, arg |
        case opt
        when '--help'
            RDoc::usage
        when '--verbose'
            $verbose = true
        when '--server'
            $base_uri = arg
        when '--depot'
            $base_path = arg
        when '--repo'
            repo = arg
        when '--save-to'
            dir = arg
        end
    end

    dir = repo if dir.nil?
    
    RDoc::usage if ($base_path.nil? or repo.nil?)

    d = P4DBDirectory.new(repo, dir)
    d.fetch_files
end

