Home | History | Annotate | Line # | Download | only in contrib
update-copyright.py revision 1.1.1.11
      1 #!/usr/bin/env python3
      2 #
      3 # Copyright (C) 2013-2024 Free Software Foundation, Inc.
      4 #
      5 # This script is free software; you can redistribute it and/or modify
      6 # it under the terms of the GNU General Public License as published by
      7 # the Free Software Foundation; either version 3, or (at your option)
      8 # any later version.
      9 
     10 # This script adjusts the copyright notices at the top of source files
     11 # so that they have the form:
     12 #
     13 #   Copyright XXXX-YYYY Free Software Foundation, Inc.
     14 #
     15 # It doesn't change code that is known to be maintained elsewhere or
     16 # that carries a non-FSF copyright.
     17 #
     18 # The script also doesn't change testsuite files, except those in
     19 # libstdc++-v3.  This is because libstdc++-v3 has a conformance testsuite,
     20 # while most tests in other directories are just things that failed at some
     21 # point in the past.
     22 #
     23 # Pass --this-year to the script if you want it to add the current year
     24 # to all applicable notices.  Pass --quilt if you are using quilt and
     25 # want files to be added to the quilt before being changed.
     26 #
     27 # By default the script will update all directories for which the
     28 # output has been vetted.  You can instead pass the names of individual
     29 # directories, including those that haven't been approved.  So:
     30 #
     31 #    update-copyright.py --this-year
     32 #
     33 # is the command that would be used at the beginning of a year to update
     34 # all copyright notices (and possibly at other times to check whether
     35 # new files have been added with old years).  On the other hand:
     36 #
     37 #    update-copyright.py --this-year libitm
     38 #
     39 # would run the script on just libitm/.
     40 #
     41 # Note that things like --version output strings must be updated before
     42 # this script is run.  There's already a separate procedure for that.
     43 
     44 import os
     45 import re
     46 import sys
     47 import time
     48 import subprocess
     49 
     50 class Errors:
     51     def __init__ (self):
     52         self.num_errors = 0
     53 
     54     def report (self, filename, string):
     55         if filename:
     56             string = filename + ': ' + string
     57         sys.stderr.write (string + '\n')
     58         self.num_errors += 1
     59 
     60     def ok (self):
     61         return self.num_errors == 0
     62 
     63 class GenericFilter:
     64     def __init__ (self):
     65         self.skip_files = set()
     66         self.skip_dirs = set()
     67         self.skip_extensions = set([
     68                 '.png',
     69                 '.pyc',
     70                 ])
     71         self.fossilised_files = set()
     72         self.own_files = set()
     73 
     74         self.skip_files |= set ([
     75                 # Skip licence files.
     76                 'COPYING',
     77                 'COPYING.LIB',
     78                 'COPYING3',
     79                 'COPYING3.LIB',
     80                 'LICENSE',
     81                 'LICENSE.txt',
     82                 'fdl.texi',
     83                 'gpl_v3.texi',
     84                 'fdl-1.3.xml',
     85                 'gpl-3.0.xml',
     86                 'gpl_v3_without_node.texi',
     87 
     88                 # Skip auto- and libtool-related files
     89                 'aclocal.m4',
     90                 'compile',
     91                 'config.guess',
     92                 'config.sub',
     93                 'depcomp',
     94                 'install-sh',
     95                 'libtool.m4',
     96                 'ltmain.sh',
     97                 'ltoptions.m4',
     98                 'ltsugar.m4',
     99                 'ltversion.m4',
    100                 'lt~obsolete.m4',
    101                 'missing',
    102                 'mkdep',
    103                 'mkinstalldirs',
    104                 'move-if-change',
    105                 'shlibpath.m4',
    106                 'symlink-tree',
    107                 'ylwrap',
    108 
    109                 # Skip FSF mission statement, etc.
    110                 'gnu.texi',
    111                 'funding.texi',
    112                 'appendix_free.xml',
    113 
    114                 # Skip imported texinfo files.
    115                 'texinfo.tex',
    116                 ])
    117 
    118 
    119     def get_line_filter (self, dir, filename):
    120         if filename.startswith ('ChangeLog'):
    121             # Ignore references to copyright in changelog entries.
    122             return re.compile ('\t')
    123 
    124         return None
    125 
    126     def skip_file (self, dir, filename):
    127         if filename in self.skip_files:
    128             return True
    129 
    130         (base, extension) = os.path.splitext (os.path.join (dir, filename))
    131         if extension in self.skip_extensions:
    132             return True
    133 
    134         if extension == '.in':
    135             # Skip .in files produced by automake.
    136             if os.path.exists (base + '.am'):
    137                 return True
    138 
    139             # Skip files produced by autogen
    140             if (os.path.exists (base + '.def')
    141                 and os.path.exists (base + '.tpl')):
    142                 return True
    143 
    144         # Skip configure files produced by autoconf
    145         if filename == 'configure':
    146             if os.path.exists (base + '.ac'):
    147                 return True
    148             if os.path.exists (base + '.in'):
    149                 return True
    150 
    151         return False
    152 
    153     def skip_dir (self, dir, subdir):
    154         return subdir in self.skip_dirs
    155 
    156     def is_fossilised_file (self, dir, filename):
    157         if filename in self.fossilised_files:
    158             return True
    159         # Only touch current current ChangeLogs.
    160         if filename != 'ChangeLog' and filename.find ('ChangeLog') >= 0:
    161             return True
    162         return False
    163 
    164     def by_package_author (self, dir, filename):
    165         return filename in self.own_files
    166 
    167 class Copyright:
    168     def __init__ (self, errors):
    169         self.errors = errors
    170 
    171         # Characters in a range of years.  Include '.' for typos.
    172         ranges = '[0-9](?:[-0-9.,\s]|\s+and\s+)*[0-9]'
    173 
    174         # Non-whitespace characters in a copyright holder's name.
    175         name = '[\w.,-]'
    176 
    177         # Matches one year.
    178         self.year_re = re.compile ('[0-9]+')
    179 
    180         # Matches part of a year or copyright holder.
    181         self.continuation_re = re.compile (ranges + '|' + name)
    182 
    183         # Matches a full copyright notice:
    184         self.copyright_re = re.compile (
    185             # 1: 'Copyright (C)', etc.
    186             '([Cc]opyright'
    187             '|[Cc]opyright\s+\([Cc]\)'
    188             '|[Cc]opyright\s+%s'
    189             '|[Cc]opyright\s+©'
    190             '|[Cc]opyright\s+@copyright{}'
    191             '|copyright = u\''
    192             '|@set\s+copyright[\w-]+)'
    193 
    194             # 2: the years.  Include the whitespace in the year, so that
    195             # we can remove any excess.
    196             '(\s*(?:' + ranges + ',?'
    197             '|@value\{[^{}]*\})\s*)'
    198 
    199             # 3: 'by ', if used
    200             '(by\s+)?'
    201 
    202             # 4: the copyright holder.  Don't allow multiple consecutive
    203             # spaces, so that right-margin gloss doesn't get caught
    204             # (e.g. gnat_ugn.texi).
    205             '(' + name + '(?:\s?' + name + ')*)?')
    206 
    207         # A regexp for notices that might have slipped by.  Just matching
    208         # 'copyright' is too noisy, and 'copyright.*[0-9]' falls foul of
    209         # HTML header markers, so check for 'copyright' and two digits.
    210         self.other_copyright_re = re.compile ('copyright.*[0-9][0-9]',
    211                                               re.IGNORECASE)
    212         self.comment_re = re.compile('#+|[*]+|;+|%+|//+|@c |dnl ')
    213         self.holders = { '@copying': '@copying' }
    214         self.holder_prefixes = set()
    215 
    216         # True to 'quilt add' files before changing them.
    217         self.use_quilt = False
    218 
    219         # If set, force all notices to include this year.
    220         self.max_year = None
    221 
    222         # Goes after the year(s).  Could be ', '.
    223         self.separator = ' '
    224 
    225     def add_package_author (self, holder, canon_form = None):
    226         if not canon_form:
    227             canon_form = holder
    228         self.holders[holder] = canon_form
    229         index = holder.find (' ')
    230         while index >= 0:
    231             self.holder_prefixes.add (holder[:index])
    232             index = holder.find (' ', index + 1)
    233 
    234     def add_external_author (self, holder):
    235         self.holders[holder] = None
    236 
    237     class BadYear (Exception):
    238         def __init__ (self, year):
    239             self.year = year
    240 
    241         def __str__ (self):
    242             return 'unrecognised year: ' + self.year
    243 
    244     def parse_year (self, string):
    245         year = int (string)
    246         if len (string) == 2:
    247             if year > 70:
    248                 return year + 1900
    249         elif len (string) == 4:
    250             return year
    251         raise self.BadYear (string)
    252 
    253     def year_range (self, years):
    254         year_list = [self.parse_year (year)
    255                      for year in self.year_re.findall (years)]
    256         assert len (year_list) > 0
    257         return (min (year_list), max (year_list))
    258 
    259     def set_use_quilt (self, use_quilt):
    260         self.use_quilt = use_quilt
    261 
    262     def include_year (self, year):
    263         assert not self.max_year
    264         self.max_year = year
    265 
    266     def canonicalise_years (self, dir, filename, filter, years):
    267         # Leave texinfo variables alone.
    268         if years.startswith ('@value'):
    269             return years
    270 
    271         (min_year, max_year) = self.year_range (years)
    272 
    273         # Update the upper bound, if enabled.
    274         if self.max_year and not filter.is_fossilised_file (dir, filename):
    275             max_year = max (max_year, self.max_year)
    276 
    277         # Use a range.
    278         if min_year == max_year:
    279             return '%d' % min_year
    280         else:
    281             return '%d-%d' % (min_year, max_year)
    282 
    283     def strip_continuation (self, line):
    284         line = line.lstrip()
    285         match = self.comment_re.match (line)
    286         if match:
    287             line = line[match.end():].lstrip()
    288         return line
    289 
    290     def is_complete (self, match):
    291         holder = match.group (4)
    292         return (holder
    293                 and (holder not in self.holder_prefixes
    294                      or holder in self.holders))
    295 
    296     def update_copyright (self, dir, filename, filter, file, line, match):
    297         orig_line = line
    298         next_line = None
    299         pathname = os.path.join (dir, filename)
    300 
    301         intro = match.group (1)
    302         if intro.startswith ('@set'):
    303             # Texinfo year variables should always be on one line
    304             after_years = line[match.end (2):].strip()
    305             if after_years != '':
    306                 self.errors.report (pathname,
    307                                     'trailing characters in @set: '
    308                                     + after_years)
    309                 return (False, orig_line, next_line)
    310         else:
    311             # If it looks like the copyright is incomplete, add the next line.
    312             while not self.is_complete (match):
    313                 try:
    314                     next_line = file.readline()
    315                 except StopIteration:
    316                     break
    317 
    318                 # If the next line doesn't look like a proper continuation,
    319                 # assume that what we've got is complete.
    320                 continuation = self.strip_continuation (next_line)
    321                 if not self.continuation_re.match (continuation):
    322                     break
    323 
    324                 # Merge the lines for matching purposes.
    325                 orig_line += next_line
    326                 line = line.rstrip() + ' ' + continuation
    327                 next_line = None
    328 
    329                 # Rematch with the longer line, at the original position.
    330                 match = self.copyright_re.match (line, match.start())
    331                 assert match
    332 
    333             holder = match.group (4)
    334 
    335             # Use the filter to test cases where markup is getting in the way.
    336             if filter.by_package_author (dir, filename):
    337                 assert holder not in self.holders
    338 
    339             elif not holder:
    340                 self.errors.report (pathname, 'missing copyright holder')
    341                 return (False, orig_line, next_line)
    342 
    343             elif holder not in self.holders:
    344                 self.errors.report (pathname,
    345                                     'unrecognised copyright holder: ' + holder)
    346                 return (False, orig_line, next_line)
    347 
    348             else:
    349                 # See whether the copyright is associated with the package
    350                 # author.
    351                 canon_form = self.holders[holder]
    352                 if not canon_form:
    353                     return (False, orig_line, next_line)
    354 
    355                 # Make sure the author is given in a consistent way.
    356                 line = (line[:match.start (4)]
    357                         + canon_form
    358                         + line[match.end (4):])
    359 
    360                 # Remove any 'by'
    361                 line = line[:match.start (3)] + line[match.end (3):]
    362 
    363         # Update the copyright years.
    364         years = match.group (2).strip()
    365         try:
    366             canon_form = self.canonicalise_years (dir, filename, filter, years)
    367         except self.BadYear as e:
    368             self.errors.report (pathname, str (e))
    369             return (False, orig_line, next_line)
    370 
    371         line = (line[:match.start (2)]
    372                 + ('' if intro.startswith ('copyright = ') else ' ')
    373                 + canon_form + self.separator
    374                 + line[match.end (2):])
    375 
    376         # Use the standard (C) form.
    377         if intro.endswith ('right'):
    378             intro += ' (C)'
    379         elif intro.endswith ('(c)'):
    380             intro = intro[:-3] + '(C)'
    381         line = line[:match.start (1)] + intro + line[match.end (1):]
    382 
    383         # Strip trailing whitespace
    384         line = line.rstrip() + '\n'
    385 
    386         return (line != orig_line, line, next_line)
    387 
    388     def guess_encoding (self, pathname):
    389         for encoding in ('utf8', 'iso8859'):
    390             try:
    391                 open(pathname, 'r', encoding=encoding).read()
    392                 return encoding
    393             except UnicodeDecodeError:
    394                 pass
    395         return None
    396 
    397     def process_file (self, dir, filename, filter):
    398         pathname = os.path.join (dir, filename)
    399         if filename.endswith ('.tmp'):
    400             # Looks like something we tried to create before.
    401             try:
    402                 os.remove (pathname)
    403             except OSError:
    404                 pass
    405             return
    406 
    407         lines = []
    408         changed = False
    409         line_filter = filter.get_line_filter (dir, filename)
    410         mode = None
    411         encoding = self.guess_encoding(pathname)
    412         with open (pathname, 'r', encoding=encoding) as file:
    413             prev = None
    414             mode = os.fstat (file.fileno()).st_mode
    415             for line in file:
    416                 while line:
    417                     next_line = None
    418                     # Leave filtered-out lines alone.
    419                     if not (line_filter and line_filter.match (line)):
    420                         match = self.copyright_re.search (line)
    421                         if match:
    422                             res = self.update_copyright (dir, filename, filter,
    423                                                          file, line, match)
    424                             (this_changed, line, next_line) = res
    425                             changed = changed or this_changed
    426 
    427                         # Check for copyright lines that might have slipped by.
    428                         elif self.other_copyright_re.search (line):
    429                             self.errors.report (pathname,
    430                                                 'unrecognised copyright: %s'
    431                                                 % line.strip())
    432                     lines.append (line)
    433                     line = next_line
    434 
    435         # If something changed, write the new file out.
    436         if changed and self.errors.ok():
    437             tmp_pathname = pathname + '.tmp'
    438             with open (tmp_pathname, 'w', encoding=encoding) as file:
    439                 for line in lines:
    440                     file.write (line)
    441                 os.fchmod (file.fileno(), mode)
    442             if self.use_quilt:
    443                 subprocess.call (['quilt', 'add', pathname])
    444             os.rename (tmp_pathname, pathname)
    445 
    446     def process_tree (self, tree, filter):
    447         for (dir, subdirs, filenames) in os.walk (tree):
    448             # Don't recurse through directories that should be skipped.
    449             for i in range (len (subdirs) - 1, -1, -1):
    450                 if filter.skip_dir (dir, subdirs[i]):
    451                     del subdirs[i]
    452 
    453             # Handle the files in this directory.
    454             for filename in filenames:
    455                 if filter.skip_file (dir, filename):
    456                     sys.stdout.write ('Skipping %s\n'
    457                                       % os.path.join (dir, filename))
    458                 else:
    459                     self.process_file (dir, filename, filter)
    460 
    461 class CmdLine:
    462     def __init__ (self, copyright = Copyright):
    463         self.errors = Errors()
    464         self.copyright = copyright (self.errors)
    465         self.dirs = []
    466         self.default_dirs = []
    467         self.chosen_dirs = []
    468         self.option_handlers = dict()
    469         self.option_help = []
    470 
    471         self.add_option ('--help', 'Print this help', self.o_help)
    472         self.add_option ('--quilt', '"quilt add" files before changing them',
    473                          self.o_quilt)
    474         self.add_option ('--this-year', 'Add the current year to every notice',
    475                          self.o_this_year)
    476 
    477     def add_option (self, name, help, handler):
    478         self.option_help.append ((name, help))
    479         self.option_handlers[name] = handler
    480 
    481     def add_dir (self, dir, filter = GenericFilter()):
    482         self.dirs.append ((dir, filter))
    483 
    484     def o_help (self, option = None):
    485         sys.stdout.write ('Usage: %s [options] dir1 dir2...\n\n'
    486                           'Options:\n' % sys.argv[0])
    487         format = '%-15s %s\n'
    488         for (what, help) in self.option_help:
    489             sys.stdout.write (format % (what, help))
    490         sys.stdout.write ('\nDirectories:\n')
    491 
    492         format = '%-25s'
    493         i = 0
    494         for (dir, filter) in self.dirs:
    495             i += 1
    496             if i % 3 == 0 or i == len (self.dirs):
    497                 sys.stdout.write (dir + '\n')
    498             else:
    499                 sys.stdout.write (format % dir)
    500         sys.exit (0)
    501 
    502     def o_quilt (self, option):
    503         self.copyright.set_use_quilt (True)
    504 
    505     def o_this_year (self, option):
    506         self.copyright.include_year (time.localtime().tm_year)
    507 
    508     def main (self):
    509         for arg in sys.argv[1:]:
    510             if arg[:1] != '-':
    511                 self.chosen_dirs.append (arg)
    512             elif arg in self.option_handlers:
    513                 self.option_handlers[arg] (arg)
    514             else:
    515                 self.errors.report (None, 'unrecognised option: ' + arg)
    516         if self.errors.ok():
    517             if len (self.chosen_dirs) == 0:
    518                 self.chosen_dirs = self.default_dirs
    519             if len (self.chosen_dirs) == 0:
    520                 self.o_help()
    521             else:
    522                 for chosen_dir in self.chosen_dirs:
    523                     canon_dir = os.path.join (chosen_dir, '')
    524                     count = 0
    525                     for (dir, filter) in self.dirs:
    526                         if (dir + os.sep).startswith (canon_dir):
    527                             count += 1
    528                             self.copyright.process_tree (dir, filter)
    529                     if count == 0:
    530                         self.errors.report (None, 'unrecognised directory: '
    531                                             + chosen_dir)
    532         sys.exit (0 if self.errors.ok() else 1)
    533 
    534 #----------------------------------------------------------------------------
    535 
    536 class TopLevelFilter (GenericFilter):
    537     def skip_dir (self, dir, subdir):
    538         return True
    539 
    540 class ConfigFilter (GenericFilter):
    541     def __init__ (self):
    542         GenericFilter.__init__ (self)
    543 
    544     def skip_file (self, dir, filename):
    545         if filename.endswith ('.m4'):
    546             pathname = os.path.join (dir, filename)
    547             with open (pathname) as file:
    548                 # Skip files imported from gettext.
    549                 if file.readline().find ('gettext-') >= 0:
    550                     return True
    551         return GenericFilter.skip_file (self, dir, filename)
    552 
    553 class GCCFilter (GenericFilter):
    554     def __init__ (self):
    555         GenericFilter.__init__ (self)
    556 
    557         self.skip_files |= set ([
    558                 # Not part of GCC
    559                 'math-68881.h',
    560 
    561                 # Weird ways to compose copyright year
    562                 'GmcOptions.cc',
    563                 ])
    564 
    565         self.skip_dirs |= set ([
    566                 # Better not create a merge nightmare for the GNAT folks.
    567                 'ada',
    568 
    569                 # Handled separately.
    570                 'testsuite',
    571                 ])
    572 
    573         self.skip_extensions |= set ([
    574                 # Maintained by the translation project.
    575                 '.po',
    576 
    577                 # Automatically-generated.
    578                 '.pot',
    579                 ])
    580 
    581         self.fossilised_files |= set ([
    582                 # Old news won't be updated.
    583                 'ONEWS',
    584                 ])
    585 
    586 class TestsuiteFilter (GenericFilter):
    587     def __init__ (self):
    588         GenericFilter.__init__ (self)
    589 
    590         self.skip_extensions |= set ([
    591                 # Don't change the tests, which could be woend by anyone.
    592                 '.c',
    593                 '.C',
    594                 '.cc',
    595                 '.d',
    596                 '.h',
    597                 '.hs',
    598                 '.f',
    599                 '.f90',
    600                 '.go',
    601                 '.inc',
    602                 '.java',
    603                 '.mod',
    604                 '.rs'
    605                 ])
    606 
    607     def skip_file (self, dir, filename):
    608         # g++.niklas/README contains historical copyright information
    609         # and isn't updated.
    610         if filename == 'README' and os.path.basename (dir) == 'g++.niklas':
    611             return True
    612         # Similarly params/README.
    613         if filename == 'README' and os.path.basename (dir) == 'params':
    614             return True
    615         if filename == 'pdt_5.f03' and os.path.basename (dir) == 'gfortran.dg':
    616             return True
    617         return GenericFilter.skip_file (self, dir, filename)
    618 
    619 class LibCppFilter (GenericFilter):
    620     def __init__ (self):
    621         GenericFilter.__init__ (self)
    622 
    623         self.skip_extensions |= set ([
    624                 # Maintained by the translation project.
    625                 '.po',
    626 
    627                 # Automatically-generated.
    628                 '.pot',
    629                 ])
    630 
    631 class LibGCCFilter (GenericFilter):
    632     def __init__ (self):
    633         GenericFilter.__init__ (self)
    634 
    635         self.skip_dirs |= set ([
    636                 # Imported from GLIBC.
    637                 'soft-fp',
    638                 ])
    639 
    640 class LibPhobosFilter (GenericFilter):
    641     def __init__ (self):
    642         GenericFilter.__init__ (self)
    643 
    644         self.skip_files |= set ([
    645                 # Source modules imported from upstream.
    646                 'object.d',
    647                 '__builtins.di'
    648                 ])
    649 
    650         self.skip_dirs |= set ([
    651                 # Contains sources imported from upstream.
    652                 'core',
    653                 'etc',
    654                 'gc',
    655                 'gcstub',
    656                 'rt',
    657                 'std',
    658                 ])
    659 
    660 class LibStdCxxFilter (GenericFilter):
    661     def __init__ (self):
    662         GenericFilter.__init__ (self)
    663 
    664         self.skip_files |= set ([
    665                 # Contains no copyright of its own, but quotes the GPL.
    666                 'intro.xml',
    667                 ])
    668 
    669         self.skip_dirs |= set ([
    670                 # Contains automatically-generated sources.
    671                 'html',
    672 
    673                 # The testsuite data files shouldn't be changed.
    674                 'data',
    675 
    676                 # Contains imported images
    677                 'images',
    678                 ])
    679 
    680         self.own_files |= set ([
    681                 # Contains markup around the copyright owner.
    682                 'spine.xml',
    683                 ])
    684 
    685     def get_line_filter (self, dir, filename):
    686         if filename == 'boost_concept_check.h':
    687             return re.compile ('// \(C\) Copyright Jeremy Siek')
    688         return GenericFilter.get_line_filter (self, dir, filename)
    689 
    690 class ContribFilter(GenericFilter):
    691     def __init__ (self):
    692         GenericFilter.__init__ (self)
    693 
    694         self.skip_files |= set ([
    695                 # A different copyrights.
    696                 'unicode-license.txt',
    697                 'Info.plist',
    698                 # Contains CR (^M).
    699                 'repro_fail',
    700                 'test_patches.txt',
    701                 ])
    702 
    703 class GCCCopyright (Copyright):
    704     def __init__ (self, errors):
    705         Copyright.__init__ (self, errors)
    706 
    707         canon_fsf = 'Free Software Foundation, Inc.'
    708         self.add_package_author ('Free Software Foundation', canon_fsf)
    709         self.add_package_author ('Free Software Foundation.', canon_fsf)
    710         self.add_package_author ('Free Software Foundation Inc.', canon_fsf)
    711         self.add_package_author ('Free Software Foundation, Inc', canon_fsf)
    712         self.add_package_author ('Free Software Foundation, Inc.', canon_fsf)
    713         self.add_package_author ('The Free Software Foundation', canon_fsf)
    714         self.add_package_author ('The Free Software Foundation, Inc.', canon_fsf)
    715         self.add_package_author ('Software Foundation, Inc.', canon_fsf)
    716 
    717         self.add_external_author ('ARM')
    718         self.add_external_author ('AdaCore')
    719         self.add_external_author ('Advanced Micro Devices Inc.')
    720         self.add_external_author ('Ami Tavory and Vladimir Dreizin, IBM-HRL.')
    721         self.add_external_author ('Cavium Networks.')
    722         self.add_external_author ('David Malcolm')
    723         self.add_external_author ('Faraday Technology Corp.')
    724         self.add_external_author ('Florida State University')
    725         self.add_external_author ('Gerard Jungman')
    726         self.add_external_author ('Greg Colvin and Beman Dawes.')
    727         self.add_external_author ('Hewlett-Packard Company')
    728         self.add_external_author ('Intel Corporation')
    729         self.add_external_author ('Information Technology Industry Council.')
    730         self.add_external_author ('James Theiler, Brian Gough')
    731         self.add_external_author ('Makoto Matsumoto and Takuji Nishimura,')
    732         self.add_external_author ('Mentor Graphics Corporation')
    733         self.add_external_author ('National Research Council of Canada.')
    734         self.add_external_author ('NVIDIA Corporation')
    735         self.add_external_author ('Peter Dimov and Multi Media Ltd.')
    736         self.add_external_author ('Peter Dimov')
    737         self.add_external_author ('Pipeline Associates, Inc.')
    738         self.add_external_author ('Regents of the University of California.')
    739         self.add_external_author ('Silicon Graphics Computer Systems, Inc.')
    740         self.add_external_author ('Silicon Graphics')
    741         self.add_external_author ('Stephen L. Moshier')
    742         self.add_external_author ('Sun Microsystems, Inc. All rights reserved.')
    743         self.add_external_author ('The D Language Foundation, All Rights Reserved')
    744         self.add_external_author ('The fast_float authors')
    745         self.add_external_author ('The Go Authors.  All rights reserved.')
    746         self.add_external_author ('The Go Authors. All rights reserved.')
    747         self.add_external_author ('The Go Authors.')
    748         self.add_external_author ('The Regents of the University of California.')
    749         self.add_external_author ('Ulf Adams')
    750         self.add_external_author ('Unicode, Inc.')
    751         self.add_external_author ('University of Illinois at Urbana-Champaign.')
    752         self.add_external_author ('University of Toronto.')
    753         self.add_external_author ('Yoshinori Sato')
    754 
    755 class GCCCmdLine (CmdLine):
    756     def __init__ (self):
    757         CmdLine.__init__ (self, GCCCopyright)
    758 
    759         self.add_dir ('.', TopLevelFilter())
    760         # boehm-gc is imported from upstream.
    761         self.add_dir ('c++tools')
    762         self.add_dir ('config', ConfigFilter())
    763         self.add_dir ('contrib', ContribFilter())
    764         self.add_dir ('fixincludes')
    765         self.add_dir ('gcc', GCCFilter())
    766         self.add_dir (os.path.join ('gcc', 'testsuite'), TestsuiteFilter())
    767         self.add_dir ('gnattools')
    768         self.add_dir ('gotools')
    769         self.add_dir ('include')
    770         # intl is imported from upstream.
    771         self.add_dir ('libada')
    772         self.add_dir ('libatomic')
    773         self.add_dir ('libbacktrace')
    774         self.add_dir ('libcc1')
    775         self.add_dir ('libcpp', LibCppFilter())
    776         self.add_dir ('libdecnumber')
    777         # libffi is imported from upstream.
    778         self.add_dir ('libgcc', LibGCCFilter())
    779         self.add_dir ('libgfortran')
    780         # libgo is imported from upstream.
    781         self.add_dir ('libgomp')
    782         self.add_dir ('libiberty')
    783         self.add_dir ('libitm')
    784         self.add_dir ('libobjc')
    785         self.add_dir ('libphobos', LibPhobosFilter())
    786         self.add_dir ('libquadmath')
    787         # libsanitizer is imported from upstream.
    788         self.add_dir ('libssp')
    789         self.add_dir ('libstdc++-v3', LibStdCxxFilter())
    790         self.add_dir ('libvtv')
    791         self.add_dir ('lto-plugin')
    792         # maintainer-scripts maintainer-scripts
    793         # zlib is imported from upstream.
    794 
    795         self.default_dirs = [
    796             'c++tools',
    797             'contrib',
    798             'gcc',
    799             'include',
    800             'libada',
    801             'libatomic',
    802             'libbacktrace',
    803             'libcc1',
    804             'libcpp',
    805             'libdecnumber',
    806             'libgcc',
    807             'libgfortran',
    808             'libgomp',
    809             'libiberty',
    810             'libitm',
    811             'libobjc',
    812             'libphobos',
    813             'libssp',
    814             'libstdc++-v3',
    815             'libvtv',
    816             'lto-plugin',
    817             ]
    818 
    819 GCCCmdLine().main()
    820