update debian copyright: tweak clscan and then manually fixup: revisit next release
[gnulib.git] / debian / clscan / clscan
index 3e7fa47..0e427e2 100755 (executable)
@@ -17,25 +17,117 @@ our $FILESCACHE="$CLSCANDIR/files.yaml";
 our $NEWFILES="$CLSCANDIR/new.txt";
 our $COPYRIGHTSTUB="$CLSCANDIR/copyright.in";
 
+my $gpl_boilerplate=<<"EOL";
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+ .
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+ .
+ You should have received a copy of the GNU General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+EOL
+
+my $lgpl2_boilerplate=<<"EOL";
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Library General Public License as published
+ by the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+ .
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ Library General Public License for more details.
+ .
+ You should have received a copy of the GNU Library General Public
+ License along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ USA.
+EOL
+
+my $lgpl3_boilerplate=<<"EOL";
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+ .
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+ .
+ You should have received a copy of the GNU General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+EOL
+
+# license overrides as specified in modules/*
+our $module_licenses = {
+    "public domain" => {
+       license => "PD",
+       license_text => "",
+    },
+    "unlimited" => {
+        license => "other",
+       license_text => "This file is free software; the Free Software Foundation\n" .
+                       "gives unlimited permission to copy and/or distribute it,\n" .
+                        "with or without modifications, as long as this notice is preserved.\n",
+    },
+    "LGPL" => {
+       license => "LGPL",
+       license_text => $lgpl3_boilerplate,
+    },
+    "LGPLv2+" => {
+       license => "LGPL-2+",
+       license_text => $lgpl2_boilerplate,
+    },
+    "LGPLv3+" => {
+       license => "LGPL-3+",
+       license_text => $lgpl3_boilerplate,
+    },
+    "unmodifiable license text" => {
+       license => "other",
+       license_text => "Everyone is permitted to copy and distribute verbatim copies\n" .
+                        "of this license document, but changing it is not allowed.\n",
+    },
+    "GPLed build tool" => {
+       license => "GPL",
+       license_text => $gpl_boilerplate,
+    },
+    "GPL" => {
+       license => "GPL",
+       license_text => $gpl_boilerplate,
+    },
+};
+
 our @filenames=();
+our %overrides=();
 our $files={};
 our $new={};
+our @deleted_files=();
 
+# actions
 my $scan=0;
 my $merge=0;
+my $help=0;
 my $writecopyright=0;
 
 usage() unless(@ARGV);
-usage() unless GetOptions("scan|s" => \$scan,
+usage() unless GetOptions("scan|s"  => \$scan,
                          "merge|m" => \$merge,
                          "write|w" => \$writecopyright,
-                         "help|h" => sub { usage(); });
+                         "help|h"  => \$help);
+usage() if $help;
 
 load_cache();
 scan() if($scan);
 merge() if($merge);
 write_copyright() if ($merge || $writecopyright);
 
+
 sub scan
 {
     get_filenames();
@@ -43,13 +135,14 @@ sub scan
     {
        scan_file($file);
     }
-    write_new();
     find_deleted();
+    write_new();
 }
 
 sub merge
 {
     merge_new();
+    load_overrides();
     save_cache();
 }
 
@@ -72,13 +165,15 @@ sub write_copyright
     my $licenses={};
     for my $file (sort keys(%$files))
     {
-       my $license=$files->{$file}->{license};
+       my $license=$files->{$file}->{license_override} || $files->{$file}->{license};
        my $copyright=$files->{$file}->{copyright};
-       my $license_text=$files->{$file}->{license_text};
+       my $license_text=$files->{$file}->{license_text_override} ||
+           $files->{$file}->{license_text};
        push(@{$licenses->{$license}->{$license_text}->{$copyright}}, $file);
     }
     my %refs=();
-    my $refnum=0;
+    my $refnum="00";
+    print COPYRIGHT license_trailer(sort keys(%$licenses));
     for my $license (sort keys(%$licenses))
     {
        for my $license_text (sort keys(%{$licenses->{$license}}))
@@ -87,31 +182,53 @@ sub write_copyright
            if(length($license_text))
            {
                $refnum++;
-               $licensestr .= " (REF$refnum)";
+               # license_text + empty license = License: other
+               if(!length($license))
+               {
+                   $licensestr = "other";
+               }
+               $licensestr .= " [REF$refnum]";
                $refs{$licensestr}=$license_text;
            }
+            else
+            {
+                if(!length($license)) {
+                    $licensestr="unknown";
+                }
+            }
            for my $copyright (sort keys(%{$licenses->{$license}->{$license_text}}))
            {
                next if(!length($license) && !length($copyright) && !length($license_text));
                my @filelist=sort @{$licenses->{$license}->{$license_text}->{$copyright}};
-               print COPYRIGHT "Files: ", join(', ', @filelist), "\n";
-               print COPYRIGHT "Copyright: $copyright\n" if length($copyright);
+               print COPYRIGHT "Files: ", join(' ', @filelist), "\n";
+               print COPYRIGHT "Copyright: ". (length($copyright) ? $copyright : "unknown" ) . "\n";
                print COPYRIGHT "License: $licensestr\n" if length($licensestr);
                print COPYRIGHT "\n";
            }
        }
     }
