#!/usr/bin/perl -0777 -pi # Update an FSF copyright year list to include the current year. my $VERSION = '2009-08-05.20:47'; # UTC # Copyright (C) 2009 Free Software Foundation, Inc. # # 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, 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 . # Written by Jim Meyering and Joel E. Denny # The arguments to this script should be names of files that contain FSF # copyright statements to be updated. For example, you may wish to # place a target like the following in the top-level makefile in your # project: # # .PHONY: update-copyright # update-copyright: # if test -d .git; then \ # git grep -l -w Copyright \ # | grep -v -E '(^|/)(COPYING|ChangeLog)' \ # | xargs $(srcdir)/build-aux/$@; \ # fi # # In the second grep, you can build a list of files to skip within your # project. # # Iff an FSF copyright statement is discovered in a file and the final # year is not the current year, the statement is updated for the new # year and reformatted to fit within 72 columns. A warning is printed # for every file for which no FSF copyright statement is discovered. # # Each file's FSF copyright statement must be formated correctly in # order to be recognized. For example, each of these is fine: # # Copyright @copyright{} 1990-2005, 2007-2009 Free Software # Foundation, Inc. # # # Copyright (C) 1990-2005, 2007-2009 Free Software # # Foundation, Inc. # # /* # * Copyright © 90,2005,2007-2009 # * Free Software Foundation, Inc. # */ # # However, the following format is not recognized because the line # prefix changes after the first line: # # /* Copyright (C) 1990-2005, 2007-2009 Free Software # * Foundation, Inc. */ # # The following copyright statement is not recognized because the # copyright holder is not the FSF: # # Copyright (C) 1990-2005, 2007-2009 Acme, Inc. # # However, any correctly formatted FSF copyright statement following # either of the previous two copyright statements would be recognized. # # The exact conditions that a file's FSF copyright statement must meet # to be recognized are: # # 1. The format is "Copyright (C)" (where "(C)" can also be "(c)", # "@copyright{}", or "©"), then a list of copyright years, and # then the name of the copyright holder, which is "Free Software # Foundation, Inc.". # 2. The "Copyright (C)" appears at the beginning of a line except # that it may be prefixed by any sequence (e.g., a comment) of no # more than 5 characters. # 3. Iff such a prefix is present, the same prefix appears at the # beginning of each remaining line within the FSF copyright # statement. # 4. Blank lines, even if preceded by the prefix, do not appear # within the FSF copyright statement. # 5. Each copyright year is 2 or 4 digits, and years are separated by # commas or dashes. Whitespace may occur after commas. # 6. It is the first FSF copyright statement that meets all of the # above conditions. Subsequent FSF copyright statements are # ignored. use strict; use warnings; my $copyright_re = 'Copyright (?:\([cC]\)|@copyright{}|©)'; my $holder = 'Free Software Foundation, Inc.'; my $prefix_max = 5; my $margin = 72; my $tab_width = 8; my $this_year = $ENV{UPDATE_COPYRIGHT_YEAR}; if (!$this_year || $this_year !~ m/^\d{4}$/) { my ($sec, $min, $hour, $mday, $month, $year) = localtime (time ()); $this_year = $year + 1900; } # Unless the file consistently uses "\r\n" as the EOL, use "\n" instead. my $eol = /(?:^|[^\r])\n/ ? "\n" : "\r\n"; my $leading; my $prefix; my $ws_re; my $stmt_re; while (/(^|\n)(.{0,$prefix_max})$copyright_re/g) { $leading = $1; $prefix = $2; $ws_re = '[ \t\r\f]'; # \s without \n $ws_re = "(?:$ws_re*(?:$ws_re|\\n" . quotemeta($prefix) . ")$ws_re*)"; my $holder_re = $holder; $holder_re =~ s/\s/$ws_re/g; my $stmt_remainder_re = "$ws_re(?:(?:\\d\\d)?\\d\\d(,$ws_re?|-))*" . "((?:\\d\\d)?\\d\\d)$ws_re$holder_re"; if (/\G$stmt_remainder_re/) { $stmt_re = quotemeta("$leading$prefix") . "($copyright_re$stmt_remainder_re)"; last; } } if (defined $stmt_re) { /$stmt_re/ or die; # Should never die. my $stmt = $1; my $sep = $2 ? $2 : ""; my $final_year_orig = $3; # Handle two-digit year numbers like "98" and "99". my $final_year = $final_year_orig; $final_year <= 99 and $final_year += 1900; if ($final_year != $this_year) { # Update the year. if ($sep eq '-' && $final_year + 1 == $this_year) { $stmt =~ s/$final_year_orig/$this_year/; } elsif ($sep ne '-' && $final_year + 1 == $this_year) { $stmt =~ s/$final_year_orig/$final_year-$this_year/; } else { $stmt =~ s/$final_year_orig/$final_year, $this_year/; } # Normalize all whitespace including newline-prefix sequences. $stmt =~ s/$ws_re/ /g; # Put spaces after commas. $stmt =~ s/, ?/, /g; # Format within margin. my $stmt_wrapped; my $text_margin = $margin - length($prefix); if ($prefix =~ /^(\t+)/) { $text_margin -= length($1) * ($tab_width - 1); } while (length $stmt) { if (($stmt =~ s/^(.{1,$text_margin})(?: |$)//) || ($stmt =~ s/^([\S]+)(?: |$)//)) { my $line = $1; $stmt_wrapped .= $stmt_wrapped ? $eol : $leading; $stmt_wrapped .= "$prefix$line"; } else { # Should be unreachable, but we don't want an infinite # loop if it can be reached. die; } } # Replace the old copyright statement. s/$stmt_re/$stmt_wrapped/; } } else { print STDERR "$ARGV: warning: FSF copyright statement not found\n"; } # Local variables: # indent-tabs-mode: nil # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "my $VERSION = '" # time-stamp-format: "%:y-%02m-%02d.%02H:%02M" # time-stamp-time-zone: "UTC" # time-stamp-end: "'; # UTC" # End: