#!/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 ] [--save-to ] [--verbose] --depot --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