-    for my $ref (keys(%refs))
+    for my $ref (sort byref keys(%refs))
     {
        print COPYRIGHT "License: $ref\n";
        my @text=split(/\n/, $refs{$ref});
+        @text=map { ($_ eq "") ? "." : $_; } @text;
        print COPYRIGHT map { "    " . $_ . "\n" } @text;
        print COPYRIGHT "\n";
     }
-    print COPYRIGHT license_trailer(sort keys(%$licenses));
     close(COPYRIGHT);
 }
 
+sub byref
+{
+    my $aref=($a=~/\[REF(\d+)\]/)[0];
+    my $bref=($b=~/\[REF(\d+)\]/)[0];
+    if($aref && $bref)
+    {
+       return($aref <=> $bref);
+    }
+    return($a cmp $b);
+}
+
 sub license_trailer
 {
     my @licenses_used=@_;
@@ -131,24 +248,36 @@ sub license_trailer
     };
 
     my %types_found=();
-TYPE: for my $type (keys(%$license_data))
+    for my $type (reverse sort keys(%$license_data))
     {
        for my $license (@licenses_used)
        {
-           if($license =~ /$type(\+|\b)/i)
+           if($license =~ /$type(\+|\s|$)/i)
            {
                $types_found{$type}=1;
-               # avoid matching eg GPL-2 *and* GPL
-               next TYPE;
            }
        }
     }
-    my $text="\n";
-    for my $type (sort keys(%types_found))
+    my $text="  .\n";
+    # if just one, use standard style
+    if(keys(%types_found) == 1)
     {
-       $text .= "The complete text of the " . $license_data->{$type} ." can be\n";
-       $text .= "found in /usr/share/common-licenses/$type\n";
+       my ($file, $name)=each(%types_found);
+       $text .= "  The complete text of the $name can be\n";
+       $text .= "  found in /usr/share/common-licenses/$file\n";
+    }
+    else
+    {
+       # more than one, use table.
+       $text .= "  The complete text of standard licenses referenced above\n";
+       $text .= "  can be found in /usr/share/common-licenses/ as follows:\n  .\n  ";
+       $text .= sprintf("%-60s %s\n", "LICENSE", "FILE");
+       for my $type (sort keys(%types_found))
+       {
+           $text .= sprintf("  %-60s %s\n", $license_data->{$type}, $type);
+       }
     }
+    $text .= "\n\n";
     return $text;
 }
 
@@ -224,22 +353,29 @@ sub filechanged
     {
        if(exists($files->{$filename}->{copyright}))
        {
-           $new->{$filename}->{copyright_old}=$files->{$filename}->{copyright};
+           $new->{$filename}->{copyright}=$files->{$filename}->{copyright};
+           $new->{$filename}->{copyright_guess}=$copyright_guess;
        }
        if(exists($files->{$filename}->{license}))
        {
-           $new->{$filename}->{license_old}=$files->{$filename}->{license};
+           $new->{$filename}->{license}=$files->{$filename}->{license};
+           $new->{$filename}->{license_guess}=$license_guess;
        }
        if(exists($files->{$filename}->{license_text}))
        {
-           $new->{$filename}->{license_text_old}=$files->{$filename}->{license_text};
+           $new->{$filename}->{license_text}=$files->{$filename}->{license_text};
        }
     }
 }
 
-sub wanted
+sub get_filenames
 {
-    if(/^\.git/ || /^\.cvs/ || /^debian/ || /^modules$/)
+    find(\&wanted_files, ".");
+}
+
+sub wanted_files
+{
+    if(/^\.git/ || /^\.cvs/ || /^debian/ || /^modules$/ || /^\.pc/)
     {
        $File::Find::prune=1;
     }
@@ -249,11 +385,78 @@ sub wanted
     }
 }
 
-sub get_filenames
+sub wanted_modules
 {
-    find(\&wanted, ".");
+    if(/^\.[^\/]/ || /^README$/ || /^COPYING$/)
+    {
+       $File::Find::prune=1;
+       return;
+    }
+    elsif(-f)
+    {
+       unless(open(MOD, $File::Find::name))
+       {
+           warn("$me: cannot open $File::Find::name: $!\n");
+           return;
+       }
+       my $infiles=0;
+       my $inlicense=0;
+       my @files=();
+       while(<MOD>)
+       {
+           chomp;
+           if(/^$/)
+           {
+               $infiles = $inlicense = 0;
+           }
+           if($inlicense)
+           {
+               push(@{$overrides{$_}},@files);
+               $inlicense=0;
+           }
+           elsif($infiles)
+           {
+               push(@files, $_);
+           }
+           elsif(/^License:/)
+           {
+               $inlicense=1;
+           }
+           elsif(/^Files:/)
+           {
+               $infiles=1;
+           }
+       }
+       close(MOD);
+    }
 }
 
