3 # InspIRCd -- Internet Relay Chat Daemon
5 # Copyright (C) 2020 Sadie Powell <sadie@witchery.services>
7 # This file is part of InspIRCd. InspIRCd is free software: you can
8 # redistribute it and/or modify it under the terms of the GNU General Public
9 # License as published by the Free Software Foundation, version 2.
11 # This program is distributed in the hope that it will be useful, but WITHOUT
12 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13 # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
16 # You should have received a copy of the GNU General Public License
17 # along with this program. If not, see <http://www.gnu.org/licenses/>.
23 use warnings FATAL => qw(all);
25 use File::Basename qw(dirname);
27 use FindBin qw($RealDir);
28 use List::Util qw(uniq);
29 use POSIX qw(strftime);
31 use lib dirname $RealDir;
35 my @ignored_revisions = (
36 '0b4285abd12323920d92fee51e199edd7527dbec', # adding copyright headers
37 '46a39046196f55b52336e19662bb7bac85b731ac', # adding copyright headers
38 '4a6fedd9324d87349a806c9c1d0ae6e7d3c1fd38', # mass-updating descriptions
39 '56375392ba94f2552bbeeeab4fd39e1e50295525', # sadie's name change
40 'bab14f0dd2345c9d7dcbc47c918563709e1ac094', # peavey breaking line endings
41 'f2acdbc3820f0f4f5ef76a0a64e73d2a320df91f', # peavey fixing line endings
44 my @paths = File::Util->new->list_dir(dirname($RealDir) => { recurse => 1 });
46 for my $path (@paths) {
48 next if $path =~ /\/\./;
49 next if $path =~ /\/build\//;
50 next if $path =~ /\/vendor\//;
52 if (system "git ls-files --error-unmatch -- $path 1>/dev/null 2>/dev/null") {
53 print_format "Skipping <|YELLOW $path|> as it has not been committed.\n" if defined $ENV{MKHEADERS_VERBOSE};
57 open(my $fh, $path) or print_error "unable to read from $path: $!";
58 my ($copyright, $indent, @lines);
59 for my $line (<$fh>) {
61 if ($line =~ /^([^0-9A-Za-z]+\s)Copyright\s+\(C\)\s+[^<]+\s+<[^>]+>$/) {
62 $copyright = scalar @lines;
70 if (defined $copyright) {
71 print_format "Updating copyright headers in <|GREEN $path|>.\n" if defined $ENV{MKHEADERS_VERBOSE};
72 my (%author, %authors);
73 my $ignored_args = join ' ', map { "--ignore-rev $_" } @ignored_revisions;
74 for my $line (split /\n+/, `git blame $ignored_args --incremental -w HEAD -- $path`) {
75 if ($line =~ /^([0-9a-f]{40})(?:\s\d+){3}$/) {
76 $author{COMMITS} //= [];
77 push @{$author{COMMITS}}, $1;
78 } elsif ($line =~ /^author (.+)/) {
80 } elsif ($line =~ /^author-mail <(.+)>/) {
82 } elsif ($line =~ /^author-time (.+)/) {
83 $author{YEAR} = strftime '%Y', gmtime $1;
84 } elsif ($line =~ /^filename /) {
85 next unless scalar keys %author > 1;
86 my $display = sprintf "%s <%s>", $author{NAME}, $author{EMAIL};
87 $authors{$display} //= [];
88 push @{$authors{$display}}, $author{YEAR};
89 for my $commit (uniq @{$author{COMMITS}}) {
90 my $details = `git rev-list --format=%B --max-count=1 $commit`;
91 while ($details =~ /co-authored-by: ([^<]+<[^>]+>)/gi) {
93 push @{$authors{$1}}, $author{YEAR};
101 while (my ($display, $years) = each %authors) {
102 next if $display eq 'InspIRCd Robot <noreply@inspircd.org>';
103 my ($last_year, $start_year, @year_ranges);
104 for my $year (uniq sort @$years) {
105 if (!defined $last_year) {
106 $start_year = $last_year = $year
107 } elsif ($year == $last_year + 1) {
110 if ($last_year == $start_year) {
111 push @year_ranges, $last_year;
113 push @year_ranges, "$start_year-$last_year";
115 $start_year = $last_year = $year;
118 if (defined $last_year) {
119 if ($last_year == $start_year) {
120 push @year_ranges, $last_year;
122 push @year_ranges, "$start_year-$last_year";
126 my $joined_years = join ', ', @year_ranges;
127 push @copyrights, "${\$indent}Copyright (C) $joined_years $display";
130 splice @lines, $copyright, 0, reverse sort @copyrights;
131 open(my $fh, '>', $path) or print_error "unable to write to $path: $!";
132 for my $line (@lines) {
136 push @updated, $path;
138 print_format "Skipping <|YELLOW $path|> as it contains no copyright headers.\n" if defined $ENV{MKHEADERS_VERBOSE};
142 execute 'git', 'commit',
143 '--author', 'InspIRCd Robot <noreply@inspircd.org>',
144 '--message', 'Update copyright headers.',