2 # Ian Beckwith <ianb@erislabs.net>
7 $me=($0=~/(?:.*\/)?(.*)/)[0];
11 use Digest::SHA qw(sha256_hex);
15 our $CLSCANDIR="debian/clscan";
16 our $FILESCACHE="$CLSCANDIR/files.yaml";
17 our $NEWFILES="$CLSCANDIR/new.txt";
18 our $COPYRIGHTSTUB="$CLSCANDIR/copyright.in";
28 usage() unless(@ARGV);
29 usage() unless GetOptions("scan|s" => \$scan,
31 "write|w" => \$writecopyright,
32 "help|h" => sub { usage(); });
37 write_copyright() if ($merge || $writecopyright);
42 for my $file (@filenames)
60 die("$me: no files known, run $0 --scan\n");
62 unless(copy($COPYRIGHTSTUB, "debian/copyright.new"))
64 die("$me: cannot copy $COPYRIGHTSTUB to debian/copyright: $!\n");
66 unless(open(COPYRIGHT, ">>debian/copyright.new"))
68 die("$me: cannot append to debian/copyright: $!\n");
71 # group files by license/license_text/copyright
73 for my $file (sort keys(%$files))
75 my $license=$files->{$file}->{license};
76 my $copyright=$files->{$file}->{copyright};
77 my $license_text=$files->{$file}->{license_text};
78 push(@{$licenses->{$license}->{$license_text}->{$copyright}}, $file);
80 for my $license (sort keys(%$licenses))
82 for my $license_text (sort keys(%{$licenses->{$license}}))
84 for my $copyright (sort keys(%{$licenses->{$license}->{$license_text}}))
86 next if(!length($license) && !length($copyright) && !length($license_text));
87 my @filelist=sort @{$licenses->{$license}->{$license_text}->{$copyright}};
88 print COPYRIGHT "Files: ", join(', ', @filelist), "\n";
89 print COPYRIGHT "Copyright: $copyright\n" if length($copyright);
90 print COPYRIGHT "License: $license\n" if length($license);
91 if(length($license_text))
93 my @text=split(/\n/, $license_text);
94 print COPYRIGHT map { " " . $_ . "\n" } @text;
100 print COPYRIGHT license_trailer(sort keys(%$licenses));
106 my @licenses_used=@_;
108 "Apache-2.0" => "Apache License Version 2.0",
109 "Artistic" => "Artistic License",
110 "GFDL-1.2" => "GNU Free Documentation License Version 1.2",
111 "GFDL-1.3" => "GNU Free Documentation License Version 1.3",
112 "GFDL" => "GNU Free Documentation License",
113 "GPL-2" => "GNU General Public License Version 2",
114 "GPL-3" => "GNU General Public License Version 3",
115 "GPL" => "GNU General Public License",
116 "LGPL-2" => "GNU Library General Public License Version 2",
117 "LGPL-2.1" => "GNU Lesser General Public License Version 2.1",
118 "LGPL-3" => "GNU Lesser General Public License Version 3",
119 "LGPL" => "GNU Lesser General Public License",
123 TYPE: for my $type (keys(%$license_data))
125 for my $license (@licenses_used)
127 if($license =~ /$type(\+|\b)/i)
129 $types_found{$type}=1;
130 # avoid matching eg GPL-2 *and* GPL
136 for my $type (sort keys(%types_found))
138 $text .= "The complete text of the " . $license_data->{$type} ." can be\n";
139 $text .= "found in /usr/share/common-licenses/$type\n";
147 unless(open(NEW, $NEWFILES))
149 die("$me: $NEWFILES: cannot open: $!\n");
156 my $in_license_text=0;
164 if($in_license_text && /^\s+(.*)/)
166 $license_text .= "\n" . $1;
168 elsif(/^File:\s*(.*)/)
171 # save previous entry
172 if(length($file) && length($hash))
174 $files->{$file}={ hash=>$hash,
175 copyright=>$copyright,
177 license_text=>$license_text };
185 elsif(/^Hash:\s*(.*)/)
189 elsif(/^Copyright:\s*(.*)/)
193 elsif(/^License:\s*(.*)/)
197 elsif(/^License_text:\s*(.*)/)
202 elsif($in_license_text && /^\s+(.*)/)
204 $license_text .= "\n" . $1;
208 warn("$me: $file: line $line not recognized\n");
213 if(length($file) && length($hash))
215 $files->{$file}={ hash=>$hash,
216 copyright=>$copyright,
218 license_text=>$license_text };
227 if(open(LICENSECHECK, "licensecheck --copyright \"$file\"|"))
229 while(<LICENSECHECK>)
242 $copyright =~ s/^\s*Copyright\s*:\s*//;
243 $license =~ s/.*UNKNOWN.*//;
244 $license =~ s/(L?GPL) \(v([\.\d]+) or later\)/$1-$2+/i;
245 $license =~ s/(L?GPL) \(v([\.\d]+)\)/$1-$2/i;
246 $license =~ s/G?FDL \(v([\.\d]+) or later\)/GFDL-$1+/i;
247 $license =~ s/G?FDL \(v([\.\d]+)\)/GFDL-$1/i;
248 $license =~ s/Apache \(v([\.\d]+) or later\)/Apache-$1+/i;
249 $license =~ s/Apache \(v([\.\d]+)\)/Apache-$1+/i;
251 return($license, $copyright);
257 unless(open(FILE, $filename))
259 warn("$me: $filename: cannot open: $!\n");
263 for(my $i=0; $i < 15; $i++)
270 my $hash=sha256_hex($header);
271 if( (!exists($files->{$filename})) ||
272 ($files->{$filename}->{hash} ne $hash))
274 filechanged($filename, $hash, $header);
281 my($filename, $hash, $header)=@_;
282 my($license_guess, $copyright_guess)=guess_license($filename);
285 license=>$license_guess,
286 copyright=>$copyright_guess,
289 if(exists($files->{$filename}))
291 if(exists($files->{$filename}->{copyright}))
293 $new->{$filename}->{copyright_old}=$files->{$filename}->{copyright};
295 if(exists($files->{$filename}->{license}))
297 $new->{$filename}->{license_old}=$files->{$filename}->{license};
299 if(exists($files->{$filename}->{license_text}))
301 $new->{$filename}->{license_text_old}=$files->{$filename}->{license_text};
308 if(/^\.git/ || /^\.cvs/ || /^debian/)
310 $File::Find::prune=1;
314 push(@filenames, $File::Find::name);
321 # find(\&wanted, "lib/uniname");
322 # find(\&wanted, "lib/uniconv");
327 unless(open(YAML,$FILESCACHE))
329 warn("$me: cannot load cache $FILESCACHE: $!\n");
344 unless(open(YAML,">$FILESCACHE"))
346 warn("$me: cannot save cache $FILESCACHE: $!\n");
349 print YAML Dump($files);
358 warn("$me: no new/changed files found\n");
361 unless(open(NEW,">$NEWFILES"))
363 die("$me: cannot write to $NEWFILES: $!\n");
365 for my $file (sort keys %$new)
367 print NEW "File: $file\n";
368 print NEW "Hash: ", $new->{$file}->{hash}, "\n";
369 print NEW "License: ", $new->{$file}->{license}, "\n";
370 print NEW "Copyright: ", $new->{$file}->{copyright}, "\n";
371 print NEW "Licence-Text: \n";
372 if($new->{$file}->{license_old})
374 print NEW "#License_old: ", $new->{$file}->{license_old}, "\n";
376 if($new->{$file}->{copyright_old})
378 print NEW "#Copyright_old: ", $new->{$file}->{copyright_old}, "\n";
380 if($new->{$file}->{licence_text_old})
382 print NEW "#License_text_old: ", $new->{$file}->{licence_text_old}, "\n";
384 print NEW "#Header: \n";
385 my @headerlines=split(/\n/, $new->{$file}->{header});
386 @headerlines=map { "#" . $_ } grep { defined $_; } @headerlines;
387 print NEW join("\n", @headerlines);
396 my %newnames = map { $_ => 1 } @filenames;
397 for my $file (sort keys(%$files))
399 unless(exists($newnames{$file}))
401 push(@deleted, $file);
406 print "Removed files:\n";
407 print join("\n", @deleted),"\n";
424 die("usage: $me [--scan] [--merge]\n",
425 " --scan Scan for new files & files with changed copyright headers\n",
426 " Writes to debian/clscan/new.txt for manual review.\n",
427 " --merge Merges new data from debian/clscan/new.txt\n",
428 " Writes updated debian/copyright.\n");