#!/usr/local/bin/perl -w
#
# Copyright (C) 2005, 2007, 2012  Internet Systems Consortium, Inc. ("ISC")
#
# Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH
# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
# AND FITNESS.  IN NO EVENT SHALL ISC 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.

# $Id$

#
#  Track which branches are still open or not in the bind9 cvs repository.
#  This is done so that work that is "in progress" (active) doesn't get
#  so easily forgotten about.
#
#  This script updates doc/private/branches by adding new branches and moving
#  closed branches to the end of the file.  New branches are found by walking
#  the cvs repository and extracting the new branches from the header fields
#  of the files there.
#
#  doc/private/branches has one line per branch in the following field order:
#  name, status, to whom the branch belongs and comments.  Comments are
#  in '(',')'.  The first three field are single words.
#
#  Note: this is intended to be run on the machine hosting the CVS repository.
#

%branches = ();
%whom = ();
%comments = ();
%history = ();
%dates = ();
$repository = "/proj/cvs/prod";
$module = "bind9";

#
# Make sure we have a up to date copy.  If the previous ran failed for
# any reason remove it (-C).
#
!system("cvs", "-d", $repository, "update", "-C", "doc/private/branches") || die "cannot update doc/private/branches: $!";

#
# load existing content
#
open(BRANCHES, "<doc/private/branches") || die "can't open util/branches: $!";
while (<BRANCHES>) {
	my $branch;
	my $status;
	my $who;
	chomp;
	next if (/^-/);
	next if (/^Branch/);
	next if (/^\s/);
	$c = "";
	if (m://.*:) {
		$c = $_;
		$c =~ s:.*?//\s*(.*)$:$1:;
		s:(.*?)//.*:$1:;
	} else {
		#
		# look for old style comment
		#
		if (m/\(.*\)/) {
			$c = $_;
			$c =~ s/.*\((.*)\).*$/$1/;
			s/\(.*\)//;
		}
	}
	s/\s*$//;
	next if (/^\s*$/);
	($branch, $status, $who) = split;
	$status = "new" if (!defined($status));
	$branches{$branch} = $status;
	$who = "" if (!defined($who));
	$whom{$branch} = $who;
	$comments{$branch} = $c;
}
close (BRANCHES);

# T 1999-03-15 21:15 +0000 vixie bind [ietf44:A]
open(HISTORY, "cvs history -T -a 2> /dev/null |") || die("can't get history");
while (<HISTORY>) {
	my $tag;
	my $date;
	my $time;
	my $tz;
	my $who;
	my $mod;
	my $branch;
        chomp;
        s/[][]//g;
        s/:[^ \t]+$//;
	s/\s+/ /g;
	($tag, $date, $time, $tz, $who, $mod, $branch) = split;
	next if ($mod ne $module );
	next if ($tag ne "T" );
	next if (exists($history{$branch}));
	$history{$branch} = $who;
	$dates{$branch} = "$date $time $tz";
        # print "$_\n";
}
close (HISTORY);

#
# Search repository for new branches.
#
# New branches have the following format "name:<revision>.0.#"
# where # is the number of potential branches from this the revision.
#
open(FILES, "find $repository/$module -type f -name *,v -print |") || die "can't start find: $!";
while (<FILES>) {
	chomp;
	# print "file: $_\n"; # debug
	# $file = $_; # save for branch debug below.
	s:^$repository/::;
	s:/Attic/([^/]*)$:/$1:;
	s:,v$::;
	#
	# use cvs so that the file is locked.
	#
	#print "cvs -d $repository rlog -h $_\n";
	open(FILE, "cvs -d $repository rlog -h $_|") || die "can't start cvs rlog -h $_: $!";
	while (<FILE>) {
		chomp;
		next unless m/^symbolic names:$/; # skip until we find the tags
		while (<FILE>) {
			chomp;
			last if (m/^locks;/);	# we are past the tags
			last if (m/^keyword/);	# we are past the tags
			next unless m/\.0\.\d+$/; # skip if not a branch
			s/\s(.*):.*/$1/;	# extract label
			if (!$branches{$_}) {
				$branches{$_} = "new";
				if (exists($history{$_})) {
					$whom{$_} = $history{$_};
					$comments{$_} = $dates{$_};
				} else {
					$whom{$_} =  "";
					$comments{$_} =  "";
				}
				# print "branch: $_ $file\n"; # debug
			}
		}
		chomp while (<FILE>);	# let cvs rlog exit normally.
	}
	close(FILE);
}
close(FILES);

#
# Write out updated version.
#
open(BRANCHES, ">doc/private/newbranches") || die "can't open doc/private/branches: $!";
print BRANCHES "\nBranch\t\t\t\tStatus\tWhom\t// Comments\n";
print BRANCHES "-----------------------------------------------------------\n\n";
print BRANCHES "\t\t\t\tnew\t\tnot yet classified\n";
print BRANCHES "\t\t\t\topen\t\tdevelopement branch\n";
print BRANCHES "\t\t\t\tactive\t\tnot a development branch\n";
print BRANCHES "\t\t\t\treview\t\tready for review\n";
print BRANCHES "\t\t\t\tprivate\t\tprivate branch\n";
print BRANCHES "\t\t\t\tclosed\t\tfinished with\n";
print BRANCHES "\n";
foreach $key (sort keys %branches) {
	next if ($branches{$key} eq "closed");
	print BRANCHES "$key";
	$len = length($key);
	if ($len >= 32) {
		$tabs = 1;
	} else {
		$needed = int (32 - $len);
		$tabs = int ($needed / 8);
		if ($needed % 8 != 0) {
			$tabs++;
		}
	}
	for ($i = 0; $i < $tabs; $i++) {
		printf	BRANCHES "\t";
	}
	print BRANCHES "$branches{$key}\t";
	print BRANCHES "$whom{$key}";
	print BRANCHES "\t// $comments{$key}" if ($comments{$key} ne "");
	print BRANCHES "\n";
}

print BRANCHES "\n\n";

foreach $key (sort keys %branches) {
	next if ($branches{$key} ne "closed");
	print BRANCHES "$key";
	$len = length($key);
	if ($len >= 32) {
		$tabs = 1;
	} else {
		$needed = int (32 - $len);
		$tabs = int ($needed / 8);
		if ($needed % 8 != 0) {
			$tabs++;
		}
	}
	for ($i = 0; $i < $tabs; $i++) {
		printf	BRANCHES "\t";
	}
	print BRANCHES "$branches{$key}";
	print BRANCHES "\t\t// $comments{$key}" if ($comments{$key} ne "");
	print BRANCHES "\n";
}
close(BRANCHES);

#
# Update if changed.
#
if (system("cmp", "-s", "doc/private/newbranches", "doc/private/branches")) {
	rename("doc/private/newbranches", "doc/private/branches") || die "Cannot rename: doc/private/newbranches -> doc/private/branches: $!";
	!system("cvs", "-d", $repository, "commit", "-m", "auto update", "doc/private/branches") || die "cvs commit failed: $!";
} else {
	unlink("doc/private/newbranches");
}
