#!/usr/local/bin/perl -w
#
# Copyright (C) 2004-2010, 2012  Internet Systems Consortium, Inc. ("ISC")
# Copyright (C) 1998-2001  Internet Software Consortium.
#
# 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$

require 5.002;

# Map copyright owners to the files containing copyright messages.
# The first line of the copyright message is not in the file;
# it is constructed by this script.
#
# Usage:
#
#   perl util/update_copyrights <util/copyrights

my %owner2filename = (
	"" => "util/COPYRIGHT",
	"NAI" => "util/COPYRIGHT.NAI",
	"NOM" => "util/COPYRIGHT.NOM",
	"BSDI" => "util/COPYRIGHT.BSDI",
	"BRIEF" => "util/COPYRIGHT.BRIEF",
	"PORTION" => "util/COPYRIGHT.PORTION",
);

# Map each copyright owner name to a reference to an array containing
# the lines of the copyright message.

my %owner2text = ();

my $keyword_pat = '\$(Id:.*|Revision:.*|Id|Revision)\$';

foreach $owner (keys %owner2filename) {
        my $f = $owner2filename{$owner};
        open(COPYRIGHT, "<$f") || die "can't open $f: $!";
        @copyright_text = <COPYRIGHT>;
        close(COPYRIGHT);
        $owner2text{$owner} = [ @copyright_text ];
}

my %file_types = ();
my %file_years = ();
my $years_list;
my $parent;

($dummy,$dummy,$dummy,$dummy,$this_month,$this_year,$dummy,$dummy,$dummy) = localtime(time());
$this_year += 1900;

while (<>) {
	chomp;
	($file, $type, $years) = split(/\s+/);
	$file_types{$file} = $type;
	$file_years{$file} = $years;
}

sub getyears {
	$parent = $_[0];
	$parent =~ s/PARENT://;
	$years_list = $file_years{$parent};
	if (defined($years_list) && $years_list =~ /^PARENT:/) {
		print "BAD PARENT:$parent\n";
		undefine($years_list);
	}
}

sub docbook {
	$parent = $_[0];
	$parent =~ s/\.[^.]*$/.docbook/;
	$years_list = $file_years{$parent};
}

sub copyright {
	my $holder = shift;
	my $result = "";
	return $result unless (@_);
	$result = "$result    <copyright>\n";
	$result = "$result      <year>$_</year>\n" foreach (@_);
	$result = "$result      <holder>$holder</holder>\n";
	$result = "$result    </copyright>\n";
	return $result;
}

sub copyrights {
	my $a = copyright("Internet Systems Consortium, Inc. (\"ISC\")",
		   grep({ $_ >= 2004} @_));
	my $b = copyright("Internet Software Consortium.",
		   grep({ $_ < 2004} @_));
	return "$a$b";
}

