summaryrefslogtreecommitdiff
path: root/checkLicense.pl
diff options
context:
space:
mode:
Diffstat (limited to 'checkLicense.pl')
-rwxr-xr-xcheckLicense.pl363
1 files changed, 363 insertions, 0 deletions
diff --git a/checkLicense.pl b/checkLicense.pl
new file mode 100755
index 0000000..88f604c
--- /dev/null
+++ b/checkLicense.pl
@@ -0,0 +1,363 @@
+#!/usr/bin/perl
+use strict;
+use IO::Handle;
+use File::Spec;
+use File::Copy;
+use File::Basename;
+
+my $base = '.';
+my $version = '0.2';
+my $mode = 'unknown';
+my $clean = 0;
+my $force = 0;
+my $verbose = 0;
+my $ownProject = 0;
+my $prefixFN = 'License.txt';
+my $years = '1897';
+my $licenseTime = 0;
+my $testOnly = 0;
+my $serialVersion = '713L';
+my $realAuthor = '<a href="mailto:geronimo013@gmx.de">Geronimo</a>';
+my $modeConfig = {
+ 'java' => { 'match' => '\.java$', 'key' => 'package ', 'cmt0' => '/**', 'cmt1' => ' * ', 'cmt2' => ' */' }
+ , 'php' => { 'match' => '\.php$', 'key' => 'use ', 'cmt0' => '/**', 'cmt1' => ' * ', 'cmt2' => ' */' }
+ , 'c++' => { 'match' => '\.(?:cc|h|cpp|hpp)$', 'key' => '#\s*(?:ifndef|include) ', 'cmt0' => '/**', 'cmt1' => ' * ', 'cmt2' => ' */' }
+};
+my $summaries = {};
+my @license = ();
+
+sub startScan {
+ my $basedir = File::Spec->rel2abs('.');
+ my $pn = basename($basedir);
+ my $localLicense = $basedir.'/doc/Legal.Note';
+
+ if (-s $localLicense) {
+ print('project has private license file: '.$localLicense."\n");
+ prepare($localLicense);
+ }
+ else {
+ prepare($prefixFN);
+ }
+ loadSummary($pn);
+ tellSummary($pn) if $verbose;
+ findFiles($base);
+}
+
+sub findFiles {
+ my $path = shift;
+ my $match = shift;
+ my $pn = basename(File::Spec->rel2abs('.'));
+ my $dp;
+
+ opendir($dp, $path) or die "can't opendir $path: $!";
+ while (my $entry = readdir($dp)){
+ next if($entry =~ /^\./);
+ if (-d $path.'/'.$entry) {
+ findFiles($path.'/'.$entry);
+ }
+ if ($entry =~ /$modeConfig->{$mode}{'match'}/) {
+ my @st = stat($path.'/'.$entry);
+ my %file;
+
+ $file{'name'} = $entry;
+ $file{'path'} = $path.'/'.$entry;
+ $file{'time'} = $st[9];
+ $file{'nprj'} = $pn;
+
+ processFile(\%file);
+ }
+ }
+ closedir($dp);
+}
+
+sub containsCopyright {
+ my $fn = shift;
+ my $fp;
+ my $own = 0;
+ my $found = 0;
+
+ open($fp, $fn) or die('Oups - can not open file '.$fn."\n");
+ while (my $line = <$fp>) {
+ last if $line =~ /$modeConfig->{$mode}{'key'}/;
+
+ if ($line =~ /=== legal notice ===/) {
+ $found = 1;
+ $own = 1;
+ last;
+ }
+ elsif ($line =~ /copyright/i) {
+ $found = 1;
+ if ($line =~ /copyright.+?Reinhard Mantey/i) { $own = 1; }
+ else { $own = 0; }
+ last;
+ }
+ }
+ close($fp);
+ $own = 1 if !$found && $ownProject;
+
+ return ($own, $found);
+}
+
+sub processFile {
+ my $fi = shift;
+ my ($own, $found) = containsCopyright($fi->{'path'});
+
+ if ($found && !$own) {
+ print('file '.$fi->{'path'}.' is owned by foreign copyright, so don\'t touch it!'."\n");
+ return;
+ }
+ print("\t".'process file '.$fi->{'path'}.' as '.($own ? 'own' : 'foreign').' source.'."\n") if $verbose;
+ return if ($testOnly);
+ my @tag = (' '
+ , 'File: '
+ , 'Author: '
+ , 'Project: '
+ , 'Created: '
+ , 'Copyright: ');
+ my %tags;
+ my @parts;
+ my @cmt;
+ my $appInfo;
+ my $fileInfo;
+ my $comment;
+ my $line;
+ my $prefix;
+ my $first = 1;
+ my ($sfp, $dfp);
+
+ print('patch '.$fi->{'path'}."\n");
+ $fi->{'bak'} = $fi->{'path'}.'.bak';
+ move($fi->{'path'}, $fi->{'bak'});
+ open($sfp, $fi->{'bak'}) or die('could not read source '.$fi->{'bak'}.' '.$!."\n");
+ open($dfp, '> '.$fi->{'path'}) or die('could not write source '.$fi->{'path'}.' '.$!."\n");
+
+ while ($line = <$sfp>) {
+ last if $line =~ /$modeConfig->{$mode}{'key'}/;
+ $line =~ s/^(?:\/\*+| \*+ |\/+ | \*+\/)(.*)$/$1/;
+ next if $line =~ /^\s*$/;
+ next if $line =~ /^ \*\n/;
+ @parts = split(/\:\s*/, $line);
+
+ if ($line =~ /Created on (.+)$/) {
+ $tags{'Created'} = $1;
+ }
+ elsif ($line =~ /=== legal notice ===/) {
+ $found = 1;
+ $own = 1;
+ }
+ elsif ($line =~ /copyright/i) {
+ $found = 1;
+ if ($line =~ /copyright.+?Reinhard Mantey/i) { $own = 1; }
+ else { $own = 0; }
+ }
+ elsif ($#parts > 0) {
+ $parts[1] =~ s/\n//;
+ $tags{$parts[0]} = $parts[1];
+ }
+ else {
+ push(@cmt, $line);
+ }
+ }
+ $prefix = $modeConfig->{$mode}{'cmt0'}."\n";
+ foreach (@license) {
+ my $pl = $_;
+
+ if ($pl =~ /\@\@AppInfo\@\@/) {
+ foreach (@{$summaries->{$fi->{'nprj'}}{'content'}}) {
+ my $sl = $_;
+
+ if ($first) {
+ $first = 0;
+ $prefix .= $modeConfig->{$mode}{'cmt1'}.$tag[3].$sl;
+ }
+ else {
+ $prefix .= $modeConfig->{$mode}{'cmt1'}.$tag[0].$sl;
+ }
+ }
+ }
+ elsif ($pl =~ /\@\@Comment\@\@/) {
+ foreach (@cmt) {
+ $prefix .= $modeConfig->{$mode}{'cmt1'}.$_;
+ }
+ }
+ elsif ($pl =~ /\@\@FileInfo\@\@/) {
+ $prefix .= $modeConfig->{$mode}{'cmt1'}.$tag[1].$fi->{'name'}."\n";
+ if ($ownProject && $own) {
+ $prefix .= $modeConfig->{$mode}{'cmt1'}.$tag[4].$tags{'Created'}."\n";
+ $prefix .= $modeConfig->{$mode}{'cmt1'}.$tag[2].$realAuthor."\n";
+ }
+ }
+ else {
+ $pl =~ s/\@\@YEAR\@\@/$years/;
+ $prefix .= $modeConfig->{$mode}{'cmt1'}.$pl;
+ }
+ }
+ $prefix .= $modeConfig->{$mode}{'cmt2'}."\n";
+
+ print($dfp $prefix);
+ print($dfp $line);
+
+ while ($line = <$sfp>) {
+ if ($line =~ /\@author\s+(.+)$/) {
+ my $author = $1;
+
+ if ($author =~ /^(?:reinhard|Reinhard Mantey|django)\s*$/) {
+ print('have to patch author: '.$author." from line: ".$line) if $verbose;
+ $line =~ s/^(.+?\@author\s+)(.+)$/\1$realAuthor/;
+ }
+ }
+ if ($line =~ /^(.*?)\@author.+?Reinhard Mantey/) {
+ print('have to patch author from line: '.$line) if $verbose;
+ $line = $1.'@author '.$realAuthor."\n";
+ }
+ if ($line =~ /^(.+?)serialVersionUID(\s+)=(\s+)([+\-0-9L]+);(.*)$/) {
+ print('have to patch serial version uid from line: '.$line) if $verbose;
+ $line = $1.'serialVersionUID'.$2.'='.$3.$serialVersion.";\n".$5;
+ }
+ print($dfp $line);
+ }
+ close($sfp);
+ close($dfp);
+
+ if ($force) {
+ utime($licenseTime, $licenseTime, $fi->{'path'});
+ }
+ elsif ($fi->{'time'} > $summaries->{$fi->{'nprj'}}{'time'}) {
+ utime($fi->{'time'}, $fi->{'time'}, $fi->{'path'});
+ }
+ else {
+ utime($summaries->{$fi->{'nprj'}}{'time'}, $summaries->{$fi->{'nprj'}}{'time'}, $fi->{'path'});
+ }
+ unlink($fi->{'bak'}) if ($clean);
+}
+
+sub loadSummary {
+ my $pn = shift;
+
+ if (not defined($summaries->{$pn})) {
+ print('have to load summary for '.$pn.' ... ') if $verbose;
+ my $fp;
+ my $fn = './summary.txt';
+ my @st = stat($fn);
+ my @sum;
+
+ if (-s $fn) {
+ open($fp, $fn) or die('Oups, can not open summary file >'.$fn.'<'."\n");
+ while (my $line = <$fp>) {
+ push(@sum, $line);
+ }
+ close($fp);
+ }
+ $summaries->{$pn}{'content'} = [@sum];
+ $summaries->{$pn}{'time'} = $st[9];
+ print(' DONE.'."\n") if $verbose;
+ }
+ else {
+ print('summary already loaded for '.$pn."\n") if $verbose;
+ }
+}
+
+sub tellSummary {
+ my $pn = shift;
+
+ if (defined($summaries->{$pn})) {
+ foreach my $line (@{$summaries->{$pn}{'content'}}) {
+ print("\t".$line);
+ }
+ }
+ else {
+ die('no summary for '.$pn." found.\n");
+ }
+}
+
+sub prepare {
+ my $licenseFile = shift;
+ print('prepare: mode='.$mode."\n") if $verbose;
+ print('prepare: prefix='.$licenseFile."\n") if $verbose;
+ print('prepare: years='.$years."\n") if $verbose;
+ my $fp;
+ my $own = 0;
+
+ sleep(1) if $verbose && !$testOnly;
+ @license = ();
+ open($fp, $licenseFile) or die('missing license-file >'.$licenseFile.'<!'."\n");
+ while (my $line = <$fp>){
+ $own = 1 if $line =~ /=== legal notice ===/;
+ push(@license, $line);
+ }
+ close($fp);
+ $ownProject = $own;
+}
+
+sub usage {
+ my $cs = "\n".'known options:'."\n";
+ $cs .= ' --mode <lang> the language mode to work on'."\n";
+ $cs .= ' --prefix <path> the path to a file, used as copyright-prefix'."\n";
+ $cs .= ' --years <time[-interval]> will substitute the year in copyright'."\n";
+ $cs .= ' --clean don\'t keep backup-files'."\n";
+ $cs .= ' --help this text'."\n";
+ return $cs;
+}
+
+sub processArgs {
+ while (my $arg = shift) {
+ if ($arg =~ /^--?(.+)$/) {
+ my $param = $1;
+
+ for ($param) {
+ /^mode$/ and do { $mode = shift; last; };
+ /^force$/ and do { $force = 1; $licenseTime = time(); last; };
+ /^prefix$/ and do { $prefixFN = shift; last; };
+ /^years$/ and do { $years = shift; last; };
+ /^clean$/ and do { $clean = 1; last; };
+ /^verbose$/ and do { $verbose = 1; last; };
+ { die('unsupported parameter: '.$arg."\n".usage()); };
+ }
+ }
+ }
+}
+
+#
+# main
+#
+autoflush STDOUT 1;
+print('checkLicense.pl Version: '.$version."\n");
+print(' scans source files for missing copyright-info.'."\n");
+print(' Possible language-mode: java, php, c++'."\n\n");
+processArgs(@ARGV);
+die('unsupported mode >'.$mode.'<'."\n".usage()) if not defined($modeConfig->{$mode});
+$prefixFN = File::Spec->rel2abs($prefixFN) if -s $prefixFN;
+if (-s 'summary.txt') {
+ startScan();
+ }
+else {
+ checkDirectory(File::Spec->rel2abs('.'));
+ }
+
+sub checkDirectory {
+ my $dir = shift;
+ my $dp;
+
+ opendir($dp, $dir) or die('could not read directory '.$dir.'! '.$!."\n");
+ print('gonna check directory '.$dir."\n") if $verbose;
+ while (my $entry = readdir($dp)) {
+ next if $entry =~ /^\./;
+ my $path = $dir.'/'.$entry;
+
+ if (-d $path) {
+ if (-s $path.'/summary.txt') {
+ print('found summary at '.$path."\n");
+ chdir $path;
+ startScan();
+ chdir $dir;
+ }
+ elsif ($entry =~ /^libs$/) {
+ checkDirectory($path);
+ }
+ else {
+ print(' skip '.$path.' (not a subproject)'."\n") if $verbose;
+ }
+ }
+ }
+ closedir($dp);
+ }