|  | #!/usr/bin/env python3 | 
|  | # | 
|  | # Copyright (C) 2013-2021 Free Software Foundation, Inc. | 
|  | # | 
|  | # This script 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 script adjusts the copyright notices at the top of source files | 
|  | # so that they have the form: | 
|  | # | 
|  | #   Copyright XXXX-YYYY Free Software Foundation, Inc. | 
|  | # | 
|  | # It doesn't change code that is known to be maintained elsewhere or | 
|  | # that carries a non-FSF copyright. | 
|  | # | 
|  | # The script also doesn't change testsuite files, except those in | 
|  | # libstdc++-v3.  This is because libstdc++-v3 has a conformance testsuite, | 
|  | # while most tests in other directories are just things that failed at some | 
|  | # point in the past. | 
|  | # | 
|  | # Pass --this-year to the script if you want it to add the current year | 
|  | # to all applicable notices.  Pass --quilt if you are using quilt and | 
|  | # want files to be added to the quilt before being changed. | 
|  | # | 
|  | # By default the script will update all directories for which the | 
|  | # output has been vetted.  You can instead pass the names of individual | 
|  | # directories, including those that haven't been approved.  So: | 
|  | # | 
|  | #    update-copyright.py --this-year | 
|  | # | 
|  | # is the command that would be used at the beginning of a year to update | 
|  | # all copyright notices (and possibly at other times to check whether | 
|  | # new files have been added with old years).  On the other hand: | 
|  | # | 
|  | #    update-copyright.py --this-year libitm | 
|  | # | 
|  | # would run the script on just libitm/. | 
|  | # | 
|  | # Note that things like --version output strings must be updated before | 
|  | # this script is run.  There's already a separate procedure for that. | 
|  |  | 
|  | import os | 
|  | import re | 
|  | import sys | 
|  | import time | 
|  | import subprocess | 
|  |  | 
|  | class Errors: | 
|  | def __init__ (self): | 
|  | self.num_errors = 0 | 
|  |  | 
|  | def report (self, filename, string): | 
|  | if filename: | 
|  | string = filename + ': ' + string | 
|  | sys.stderr.write (string + '\n') | 
|  | self.num_errors += 1 | 
|  |  | 
|  | def ok (self): | 
|  | return self.num_errors == 0 | 
|  |  | 
|  | class GenericFilter: | 
|  | def __init__ (self): | 
|  | self.skip_files = set() | 
|  | self.skip_dirs = set() | 
|  | self.skip_extensions = set([ | 
|  | '.png', | 
|  | '.pyc', | 
|  | ]) | 
|  | self.fossilised_files = set() | 
|  | self.own_files = set() | 
|  |  | 
|  | self.skip_files |= set ([ | 
|  | # Skip licence files. | 
|  | 'COPYING', | 
|  | 'COPYING.LIB', | 
|  | 'COPYING3', | 
|  | 'COPYING3.LIB', | 
|  | 'LICENSE', | 
|  | 'LICENSE.txt', | 
|  | 'fdl.texi', | 
|  | 'gpl_v3.texi', | 
|  | 'fdl-1.3.xml', | 
|  | 'gpl-3.0.xml', | 
|  |  | 
|  | # Skip auto- and libtool-related files | 
|  | 'aclocal.m4', | 
|  | 'compile', | 
|  | 'config.guess', | 
|  | 'config.sub', | 
|  | 'depcomp', | 
|  | 'install-sh', | 
|  | 'libtool.m4', | 
|  | 'ltmain.sh', | 
|  | 'ltoptions.m4', | 
|  | 'ltsugar.m4', | 
|  | 'ltversion.m4', | 
|  | 'lt~obsolete.m4', | 
|  | 'missing', | 
|  | 'mkdep', | 
|  | 'mkinstalldirs', | 
|  | 'move-if-change', | 
|  | 'shlibpath.m4', | 
|  | 'symlink-tree', | 
|  | 'ylwrap', | 
|  |  | 
|  | # Skip FSF mission statement, etc. | 
|  | 'gnu.texi', | 
|  | 'funding.texi', | 
|  | 'appendix_free.xml', | 
|  |  | 
|  | # Skip imported texinfo files. | 
|  | 'texinfo.tex', | 
|  | ]) | 
|  |  | 
|  |  | 
|  | def get_line_filter (self, dir, filename): | 
|  | if filename.startswith ('ChangeLog'): | 
|  | # Ignore references to copyright in changelog entries. | 
|  | return re.compile ('\t') | 
|  |  | 
|  | return None | 
|  |  | 
|  | def skip_file (self, dir, filename): | 
|  | if filename in self.skip_files: | 
|  | return True | 
|  |  | 
|  | (base, extension) = os.path.splitext (os.path.join (dir, filename)) | 
|  | if extension in self.skip_extensions: | 
|  | return True | 
|  |  | 
|  | if extension == '.in': | 
|  | # Skip .in files produced by automake. | 
|  | if os.path.exists (base + '.am'): | 
|  | return True | 
|  |  | 
|  | # Skip files produced by autogen | 
|  | if (os.path.exists (base + '.def') | 
|  | and os.path.exists (base + '.tpl')): | 
|  | return True | 
|  |  | 
|  | # Skip configure files produced by autoconf | 
|  | if filename == 'configure': | 
|  | if os.path.exists (base + '.ac'): | 
|  | return True | 
|  | if os.path.exists (base + '.in'): | 
|  | return True | 
|  |  | 
|  | return False | 
|  |  | 
|  | def skip_dir (self, dir, subdir): | 
|  | return subdir in self.skip_dirs | 
|  |  | 
|  | def is_fossilised_file (self, dir, filename): | 
|  | if filename in self.fossilised_files: | 
|  | return True | 
|  | # Only touch current current ChangeLogs. | 
|  | if filename != 'ChangeLog' and filename.find ('ChangeLog') >= 0: | 
|  | return True | 
|  | return False | 
|  |  | 
|  | def by_package_author (self, dir, filename): | 
|  | return filename in self.own_files | 
|  |  | 
|  | class Copyright: | 
|  | def __init__ (self, errors): | 
|  | self.errors = errors | 
|  |  | 
|  | # Characters in a range of years.  Include '.' for typos. | 
|  | ranges = '[0-9](?:[-0-9.,\s]|\s+and\s+)*[0-9]' | 
|  |  | 
|  | # Non-whitespace characters in a copyright holder's name. | 
|  | name = '[\w.,-]' | 
|  |  | 
|  | # Matches one year. | 
|  | self.year_re = re.compile ('[0-9]+') | 
|  |  | 
|  | # Matches part of a year or copyright holder. | 
|  | self.continuation_re = re.compile (ranges + '|' + name) | 
|  |  | 
|  | # Matches a full copyright notice: | 
|  | self.copyright_re = re.compile ( | 
|  | # 1: 'Copyright (C)', etc. | 
|  | '([Cc]opyright' | 
|  | '|[Cc]opyright\s+\([Cc]\)' | 
|  | '|[Cc]opyright\s+%s' | 
|  | '|[Cc]opyright\s+©' | 
|  | '|[Cc]opyright\s+@copyright{}' | 
|  | '|copyright = u\'' | 
|  | '|@set\s+copyright[\w-]+)' | 
|  |  | 
|  | # 2: the years.  Include the whitespace in the year, so that | 
|  | # we can remove any excess. | 
|  | '(\s*(?:' + ranges + ',?' | 
|  | '|@value\{[^{}]*\})\s*)' | 
|  |  | 
|  | # 3: 'by ', if used | 
|  | '(by\s+)?' | 
|  |  | 
|  | # 4: the copyright holder.  Don't allow multiple consecutive | 
|  | # spaces, so that right-margin gloss doesn't get caught | 
|  | # (e.g. gnat_ugn.texi). | 
|  | '(' + name + '(?:\s?' + name + ')*)?') | 
|  |  | 
|  | # A regexp for notices that might have slipped by.  Just matching | 
|  | # 'copyright' is too noisy, and 'copyright.*[0-9]' falls foul of | 
|  | # HTML header markers, so check for 'copyright' and two digits. | 
|  | self.other_copyright_re = re.compile ('copyright.*[0-9][0-9]', | 
|  | re.IGNORECASE) | 
|  | self.comment_re = re.compile('#+|[*]+|;+|%+|//+|@c |dnl ') | 
|  | self.holders = { '@copying': '@copying' } | 
|  | self.holder_prefixes = set() | 
|  |  | 
|  | # True to 'quilt add' files before changing them. | 
|  | self.use_quilt = False | 
|  |  | 
|  | # If set, force all notices to include this year. | 
|  | self.max_year = None | 
|  |  | 
|  | # Goes after the year(s).  Could be ', '. | 
|  | self.separator = ' ' | 
|  |  | 
|  | def add_package_author (self, holder, canon_form = None): | 
|  | if not canon_form: | 
|  | canon_form = holder | 
|  | self.holders[holder] = canon_form | 
|  | index = holder.find (' ') | 
|  | while index >= 0: | 
|  | self.holder_prefixes.add (holder[:index]) | 
|  | index = holder.find (' ', index + 1) | 
|  |  | 
|  | def add_external_author (self, holder): | 
|  | self.holders[holder] = None | 
|  |  | 
|  | class BadYear (Exception): | 
|  | def __init__ (self, year): | 
|  | self.year = year | 
|  |  | 
|  | def __str__ (self): | 
|  | return 'unrecognised year: ' + self.year | 
|  |  | 
|  | def parse_year (self, string): | 
|  | year = int (string) | 
|  | if len (string) == 2: | 
|  | if year > 70: | 
|  | return year + 1900 | 
|  | elif len (string) == 4: | 
|  | return year | 
|  | raise self.BadYear (string) | 
|  |  | 
|  | def year_range (self, years): | 
|  | year_list = [self.parse_year (year) | 
|  | for year in self.year_re.findall (years)] | 
|  | assert len (year_list) > 0 | 
|  | return (min (year_list), max (year_list)) | 
|  |  | 
|  | def set_use_quilt (self, use_quilt): | 
|  | self.use_quilt = use_quilt | 
|  |  | 
|  | def include_year (self, year): | 
|  | assert not self.max_year | 
|  | self.max_year = year | 
|  |  | 
|  | def canonicalise_years (self, dir, filename, filter, years): | 
|  | # Leave texinfo variables alone. | 
|  | if years.startswith ('@value'): | 
|  | return years | 
|  |  | 
|  | (min_year, max_year) = self.year_range (years) | 
|  |  | 
|  | # Update the upper bound, if enabled. | 
|  | if self.max_year and not filter.is_fossilised_file (dir, filename): | 
|  | max_year = max (max_year, self.max_year) | 
|  |  | 
|  | # Use a range. | 
|  | if min_year == max_year: | 
|  | return '%d' % min_year | 
|  | else: | 
|  | return '%d-%d' % (min_year, max_year) | 
|  |  | 
|  | def strip_continuation (self, line): | 
|  | line = line.lstrip() | 
|  | match = self.comment_re.match (line) | 
|  | if match: | 
|  | line = line[match.end():].lstrip() | 
|  | return line | 
|  |  | 
|  | def is_complete (self, match): | 
|  | holder = match.group (4) | 
|  | return (holder | 
|  | and (holder not in self.holder_prefixes | 
|  | or holder in self.holders)) | 
|  |  | 
|  | def update_copyright (self, dir, filename, filter, file, line, match): | 
|  | orig_line = line | 
|  | next_line = None | 
|  | pathname = os.path.join (dir, filename) | 
|  |  | 
|  | intro = match.group (1) | 
|  | if intro.startswith ('@set'): | 
|  | # Texinfo year variables should always be on one line | 
|  | after_years = line[match.end (2):].strip() | 
|  | if after_years != '': | 
|  | self.errors.report (pathname, | 
|  | 'trailing characters in @set: ' | 
|  | + after_years) | 
|  | return (False, orig_line, next_line) | 
|  | else: | 
|  | # If it looks like the copyright is incomplete, add the next line. | 
|  | while not self.is_complete (match): | 
|  | try: | 
|  | next_line = file.readline() | 
|  | except StopIteration: | 
|  | break | 
|  |  | 
|  | # If the next line doesn't look like a proper continuation, | 
|  | # assume that what we've got is complete. | 
|  | continuation = self.strip_continuation (next_line) | 
|  | if not self.continuation_re.match (continuation): | 
|  | break | 
|  |  | 
|  | # Merge the lines for matching purposes. | 
|  | orig_line += next_line | 
|  | line = line.rstrip() + ' ' + continuation | 
|  | next_line = None | 
|  |  | 
|  | # Rematch with the longer line, at the original position. | 
|  | match = self.copyright_re.match (line, match.start()) | 
|  | assert match | 
|  |  | 
|  | holder = match.group (4) | 
|  |  | 
|  | # Use the filter to test cases where markup is getting in the way. | 
|  | if filter.by_package_author (dir, filename): | 
|  | assert holder not in self.holders | 
|  |  | 
|  | elif not holder: | 
|  | self.errors.report (pathname, 'missing copyright holder') | 
|  | return (False, orig_line, next_line) | 
|  |  | 
|  | elif holder not in self.holders: | 
|  | self.errors.report (pathname, | 
|  | 'unrecognised copyright holder: ' + holder) | 
|  | return (False, orig_line, next_line) | 
|  |  | 
|  | else: | 
|  | # See whether the copyright is associated with the package | 
|  | # author. | 
|  | canon_form = self.holders[holder] | 
|  | if not canon_form: | 
|  | return (False, orig_line, next_line) | 
|  |  | 
|  | # Make sure the author is given in a consistent way. | 
|  | line = (line[:match.start (4)] | 
|  | + canon_form | 
|  | + line[match.end (4):]) | 
|  |  | 
|  | # Remove any 'by' | 
|  | line = line[:match.start (3)] + line[match.end (3):] | 
|  |  | 
|  | # Update the copyright years. | 
|  | years = match.group (2).strip() | 
|  | try: | 
|  | canon_form = self.canonicalise_years (dir, filename, filter, years) | 
|  | except self.BadYear as e: | 
|  | self.errors.report (pathname, str (e)) | 
|  | return (False, orig_line, next_line) | 
|  |  | 
|  | line = (line[:match.start (2)] | 
|  | + ('' if intro.startswith ('copyright = ') else ' ') | 
|  | + canon_form + self.separator | 
|  | + line[match.end (2):]) | 
|  |  | 
|  | # Use the standard (C) form. | 
|  | if intro.endswith ('right'): | 
|  | intro += ' (C)' | 
|  | elif intro.endswith ('(c)'): | 
|  | intro = intro[:-3] + '(C)' | 
|  | line = line[:match.start (1)] + intro + line[match.end (1):] | 
|  |  | 
|  | # Strip trailing whitespace | 
|  | line = line.rstrip() + '\n' | 
|  |  | 
|  | return (line != orig_line, line, next_line) | 
|  |  | 
|  | def guess_encoding (self, pathname): | 
|  | for encoding in ('utf8', 'iso8859'): | 
|  | try: | 
|  | open(pathname, 'r', encoding=encoding).read() | 
|  | return encoding | 
|  | except UnicodeDecodeError: | 
|  | pass | 
|  | return None | 
|  |  | 
|  | def process_file (self, dir, filename, filter): | 
|  | pathname = os.path.join (dir, filename) | 
|  | if filename.endswith ('.tmp'): | 
|  | # Looks like something we tried to create before. | 
|  | try: | 
|  | os.remove (pathname) | 
|  | except OSError: | 
|  | pass | 
|  | return | 
|  |  | 
|  | lines = [] | 
|  | changed = False | 
|  | line_filter = filter.get_line_filter (dir, filename) | 
|  | mode = None | 
|  | encoding = self.guess_encoding(pathname) | 
|  | with open (pathname, 'r', encoding=encoding) as file: | 
|  | prev = None | 
|  | mode = os.fstat (file.fileno()).st_mode | 
|  | for line in file: | 
|  | while line: | 
|  | next_line = None | 
|  | # Leave filtered-out lines alone. | 
|  | if not (line_filter and line_filter.match (line)): | 
|  | match = self.copyright_re.search (line) | 
|  | if match: | 
|  | res = self.update_copyright (dir, filename, filter, | 
|  | file, line, match) | 
|  | (this_changed, line, next_line) = res | 
|  | changed = changed or this_changed | 
|  |  | 
|  | # Check for copyright lines that might have slipped by. | 
|  | elif self.other_copyright_re.search (line): | 
|  | self.errors.report (pathname, | 
|  | 'unrecognised copyright: %s' | 
|  | % line.strip()) | 
|  | lines.append (line) | 
|  | line = next_line | 
|  |  | 
|  | # If something changed, write the new file out. | 
|  | if changed and self.errors.ok(): | 
|  | tmp_pathname = pathname + '.tmp' | 
|  | with open (tmp_pathname, 'w', encoding=encoding) as file: | 
|  | for line in lines: | 
|  | file.write (line) | 
|  | os.fchmod (file.fileno(), mode) | 
|  | if self.use_quilt: | 
|  | subprocess.call (['quilt', 'add', pathname]) | 
|  | os.rename (tmp_pathname, pathname) | 
|  |  | 
|  | def process_tree (self, tree, filter): | 
|  | for (dir, subdirs, filenames) in os.walk (tree): | 
|  | # Don't recurse through directories that should be skipped. | 
|  | for i in range (len (subdirs) - 1, -1, -1): | 
|  | if filter.skip_dir (dir, subdirs[i]): | 
|  | del subdirs[i] | 
|  |  | 
|  | # Handle the files in this directory. | 
|  | for filename in filenames: | 
|  | if filter.skip_file (dir, filename): | 
|  | sys.stdout.write ('Skipping %s\n' | 
|  | % os.path.join (dir, filename)) | 
|  | else: | 
|  | self.process_file (dir, filename, filter) | 
|  |  | 
|  | class CmdLine: | 
|  | def __init__ (self, copyright = Copyright): | 
|  | self.errors = Errors() | 
|  | self.copyright = copyright (self.errors) | 
|  | self.dirs = [] | 
|  | self.default_dirs = [] | 
|  | self.chosen_dirs = [] | 
|  | self.option_handlers = dict() | 
|  | self.option_help = [] | 
|  |  | 
|  | self.add_option ('--help', 'Print this help', self.o_help) | 
|  | self.add_option ('--quilt', '"quilt add" files before changing them', | 
|  | self.o_quilt) | 
|  | self.add_option ('--this-year', 'Add the current year to every notice', | 
|  | self.o_this_year) | 
|  |  | 
|  | def add_option (self, name, help, handler): | 
|  | self.option_help.append ((name, help)) | 
|  | self.option_handlers[name] = handler | 
|  |  | 
|  | def add_dir (self, dir, filter = GenericFilter()): | 
|  | self.dirs.append ((dir, filter)) | 
|  |  | 
|  | def o_help (self, option = None): | 
|  | sys.stdout.write ('Usage: %s [options] dir1 dir2...\n\n' | 
|  | 'Options:\n' % sys.argv[0]) | 
|  | format = '%-15s %s\n' | 
|  | for (what, help) in self.option_help: | 
|  | sys.stdout.write (format % (what, help)) | 
|  | sys.stdout.write ('\nDirectories:\n') | 
|  |  | 
|  | format = '%-25s' | 
|  | i = 0 | 
|  | for (dir, filter) in self.dirs: | 
|  | i += 1 | 
|  | if i % 3 == 0 or i == len (self.dirs): | 
|  | sys.stdout.write (dir + '\n') | 
|  | else: | 
|  | sys.stdout.write (format % dir) | 
|  | sys.exit (0) | 
|  |  | 
|  | def o_quilt (self, option): | 
|  | self.copyright.set_use_quilt (True) | 
|  |  | 
|  | def o_this_year (self, option): | 
|  | self.copyright.include_year (time.localtime().tm_year) | 
|  |  | 
|  | def main (self): | 
|  | for arg in sys.argv[1:]: | 
|  | if arg[:1] != '-': | 
|  | self.chosen_dirs.append (arg) | 
|  | elif arg in self.option_handlers: | 
|  | self.option_handlers[arg] (arg) | 
|  | else: | 
|  | self.errors.report (None, 'unrecognised option: ' + arg) | 
|  | if self.errors.ok(): | 
|  | if len (self.chosen_dirs) == 0: | 
|  | self.chosen_dirs = self.default_dirs | 
|  | if len (self.chosen_dirs) == 0: | 
|  | self.o_help() | 
|  | else: | 
|  | for chosen_dir in self.chosen_dirs: | 
|  | canon_dir = os.path.join (chosen_dir, '') | 
|  | count = 0 | 
|  | for (dir, filter) in self.dirs: | 
|  | if (dir + os.sep).startswith (canon_dir): | 
|  | count += 1 | 
|  | self.copyright.process_tree (dir, filter) | 
|  | if count == 0: | 
|  | self.errors.report (None, 'unrecognised directory: ' | 
|  | + chosen_dir) | 
|  | sys.exit (0 if self.errors.ok() else 1) | 
|  |  | 
|  | #---------------------------------------------------------------------------- | 
|  |  | 
|  | class TopLevelFilter (GenericFilter): | 
|  | def skip_dir (self, dir, subdir): | 
|  | return True | 
|  |  | 
|  | class ConfigFilter (GenericFilter): | 
|  | def __init__ (self): | 
|  | GenericFilter.__init__ (self) | 
|  |  | 
|  | def skip_file (self, dir, filename): | 
|  | if filename.endswith ('.m4'): | 
|  | pathname = os.path.join (dir, filename) | 
|  | with open (pathname) as file: | 
|  | # Skip files imported from gettext. | 
|  | if file.readline().find ('gettext-') >= 0: | 
|  | return True | 
|  | return GenericFilter.skip_file (self, dir, filename) | 
|  |  | 
|  | class GCCFilter (GenericFilter): | 
|  | def __init__ (self): | 
|  | GenericFilter.__init__ (self) | 
|  |  | 
|  | self.skip_files |= set ([ | 
|  | # Not part of GCC | 
|  | 'math-68881.h', | 
|  | ]) | 
|  |  | 
|  | self.skip_dirs |= set ([ | 
|  | # Better not create a merge nightmare for the GNAT folks. | 
|  | 'ada', | 
|  |  | 
|  | # Handled separately. | 
|  | 'testsuite', | 
|  | ]) | 
|  |  | 
|  | self.skip_extensions |= set ([ | 
|  | # Maintained by the translation project. | 
|  | '.po', | 
|  |  | 
|  | # Automatically-generated. | 
|  | '.pot', | 
|  | ]) | 
|  |  | 
|  | self.fossilised_files |= set ([ | 
|  | # Old news won't be updated. | 
|  | 'ONEWS', | 
|  | ]) | 
|  |  | 
|  | class TestsuiteFilter (GenericFilter): | 
|  | def __init__ (self): | 
|  | GenericFilter.__init__ (self) | 
|  |  | 
|  | self.skip_extensions |= set ([ | 
|  | # Don't change the tests, which could be woend by anyone. | 
|  | '.c', | 
|  | '.C', | 
|  | '.cc', | 
|  | '.d', | 
|  | '.h', | 
|  | '.hs', | 
|  | '.f', | 
|  | '.f90', | 
|  | '.go', | 
|  | '.inc', | 
|  | '.java', | 
|  | ]) | 
|  |  | 
|  | def skip_file (self, dir, filename): | 
|  | # g++.niklas/README contains historical copyright information | 
|  | # and isn't updated. | 
|  | if filename == 'README' and os.path.basename (dir) == 'g++.niklas': | 
|  | return True | 
|  | # Similarly params/README. | 
|  | if filename == 'README' and os.path.basename (dir) == 'params': | 
|  | return True | 
|  | if filename == 'pdt_5.f03' and os.path.basename (dir) == 'gfortran.dg': | 
|  | return True | 
|  | return GenericFilter.skip_file (self, dir, filename) | 
|  |  | 
|  | class LibCppFilter (GenericFilter): | 
|  | def __init__ (self): | 
|  | GenericFilter.__init__ (self) | 
|  |  | 
|  | self.skip_extensions |= set ([ | 
|  | # Maintained by the translation project. | 
|  | '.po', | 
|  |  | 
|  | # Automatically-generated. | 
|  | '.pot', | 
|  | ]) | 
|  |  | 
|  | class LibGCCFilter (GenericFilter): | 
|  | def __init__ (self): | 
|  | GenericFilter.__init__ (self) | 
|  |  | 
|  | self.skip_dirs |= set ([ | 
|  | # Imported from GLIBC. | 
|  | 'soft-fp', | 
|  | ]) | 
|  |  | 
|  | class LibPhobosFilter (GenericFilter): | 
|  | def __init__ (self): | 
|  | GenericFilter.__init__ (self) | 
|  |  | 
|  | self.skip_files |= set ([ | 
|  | # Source module imported from upstream. | 
|  | 'object.d', | 
|  | ]) | 
|  |  | 
|  | self.skip_dirs |= set ([ | 
|  | # Contains sources imported from upstream. | 
|  | 'core', | 
|  | 'etc', | 
|  | 'gc', | 
|  | 'gcstub', | 
|  | 'rt', | 
|  | 'std', | 
|  | ]) | 
|  |  | 
|  | class LibStdCxxFilter (GenericFilter): | 
|  | def __init__ (self): | 
|  | GenericFilter.__init__ (self) | 
|  |  | 
|  | self.skip_files |= set ([ | 
|  | # Contains no copyright of its own, but quotes the GPL. | 
|  | 'intro.xml', | 
|  | ]) | 
|  |  | 
|  | self.skip_dirs |= set ([ | 
|  | # Contains automatically-generated sources. | 
|  | 'html', | 
|  |  | 
|  | # The testsuite data files shouldn't be changed. | 
|  | 'data', | 
|  |  | 
|  | # Contains imported images | 
|  | 'images', | 
|  | ]) | 
|  |  | 
|  | self.own_files |= set ([ | 
|  | # Contains markup around the copyright owner. | 
|  | 'spine.xml', | 
|  | ]) | 
|  |  | 
|  | def get_line_filter (self, dir, filename): | 
|  | if filename == 'boost_concept_check.h': | 
|  | return re.compile ('// \(C\) Copyright Jeremy Siek') | 
|  | return GenericFilter.get_line_filter (self, dir, filename) | 
|  |  | 
|  | class GCCCopyright (Copyright): | 
|  | def __init__ (self, errors): | 
|  | Copyright.__init__ (self, errors) | 
|  |  | 
|  | canon_fsf = 'Free Software Foundation, Inc.' | 
|  | self.add_package_author ('Free Software Foundation', canon_fsf) | 
|  | self.add_package_author ('Free Software Foundation.', canon_fsf) | 
|  | self.add_package_author ('Free Software Foundation Inc.', canon_fsf) | 
|  | self.add_package_author ('Free Software Foundation, Inc', canon_fsf) | 
|  | self.add_package_author ('Free Software Foundation, Inc.', canon_fsf) | 
|  | self.add_package_author ('The Free Software Foundation', canon_fsf) | 
|  | self.add_package_author ('The Free Software Foundation, Inc.', canon_fsf) | 
|  | self.add_package_author ('Software Foundation, Inc.', canon_fsf) | 
|  |  | 
|  | self.add_external_author ('ARM') | 
|  | self.add_external_author ('AdaCore') | 
|  | self.add_external_author ('Advanced Micro Devices Inc.') | 
|  | self.add_external_author ('Ami Tavory and Vladimir Dreizin, IBM-HRL.') | 
|  | self.add_external_author ('Cavium Networks.') | 
|  | self.add_external_author ('Faraday Technology Corp.') | 
|  | self.add_external_author ('Florida State University') | 
|  | self.add_external_author ('Gerard Jungman') | 
|  | self.add_external_author ('Greg Colvin and Beman Dawes.') | 
|  | self.add_external_author ('Hewlett-Packard Company') | 
|  | self.add_external_author ('Intel Corporation') | 
|  | self.add_external_author ('Information Technology Industry Council.') | 
|  | self.add_external_author ('James Theiler, Brian Gough') | 
|  | self.add_external_author ('Makoto Matsumoto and Takuji Nishimura,') | 
|  | self.add_external_author ('Mentor Graphics Corporation') | 
|  | self.add_external_author ('National Research Council of Canada.') | 
|  | self.add_external_author ('NVIDIA Corporation') | 
|  | self.add_external_author ('Peter Dimov and Multi Media Ltd.') | 
|  | self.add_external_author ('Peter Dimov') | 
|  | self.add_external_author ('Pipeline Associates, Inc.') | 
|  | self.add_external_author ('Regents of the University of California.') | 
|  | self.add_external_author ('Silicon Graphics Computer Systems, Inc.') | 
|  | self.add_external_author ('Silicon Graphics') | 
|  | self.add_external_author ('Stephen L. Moshier') | 
|  | self.add_external_author ('Sun Microsystems, Inc. All rights reserved.') | 
|  | self.add_external_author ('The D Language Foundation, All Rights Reserved') | 
|  | self.add_external_author ('The Go Authors.  All rights reserved.') | 
|  | self.add_external_author ('The Go Authors. All rights reserved.') | 
|  | self.add_external_author ('The Go Authors.') | 
|  | self.add_external_author ('The Regents of the University of California.') | 
|  | self.add_external_author ('Ulf Adams') | 
|  | self.add_external_author ('Unicode, Inc.') | 
|  | self.add_external_author ('University of Toronto.') | 
|  | self.add_external_author ('Yoshinori Sato') | 
|  |  | 
|  | class GCCCmdLine (CmdLine): | 
|  | def __init__ (self): | 
|  | CmdLine.__init__ (self, GCCCopyright) | 
|  |  | 
|  | self.add_dir ('.', TopLevelFilter()) | 
|  | # boehm-gc is imported from upstream. | 
|  | self.add_dir ('c++tools') | 
|  | self.add_dir ('config', ConfigFilter()) | 
|  | # contrib isn't really part of GCC. | 
|  | self.add_dir ('fixincludes') | 
|  | self.add_dir ('gcc', GCCFilter()) | 
|  | self.add_dir (os.path.join ('gcc', 'testsuite'), TestsuiteFilter()) | 
|  | self.add_dir ('gnattools') | 
|  | self.add_dir ('gotools') | 
|  | self.add_dir ('include') | 
|  | # intl is imported from upstream. | 
|  | self.add_dir ('libada') | 
|  | self.add_dir ('libatomic') | 
|  | self.add_dir ('libbacktrace') | 
|  | self.add_dir ('libcc1') | 
|  | self.add_dir ('libcpp', LibCppFilter()) | 
|  | self.add_dir ('libdecnumber') | 
|  | # libffi is imported from upstream. | 
|  | self.add_dir ('libgcc', LibGCCFilter()) | 
|  | self.add_dir ('libgfortran') | 
|  | # libgo is imported from upstream. | 
|  | self.add_dir ('libgomp') | 
|  | self.add_dir ('libiberty') | 
|  | self.add_dir ('libitm') | 
|  | self.add_dir ('libobjc') | 
|  | # liboffloadmic is imported from upstream. | 
|  | self.add_dir ('libphobos', LibPhobosFilter()) | 
|  | self.add_dir ('libquadmath') | 
|  | # libsanitizer is imported from upstream. | 
|  | self.add_dir ('libssp') | 
|  | self.add_dir ('libstdc++-v3', LibStdCxxFilter()) | 
|  | self.add_dir ('libvtv') | 
|  | self.add_dir ('lto-plugin') | 
|  | # maintainer-scripts maintainer-scripts | 
|  | # zlib is imported from upstream. | 
|  |  | 
|  | self.default_dirs = [ | 
|  | 'c++tools', | 
|  | 'gcc', | 
|  | 'include', | 
|  | 'libada', | 
|  | 'libatomic', | 
|  | 'libbacktrace', | 
|  | 'libcc1', | 
|  | 'libcpp', | 
|  | 'libdecnumber', | 
|  | 'libgcc', | 
|  | 'libgfortran', | 
|  | 'libgomp', | 
|  | 'libiberty', | 
|  | 'libitm', | 
|  | 'libobjc', | 
|  | 'libphobos', | 
|  | 'libssp', | 
|  | 'libstdc++-v3', | 
|  | 'libvtv', | 
|  | 'lto-plugin', | 
|  | ] | 
|  |  | 
|  | GCCCmdLine().main() |