+sub load_overrides
+{
+    find({ wanted => \&wanted_modules, no_chdir => 1}, "modules/");
+    for my $license (keys(%overrides))
+    {
+       if(!exists($module_licenses->{$license}))
+       {
+           die("$me: license override \"$license\" not found in \$module_licenses\n");
+       }
+       my @overridden_files=map { "./" . $_; } @{$overrides{$license}};
+       for my $file (@overridden_files)
+       {
+           my $override=$module_licenses->{$license};
+           if(length($override->{license}))
+           {
+               $files->{$file}->{license_override}=$override->{license};
+           }
+           if(length($override->{license_text}))
+           {
+               $files->{$file}->{license_text_override}=$override->{license_text};
+           }
+       }
+    }
+}
+
+
 sub load_cache
 {
     unless(open(YAML,$FILESCACHE))
@@ -288,7 +491,6 @@ sub write_new
     unless(keys(%$new))
     {
        warn("$me: no new/changed files found\n");
-       return;
     }
     unless(open(NEW,">$NEWFILES"))
     {
@@ -299,19 +501,19 @@ sub write_new
        print NEW "File: $file\n";
        print NEW "Hash: ", $new->{$file}->{hash}, "\n";
        print NEW "Copyright: ", $new->{$file}->{copyright}, "\n";
-       print NEW "License: ", $new->{$file}->{license}, "\n";
-       print NEW "Licence_Text: \n";
-       if($new->{$file}->{license_old})
+       if($new->{$file}->{copyright_guess})
        {
-           print NEW "#License_old: ", $new->{$file}->{license_old}, "\n";
+           print NEW "#Copyright_guess: ", $new->{$file}->{copyright_guess}, "\n";
        }
-       if($new->{$file}->{copyright_old})
+       print NEW "License: ", $new->{$file}->{license}, "\n";
+       if($new->{$file}->{license_guess})
        {
-           print NEW "#Copyright_old: ", $new->{$file}->{copyright_old}, "\n";
+           print NEW "#License_guess: ", $new->{$file}->{license_guess}, "\n";
        }
-       if($new->{$file}->{licence_text_old})
+       if($new->{$file}->{license_text})
        {
-           print NEW "#License_text_old: ", $new->{$file}->{licence_text_old}, "\n";
+           my @text=split(/\n/, $new->{$file}->{license_text});
+           print NEW "\t" . join("\n\t", @text), "\n";
        }
        print NEW "#Header: \n";
        my @headerlines=split(/\n/, $new->{$file}->{header});
@@ -319,6 +521,10 @@ sub write_new
        print NEW join("\n", @headerlines);
        print NEW "\n\n";
     }
+    if(@deleted_files)
+    {
+       print NEW map { "Deleted: $_\n"; } @deleted_files;
+    }
     close NEW;
 }
 
@@ -339,11 +545,14 @@ sub merge_new
     {
        $line++;
        chomp;
-       next if(/^\s*$/);
        next if(/^\s*\#/);
        if($in_license_text && /^\s+(.*)/)
        {
-           $license_text .= "\n" . $1;
+           $license_text .= $1 . "\n";
+       }
+       elsif(/^\s*$/)
+       {
+           next;
        }
        elsif(/^File:\s*(.*)/)
        {
@@ -361,6 +570,7 @@ sub merge_new
            $copyright='';
            $hash='';
            $license_text='';
+           $in_license_text = 0;
        }
        elsif(/^Hash:\s*(.*)/)
        {
@@ -373,15 +583,15 @@ sub merge_new
        elsif(/^License:\s*(.*)/)
        {
            $license=$1;
-       }
-       elsif(/^License_text:\s*(.*)/)
-       {
            $in_license_text=1;
-           $license_text=$1;
+           $license_text='';
        }
-       elsif($in_license_text && /^\s+(.*)/)
+       elsif(/^Deleted:\s*(.*)/)
        {
-           $license_text .= "\n" . $1;
+           if(exists($files->{$1}))
+           {
+               delete($files->{$1});
+           }
        }
        else
        {
@@ -401,19 +611,18 @@ sub merge_new
 
 sub find_deleted
 {
-    my @deleted=();
     my %newnames = map { $_ => 1 } @filenames;
     for my $file (sort keys(%$files))
     {
        unless(exists($newnames{$file}))
        {
-           push(@deleted, $file);
+           push(@deleted_files, $file);
        }
     }
-    if(@deleted)
+    if(@deleted_files)
     {
        print "Removed files:\n";
-       print join("\n", @deleted),"\n";
+       print join("\n", @deleted_files),"\n";
     }
 }