Fix update-copyright script for files without a header.
[cvc5.git] / contrib / update-copyright.pl
1 #!/usr/bin/perl -w
2 #
3 # update-copyright.pl
4 # Copyright (c) 2009-2018 The CVC4 Project
5 #
6 # usage: update-copyright [-m] [files/directories...]
7 # update-copyright [-h | --help]
8 #
9 # This script goes through a source directory rewriting the top bits of
10 # source files to match a template (inline, below). For files with no
11 # top comment, it adds a fresh one.
12 #
13 # if no files/directories are unspecified, the script scans its own
14 # parent directory's "src" directory. Since it lives in contrib/ in
15 # the CVC4 source tree, that means src/ in the CVC4 source tree.
16 #
17 # If -m is specified as the first argument, all files and directories
18 # are scanned, but only ones modified in the index or working tree
19 # are modified (i.e., those that have at least one status M in
20 # "git status -s").
21 #
22 # It ignores any file/directory not starting with [a-zA-Z]
23 # (so, this includes . and .., vi swaps, .git meta-info,
24 # .deps, etc.)
25 #
26 # It ignores any file not ending with one of:
27 # .c .cc .cpp .C .h .hh .hpp .H .y .yy .ypp .Y .l .ll .lpp .L .g
28 # [ or those with ".in" also suffixed, e.g., .cpp.in ]
29 # (so, this includes emacs ~-backups, CVS detritus, etc.)
30 #
31 # It ignores any directory matching $excluded_directories
32 # (so, you should add here any sources imported but not covered under
33 # the license.)
34 #
35
36 my $excluded_directories = '^(CVS|generated)$';
37 my $excluded_paths = '^(';
38 $excluded_paths .= 'src/bindings/compat/.*';
39 # different license
40 $excluded_paths .= '|src/util/channel.h';
41 # minisat license
42 $excluded_paths .= '|src/prop/(bv)?minisat/core/.*';
43 $excluded_paths .= '|src/prop/(bv)?minisat/mtl/.*';
44 $excluded_paths .= '|src/prop/(bv)?minisat/simp/.*';
45 $excluded_paths .= '|src/prop/(bv)?minisat/utils/.*';
46 $excluded_paths .= '$)';
47
48 # Years of copyright for the template. E.g., the string
49 # "1985, 1987, 1992, 1997, 2008" or "2006-2009" or whatever.
50 my $years = '2009-2018';
51
52 my $standard_template = <<EOF;
53 ** This file is part of the CVC4 project.
54 ** Copyright (c) $years by the authors listed in the file AUTHORS
55 ** in the top-level source directory) and their institutional affiliations.
56 ** All rights reserved. See the file COPYING in the top-level source
57 ** directory for licensing information.\\endverbatim
58 EOF
59
60 ## end config ##
61
62 use strict;
63 use Fcntl ':mode';
64
65 my $dir = $0;
66 $dir =~ s,/[^/]+/*$,,;
67
68 if($#ARGV >= 0 && ($ARGV[0] eq '-h' || $ARGV[0] eq '--help')) {
69 open(my $SELF, $0) || die "error opening $0 for reading";
70 while($_ = <$SELF>) {
71 last if !/^#/;
72 print;
73 }
74 close $SELF;
75 exit;
76 }
77
78 # whether we ONLY process files with git status "M"
79 my $modonly = 0;
80
81 if($#ARGV >= 0 && $ARGV[0] eq '-m') {
82 $modonly = 1;
83 shift;
84 }
85
86 my @searchdirs = ();
87 if($#ARGV == -1) {
88 (chdir($dir."/..") && -f "src/include/cvc4_public.h") || die "can't find top-level source directory for CVC4";
89 my $pwd = `pwd`; chomp $pwd;
90
91 print <<EOF;
92 Warning: this script is dangerous. It will overwrite the header comments in your
93 source files to match the template in the script, attempting to retain file-specific
94 comments, but this isn't guaranteed. You should run this in a git working tree
95 and run "git diff" after to ensure everything was correctly rewritten.
96
97 The directories in which to search for and change sources is:
98 $pwd/src
99 $pwd/examples
100 $pwd/test
101
102 Continue? y or n:
103 EOF
104
105 $_ = <STDIN>; chomp;
106 die 'aborting operation' if !( $_ eq 'y' || $_ eq 'yes' || $_ eq 'Y' || $_ eq 'YES' );
107
108 $searchdirs[0] = 'src';
109 $searchdirs[1] = 'examples';
110 $searchdirs[2] = 'test';
111 } else {
112 @searchdirs = @ARGV;
113 }
114
115 print "Updating sources...\n";
116
117 while($#searchdirs >= 0) {
118 my $dir = shift @searchdirs;
119 $dir =~ s,\/$,,; # remove trailing slash from directory
120 my $mode = (stat($dir))[2] || warn "file or directory \`$dir' does not exist!";
121 my $is_directory = S_ISDIR($mode);
122 if($is_directory) {
123 recurse($dir);
124 } else {
125 if($dir =~ m,^(.*)\/([^/]*)$,) {
126 my($dir, $file) = ($1, $2);
127 if($dir eq "") {
128 $dir = "/";
129 }
130 handleFile($dir, $file);
131 } else {
132 handleFile(".", $dir);
133 }
134 }
135 }
136
137 sub handleFile {
138 my ($srcdir, $file) = @_;
139 return if !($file =~ /\.(c|cc|cpp|C|h|hh|hpp|H|y|yy|ypp|Y|l|ll|lpp|L|g|java)(\.in)?$/);
140 return if ($srcdir.'/'.$file) =~ /$excluded_paths/;
141 return if $modonly && `git status -s "$srcdir/$file" 2>/dev/null` !~ /^(M|.M)/;
142 print "$srcdir/$file...";
143 my $infile = $srcdir.'/'.$file;
144 my $outfile = $srcdir.'/#'.$file.'.tmp';
145 open(my $IN, $infile) || die "error opening $infile for reading";
146 open(my $OUT, '>', $outfile) || die "error opening $outfile for writing";
147 open(my $AUTHOR, "$dir/get-authors " . $infile . '|');
148 my $authors = <$AUTHOR>; chomp $authors;
149 close $AUTHOR;
150 $_ = <$IN>;
151 if(m,^(%\{)?/\*(\*| )\*\*\*,) {
152 print "updating\n";
153 if($file =~ /\.(y|yy|ypp|Y)$/) {
154 print $OUT "%{/******************* */\n";
155 print $OUT "/** $file\n";
156 } elsif($file =~ /\.g$/) {
157 # avoid javadoc-style comment here; antlr complains
158 print $OUT "/* ******************* */\n";
159 print $OUT "/*! \\file $file\n";
160 } else {
161 print $OUT "/********************* */\n";
162 print $OUT "/*! \\file $file\n";
163 }
164 print $OUT " ** \\verbatim\n";
165 print $OUT " ** Top contributors (to current version):\n";
166 print $OUT " ** $authors\n";
167 my $comment_stub = "";
168 while(my $line = <$IN>) {
169 if($line =~ /\b[Cc]opyright\b/ && $line !~ /\bby the authors listed in the file AUTHORS\b/) {
170 # someone else holds this copyright
171 print $OUT $line;
172 }
173 last if $line =~ /^ \*\*\s*$/;
174 if($line =~ /\*\//) {
175 $comment_stub = " ** [[ Add lengthier description here ]]\n\
176 ** \\todo document this file\n\
177 $line";
178 last;
179 }
180 }
181 print $OUT $standard_template;
182 print $OUT " **\n";
183 if($comment_stub) {
184 print $OUT $comment_stub;
185 }
186 } else {
187 my $line = $_;
188 print "adding\n";
189 if($file =~ /\.(y|yy|ypp|Y)$/) {
190 print $OUT "%{/******************* */\n";
191 print $OUT "/*! \\file $file\n";
192 } elsif($file =~ /\.g$/) {
193 # avoid javadoc-style comment here; antlr complains
194 print $OUT "/* ******************* */\n";
195 print $OUT "/*! \\file $file\n";
196 } else {
197 print $OUT "/********************* */\n";
198 print $OUT "/*! \\file $file\n";
199 }
200 print $OUT " ** \\verbatim\n";
201 print $OUT " ** Top contributors (to current version):\n";
202 print $OUT " ** $authors\n";
203 print $OUT $standard_template;
204 print $OUT " **\n";
205 print $OUT " ** \\brief [[ Add one-line brief description here ]]\n";
206 print $OUT " **\n";
207 print $OUT " ** [[ Add lengthier description here ]]\n";
208 print $OUT " ** \\todo document this file\n";
209 print $OUT " **/\n\n";
210 print $OUT $line;
211 if($file =~ /\.(y|yy|ypp|Y)$/) {
212 while(my $line = <$IN>) {
213 chomp $line;
214 if($line =~ '\s*%\{(.*)') {
215 print $OUT "$1\n";
216 last;
217 }
218 # just in case something's weird with the file ?
219 if(!($line =~ '\s*')) {
220 print $OUT "$line\n";
221 last;
222 }
223 }
224 }
225 }
226 while(my $line = <$IN>) {
227 print $OUT $line;
228 }
229 close $IN;
230 close $OUT;
231 rename($outfile, $infile) || die "can't rename working file \`$outfile' to \`$infile'";
232 }
233
234 sub recurse {
235 my ($srcdir) = @_;
236 print "in dir $srcdir\n";
237 opendir(my $DIR, $srcdir);
238 while(my $file = readdir $DIR) {
239 next if !($file =~ /^[a-zA-Z]/);
240
241 my $mode = (stat($srcdir.'/'.$file))[2];
242 my $is_directory = S_ISDIR($mode);
243 if($is_directory) {
244 next if $file =~ /$excluded_directories/;
245 recurse($srcdir.'/'.$file);
246 } else {
247 handleFile($srcdir, $file);
248 }
249 }
250 closedir $DIR;
251 }
252
253 ### Local Variables:
254 ### perl-indent-level: 2
255 ### End: