#!/usr/bin/env ruby # $Id$ # $URL$ # # Copyright (c) 2007 Ward Wouts # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # require 'net/https' require 'uri' require 'rexml/document' require 'date' require 'getoptlong' def usage puts < -h, --help show this message -u set baseurl EOT exit end def cmdline options = Hash.new begin opts = GetoptLong.new( [ "-h", "--help", GetoptLong::NO_ARGUMENT ], [ "-u", GetoptLong::REQUIRED_ARGUMENT ] ) opts.quiet=true opts.each do |opt, arg| options[opt] = arg end rescue print "#{$!}\n" usage end if options["-h"] usage end return options end def fetch(uri_str, limit = 10) # You should choose better exception. raise ArgumentError, 'HTTP redirect too deep' if limit == 0 host = URI.parse(uri_str).host port = URI.parse(uri_str).port path = URI.parse(uri_str).path query = URI.parse(uri_str).query Net::HTTP.start(host, port) {|http| if query req = Net::HTTP::Get.new("#{URI.escape(path)}?#{URI.escape(query)}") else req = Net::HTTP::Get.new("#{URI.escape(path)}") end req.basic_auth @user, @pass response = http.request(req) case response when Net::HTTPSuccess then response when Net::HTTPRedirection then fetch(response['location'], limit - 1) when Net::HTTPNotFound then puts "404 Not Found #{uri_str}"; response.error! when Net::HTTPUnauthorized then puts "401 Authorization Required #{uri_str}"; response.error! else response.error! end } end def svnparse(url) puts "\n#{url}" begin body = fetch("#{url}/.svn/entries").body rescue end if body.nil? return end dirs = Array.new if body[0].chr == '<' xmldoc = REXML::Document.new(body) xmldoc.elements.each("wc-entries/entry") {|item| case item.attribute("kind").to_s when "dir" then if item.attribute("name").to_s == "" next end puts "#{item.attribute("name")}/" dirs.push(item.attribute("name").to_s) when "file" then puts "#{item.attribute("name")} #{item.attribute("last-author")} #{item.attribute("committed-date")}" else puts " Strange kind #{item.attribute("kind")}" end } else lastline = "" commitdate = "" author = "" bodyarr = Array.new body.each_line{|line| bodyarr.push line } (0...bodyarr.length).each{|count| line = bodyarr[count] line.chomp! if line.match(/\d\d\d\d-\d\d-\d\dT/) commitdate = line author = bodyarr[count+2] end case line when 'dir' if lastline == "" next end puts "#{lastline}/" dirs.push lastline when 'file' puts "#{lastline} #{author} #{commitdate}" else lastline = line end } end dirs.each{|dir| #p "#{url}/#{dir}" svnparse("#{url}/#{dir}") } end def cvsparse(url) puts "\n#{url}" body=[] begin body = fetch("#{url}/CVS/Entries").body rescue end dirs = Array.new body.each{|line| case line when /^D\/?(.*?)\/.*/ then puts "#{$1}/" dirs.push($1) when /^\/(.*?)\/(.*?)\/(.*?)\/.*/ then puts "#{$1} #{$2} #{$3}" end } dirs.each{|dir| cvsparse("#{url}/#{dir}") } end options = cmdline if options["-u"].nil? usage end mode=nil # first test for subversion entries if mode.nil? begin body = fetch("#{options["-u"]}/.svn/entries").body mode="subversion" rescue end end # then test for CVS entries if mode.nil? begin body = fetch("#{options["-u"]}/CVS/Entries").body mode="cvs" rescue end end if mode.nil? puts "Couldn't determine versioning information" else case mode when "subversion" then svnparse(options["-u"]) when "cvs" then cvsparse(options["-u"]) end end