foreach $file (keys %file_types) {
	$typeandowner = $file_types{$file};
	$years_list = $file_years{$file};

        if ( ! -f $file ) {
                print "$file: missing\n";
                next;
        }
	# print "Doing: $file";

        if ($years_list =~ /PARENT:/) {
		getyears($years_list);
		if (!defined $years_list) {
			print "$file: has bad parent $parent\n";
			next;
		}
	}

	# copyright notice is now generated from the source.
	next if ($years_list eq "DOCBOOK");

	if ($years_list eq "DOCBOOK") {
		docbook($file);
		if (!defined $years_list) {
			print "$file: has bad parent $parent\n";
			next;
		}
	}

        @years = split(/,/, $years_list);

        my ($type, $owner) = split(/\./, $typeandowner);
        $owner = "" if !defined $owner;

        $textp = $owner2text{$owner};
        if (!defined $textp) {
                print "$file: unknown copyright owner $owner\n";
                next;
        }

        next if $type eq "X" or $type eq "BAT";

        $before_copyright = "";
        $c_comment = 0;
        $shell_comment = 0;
        $m4_comment = 0;
        $sgml_comment = 0;
        $zone_comment = 0;
        $man_comment = 0;
        $python_comment = 0;
        $start_comment = "";
        $end_comment = "";
        $first = "";
        if ($type =~ /^(C|YACC|CONF-C)$/) {
                $c_comment = 1;
                $start_comment = "/*\n";
                $prefix = " * ";
                $end_comment = " */\n";
        } elsif ($type =~ /^(SH|PERL|TCL|MAKE|CONF-SH|RNC)$/) {
                $shell_comment = 1;
		$prefix = "# ";
        } elsif ($type =~ /^PYTHON$/) {
                $python_comment = 1;
                $start_comment = "############################################################################\n";
                $prefix = "# ";
		$end_comment = "############################################################################\n"
        } elsif ($type eq "ZONE" || $type eq "MC") {
                $zone_comment = 1;
                $prefix = "; ";
        } elsif ($type eq "MAN") {
                $man_comment = 1;
                $prefix = ".\\\" ";
        } elsif ($type eq "M4") {
                $m4_comment = 1;
                $prefix = "dnl ";
        } elsif ($type eq "HTML" || $type eq "SGML") {
                $sgml_comment = 1;
                $start_comment = "<!--\n";
                $prefix = " - ";
                $end_comment = "-->\n";
        } elsif ($type eq "TXT") {
                $prefix = "";
        } else {
                print "$file: type '$type' not supported yet; skipping\n";
                next;
        }

        ($nonspaceprefix = $prefix) =~ s/\s+$//;

        open(SOURCE, "<$file") || die "can't open $file: $!";
        $_ = <SOURCE>;
        if ($type eq "YACC") {
                unless ($_ eq "%{\n") {
                        print "$file: unexpected yacc file start ",
                              "(expected \"%{\\n\")\n";
                        close(SOURCE);
                        next;
                }
                $before_copyright = "$_";
                $_ = <SOURCE>;
        }
        if ($c_comment && /^\/\*/) {
                $_ = <SOURCE>;
                if ($_ !~ /[Cc]opyright/) {
                        print "$file: non-copyright comment\n";
                        close(SOURCE);
                        next;
                }
                if ($_ !~ /\*\//) {
                        while (<SOURCE>) {
                                last if $_ =~ /\*\//;
                        }
                }
        } elsif ($shell_comment) {
                if (/^\#\!/) {
                        $before_copyright = "$_#\n";
                        $_ = <SOURCE>;
                        $_ = <SOURCE> if $_ eq "#\n";
                }
                if (/^\#/) {
                        if ($_ !~ /[Cc]opyright/) {
                                print "$file: non-copyright comment\n";
                                close(SOURCE);
                                next;
                        }
                        while (<SOURCE>) {
                                if ($_ !~ /^\#/) {
                                        $first = $_;
                                        last;
                                }
                        }
                } else {
                        $first = $_;
                }
        } elsif ($python_comment) {
                if (/^\#\!/) {
                        $before_copyright = "$_";
                        $_ = <SOURCE>;
                        $_ = <SOURCE> if $_ eq "#\n";
                        $_ = <SOURCE> if $_ eq "############################################################################\n";
                }
                if (/^\#/) {
                        if ($_ !~ /[Cc]opyright/) {
                                print "$file: non-copyright comment\n";
                                close(SOURCE);
                                next;
                        }
                        while (<SOURCE>) {
                                if ($_ !~ /^\#/) {
                                        $first = $_;
                                        last;
                                }
                        }
                } else {
                        $first = $_;
                }
        } elsif (($m4_comment || $zone_comment || $man_comment) &&
                 /^\Q$nonspaceprefix\E/) {

                while (/^\Q$nonspaceprefix\E\s*$/) {
                        $_ = <SOURCE>;
                }

                if ($_ !~ /[Cc]opyright/) {
                        print "$file: non-copyright comment\n";
                        close(SOURCE);
                        next;
                }
                while (<SOURCE>) {
                        if ($_ !~ /^\Q$nonspaceprefix\E/ ||
                            $_ =~ /$keyword_pat/) {
                                $first = $_;
                                last;
                        }
                }
        } elsif ($sgml_comment) {
		$before_copyright = "";
                while (/^<!DOCTYPE/ || /^<\?xml-stylesheet/ || /^<\?xml /) {
			# print "SGML: $_";
			$before_copyright = "$before_copyright$_";
			if (/>$/ ) {
				$_ = <SOURCE>;
				close(SOURCE) if (eof(SOURCE));
				next;
			}
			$_ = <SOURCE>;
			while (!eof(SOURCE) && ! /^<!/ ) {
				$before_copyright = "$before_copyright$_";
				$_ = <SOURCE>;
			}
			if (eof(SOURCE)) {
				close(SOURCE);
				next;
			}
                }
                if (/^<!/) {
                        $_ = <SOURCE> if $_ eq "<!--\n";
                        if ($_ !~ /[Cc]opyright/) {
                                print "$file: non-copyright comment\n";
                                close(SOURCE);
                                next;
                        }
                        while (defined($_)) {
                                last if s/.*-->//;
                                $_ = <SOURCE>;
                        }
                        print "$file: unterminated comment\n"
                          unless defined($_);
                        if ($_ ne "\n") {
                                $first = $_;
                        } else {
                                $first = <SOURCE>;
                        }
                } else {
                        $first = $_;
                }
        } elsif ($type eq "TXT") {
                if ($_ =~ /[Cc]opyright/) {
                        $/ = "";            # paragraph at a time
                        while (<SOURCE>) {
                                # Not very maintainable, but ok enough for now.
                                last unless
                                  /[Cc]opyright/ ||
                                  /See COPYRIGHT in the source root/ ||
                                  /Permission to use, copy, modify, and / ||
                                  /THE SOFTWARE IS PROVIDED "AS IS" AND /;
                        }
                        $/ = "\n";
                }
                $first = $_;
        } else {
                $first = $_;
        }

        $first = "" if ! defined($first);

        open(TARGET, ">$file.new") || die "can't open $file.new: $!";
        print TARGET $before_copyright if $before_copyright;
        print TARGET $start_comment if $start_comment;

        $sysyears = "";
        $sftyears = "";
        $nomyears = "";

	#
	# Internet Software Consortium: up to 2003
	#
        $last_year = 0;
        $anchor_year = 0;
	$years = "";
        foreach $year (@years) {
		if ($year >= 2004) { next; }
                if ($last_year != 0 && $year == $last_year + 1) {
                        if ($year > $anchor_year + 1) {
                                substr($years, $anchor_end) = "-$year";
                        } else {
                                $years .= ", $year";
                        }
                } else {
                        $years .= $last_year == 0 ? "$year" : ", $year";
                        #if ($anchor_year != 0) {
                        #        print "$file: noncontiguous year: ",
                        #              "$year != $last_year + 1\n";
                        #}
                        $anchor_year = $year;
                        $anchor_end = length($years);
                }

                $last_year = $year;
        }
	$sftyears = $years;

	#
	# Nominum: up to 2001.
	#
        $last_year = 0;
        $anchor_year = 0;
	$years = "";
        foreach $year (@years) {
		if ($year >= 2002) { next; }
                if ($last_year != 0 && $year == $last_year + 1) {
                        if ($year > $anchor_year + 1) {
                                substr($years, $anchor_end) = "-$year";
                        } else {
                                $years .= ", $year";
                        }
                } else {
                        $years .= $last_year == 0 ? "$year" : ", $year";
                        #if ($anchor_year != 0) {
                        #        print "$file: noncontiguous year: ",
                        #              "$year != $last_year + 1\n";
                        #}
                        $anchor_year = $year;
                        $anchor_end = length($years);
                }

                $last_year = $year;
        }
	$nomyears = $years;

	#
	# Internet Systems Consortium: 2004 onwards.
	#
        $last_year = 0;
        $anchor_year = 0;
	$years = "";
	$anchor_end = length($years);
	my $andor = 0;
	my $noid = 0;
        foreach $year (@years) {
		if ($year < 2004) { next; }
		$andor = 1 if ($year >= 2007);
		$noid = 1 if ($year > 2012 || ($year == 2012 && $this_month >= 5) );
                if ($last_year != 0 && $year == $last_year + 1) {
                        if ($year > $anchor_year + 1) {
                                substr($years, $anchor_end) = "-$year";
                        } else {
                                $years .= ", $year";
                        }
                } else {
                        $years .= $last_year == 0 ? "$year" : ", $year";
                        #if ($anchor_year != 0) {
                        #        print "$file: noncontiguous year: ",
                        #              "$year != $last_year + 1\n";
                        #}
                        $anchor_year = $year;
                        $anchor_end = length($years);
                }

                $last_year = $year;
        }
	$sysyears = $years;

        ($firstline, $secondline, @otherlines) = @$textp;

        $firstline =~ s/\@SYSYEARS\@/$sysyears/;
        $secondline =~ s/\@SFTYEARS\@/$sftyears/;

        print TARGET "$prefix$firstline";
	if ($sftyears ne "" ) {
		print TARGET $secondline =~ /^$/ ? $nonspaceprefix : $prefix;
		print TARGET "$secondline";
	}

        foreach $_ (@otherlines) {
		s:modify, and distribute:modify, and/or distribute: if ($andor);
                print TARGET (/^$/ ? $nonspaceprefix : $prefix);
		s/\@NOMYEARS\@/$nomyears/;
                print TARGET "$_";
        }
        print TARGET $end_comment if $end_comment;

        if ($first eq "") {
                $first = <SOURCE>;
        }

        if (defined($first)) {
          	if ($type eq 'MAN') {
                	print TARGET "$nonspaceprefix\n";
		} else {
	                print TARGET "\n";
                }

		if ($type eq "C" && $sysyears =~ /$this_year/) {
			my $body = "";
			while (<SOURCE>) {
			# Process leading white space.
			# Remove 1-7 spaces followed by a tab into a single
			# tab if at start of line or proceeded by tabs.
			s/^(\t*) {1,7}\t/$1\t/ while (/^\t* {1,7}\t/);
			# Convert 8 spaces into tabs if at start of line
			# or preceeded by tabs.
			s/^(\t*)        /$1\t/ while (/^\t*        /);
			# Remove trailing white space.
			s/[ \t]*$//;
				$body = "$body$_";
			}
			$_ = $body;
		} else {
			undef $/;
			$_ = <SOURCE>;
			$/ = "\n";
		}

		if ($type eq 'SGML' && m:<articleinfo>.*?</articleinfo>:s) {
			# print "docinfo: $file\n";
			my $r = copyrights(@years);
			s:<articleinfo>.*?</articleinfo>:<articleinfo>\n$r  </articleinfo>:s;
		}
		if ($type eq 'SGML' && m:<docinfo>.*?</docinfo>:s) {
			# print "docinfo: $file\n";
			my $r = copyrights(@years);
			s:<docinfo>.*?</docinfo>:<docinfo>\n$r  </docinfo>:s;
		}
		if ($type eq 'SGML' && m:<bookinfo>.*?</bookinfo>:s) {
			# print "bookinfo: $file\n";
			my $r = copyrights(@years);
			s:<bookinfo>.*?</bookinfo>:<bookinfo>\n$r  </bookinfo>:s;
		}

                my ($start, $end);
		if ($type =~ /^PYTHON$/) {
                        ($start = $prefix) =~ s/\s*\n//;
			$end = "\n";
                } elsif ($start_comment ne "") {
                        ($start = $start_comment) =~ s/\s*\n/ /;
                        ($end = $end_comment) =~ s/^\s*(.*)\n/ $1\n/;
                } elsif ($prefix ne "") {
                        ($start = $prefix) =~ s/\s*\n//;
                        $end = "\n";
                } else {
                        $start = "";
                        $end = "\n";
                }

                if (!$noid && $first !~ /$keyword_pat/ && 
		    (!defined($_) || $_ !~ /$keyword_pat/)) {
			$end = "\n$nonspaceprefix" if ($type eq "MAN");
                        print TARGET "$start\$";
                        print TARGET "Id";
                        print TARGET "\$$end\n";
                }

                print TARGET $first if $first !~ /^\s*$/;
                print TARGET $_ if (defined($_));
        }
        close(TARGET);
        close(SOURCE);

        $mode = (stat $file)[2]&511;
        chmod $mode, "$file.new";

        if (system("cmp -s $file.new $file") == 0) {
                unlink("$file.new");
        } else {
                rename("$file.new", "$file")
                  or die "rename($file.new, $file): $!";
        }
}
