check-license.py   [plain text]


#!/usr/bin/env python
#
# check if a file has the proper license in it
#
# USAGE: check-license.py [-C] file1 file2 ... fileN
#
# A 'file' may in fact be a directory, in which case it is recursively
# searched.
#
# If the license cannot be found, then the filename is printed to stdout.
# Typical usage:
#    $ check-license.py . > bad-files
#
# -C switch is used to change licenses.
# Typical usage:
#    $ check-license.py -C file1 file2 ... fileN
#

import sys, os, re

OLD_LICENSE = '''\
 \* ====================================================================
 \* Copyright \(c\) (200[0-9]|200[0-9]-200[0-9]) CollabNet.  All rights reserved.
 \*
 \* This software is licensed as described in the file COPYING, which
 \* you should have received as part of this distribution\.  The terms
 \* are also available at http://subversion.tigris.org/license-1\.html\.
 \* If newer versions of this license are posted there, you may use a
 \* newer version instead, at your option\.
 \*
 \* This software consists of voluntary contributions made by many
 \* individuals\.  For exact contribution history, see the revision
 \* history and logs, available at http://subversion.tigris.org/\.
 \* ====================================================================
'''

SH_OLD_LICENSE = re.subn(r'(?m)^ \\\*', '#', OLD_LICENSE)[0]

# Remember not to do regexp quoting for NEW_LICENSE.  Only OLD_LICENSE
# is used for matching; NEW_LICENSE is inserted as-is.
NEW_LICENSE = '''\
 * ====================================================================
 * Copyright (c) 2000-2009 CollabNet.  All rights reserved.
 *
 * This software is licensed as described in the file COPYING, which
 * you should have received as part of this distribution.  The terms
 * are also available at http://subversion.tigris.org/license-1.html.
 * If newer versions of this license are posted there, you may use a
 * newer version instead, at your option.
 *
 * This software consists of voluntary contributions made by many
 * individuals.  For exact contribution history, see the revision
 * history and logs, available at http://subversion.tigris.org/.
 * ====================================================================
'''

SH_NEW_LICENSE = re.subn(r'(?m)^ \*', '#', NEW_LICENSE)[0]

re_OLD = re.compile(OLD_LICENSE)
re_SH_OLD = re.compile(SH_OLD_LICENSE)
re_EXCLUDE = re.compile(
    r'automatically generated by SWIG'
    + r'|Generated from configure\.in'
    + r'|placed into the public domain'
    )

c_comment_suffices = ('.c', '.java', '.h', '.cpp', '.hw', '.pas')

# Yes, this is an empty tuple. No types that fit in this category uniformly
# have a copyright block.
# Possible types to add here:
# ('.bat', '.py', '.pl', '.in')
sh_comment_suffices = ()

def check_file(fname, old_re, new_lic):
  s = open(fname).read()
  if (not old_re.search(s)
      and not re_EXCLUDE.search(s)):
    print(fname)

def change_license(fname, old_re, new_lic):
  s = open(fname).read()
  m = old_re.search(s)
  if not m:
    print('ERROR: missing old license: %s' % fname)
  else:
    s = s[:m.start()] + new_lic + s[m.end():]
    open(fname, 'w').write(s)
    print('Changed: %s' % fname)

def visit(baton, dirname, dircontents):
  file_func = baton
  for i in dircontents:
    # Don't recurse into certain directories
    if i in ('.svn', '.libs'):
      dircontents.remove(i)
      continue

    extension = os.path.splitext(i)[1]
    fullname = os.path.join(dirname, i)

    if os.path.isdir(fullname):
      continue

    if extension in c_comment_suffices:
      file_func(fullname, re_OLD, NEW_LICENSE)
    elif extension in sh_comment_suffices:
      file_func(fullname, re_SH_OLD, SH_NEW_LICENSE)

def main():
  file_func = check_file
  if sys.argv[1] == '-C':
    print('Changing license text...')
    del sys.argv[1]
    file_func = change_license

  for f in sys.argv[1:]:
    if os.path.isdir(f):
      baton = file_func
      os.path.walk(f, visit, baton)
    else:
      baton = file_func
      dir, i = os.path.split(f)
      visit(baton, dir, i)

if __name__ == '__main__':
  main()