--- /dev/null
+#!/usr/bin/env python
+# -*- coding: iso-8859-1 -*-
+
+# Copyright 2016 EDF R&D
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License Version 3 as
+# published by the Free Software Foundation.
+#
+# 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, you may download a copy of license
+# from https://www.gnu.org/licenses/gpl-3.0.
+
+"""
+Command line tool to insert copyright notice to a file.
+Usage: type "insert_copyright --help" to learn how to use tool.
+"""
+
+import optparse
+import os
+import re
+import sys
+import time
+
+# pragma pylint: disable=redefined-builtin
+
+# -----------------------------------------------------------------------------
+_COMMENTS = {
+ 'cpp' : '//',
+ 'shell' : '#',
+ 'python' : '#',
+ 'auto' : None,
+ }
+
+_OWNERS = {
+ 'cea' : 'CEA/DEN',
+ 'edf' : 'EDF R&D',
+ 'occ' : 'OPEN CASCADE'
+ }
+
+# -----------------------------------------------------------------------------
+def error_exit(msg):
+ """
+ Print error message to stderr and exit.
+
+ Arguments:
+ msg (str): Error message.
+ """
+ sys.stderr.write("ERROR: {}\n".format(msg))
+ sys.exit(-1)
+
+# -----------------------------------------------------------------------------
+def warning(msg):
+ """
+ Print wating message to stderr.
+
+ Arguments:
+ msg (str): Warning message.
+ """
+ sys.stderr.write("WARNING: {}\n".format(msg))
+
+# -----------------------------------------------------------------------------
+def formats():
+ """
+ Get supported formats of comments.
+
+ Returns:
+ list[str]: List of formats.
+ """
+ return _COMMENTS.keys()
+
+# -----------------------------------------------------------------------------
+def search_line(lines, rex, depth=1):
+ """
+ Search regexp in given lines.
+
+ Arguments:
+ lines (list[str]): List of strings.
+ regex (str): Regular expression.
+ depth (Optional[int]): Depth of search. Defaults to 1 line.
+
+ Returns:
+ int: Index of first matched line.
+ """
+ for i in range(depth if depth >= 0 else len(lines)):
+ if i < len(lines):
+ if re.search(rex, lines[i]):
+ return i
+ return -1
+
+# -----------------------------------------------------------------------------
+def get_owner(owner):
+ """
+ Get owner's title
+
+ Arguments:
+ owner (str): Owner's name of alias or list of owners separated
+ by comma.
+
+ Returns:
+ str: Owner's title.
+ """
+ if owner.lower() in 'all':
+ return get_owner('cea,edf,occ')
+
+ owners = [i.strip() for i in owner.split(',')]
+ result = []
+ for i in owners:
+ i = _OWNERS.get(i.lower(), i)
+ if i not in result:
+ result.append(i)
+ return ', '.join(result)
+
+# -----------------------------------------------------------------------------
+def get_comment(format):
+ """
+ Get comment for given format.
+
+ Arguments:
+ format (str): Format of comments.
+
+ Returns:
+ str: Comment signature for given format; *None* for unsupported
+ format.
+ """
+ return _COMMENTS.get(format) if format else None
+
+# -----------------------------------------------------------------------------
+def get_copyright(comment, owner, year):
+ """
+ Generate copyright from template.
+
+ Arguments:
+ comment (str): Comment signature.
+ owner (str): Copyright owner.
+ year (str): Copyright year(s).
+
+ Returns:
+ list[str]: List of strings with copyright data.
+ """
+ template = os.path.join(os.path.dirname(sys.argv[0]), 'copyright.template')
+ copyright = []
+ try:
+ with open(template) as fid:
+ copyright = fid.readlines()
+ except IOError:
+ error_exit("cannot find copyright template")
+ copyright = [i.replace('@year@', year) for i in copyright]
+ copyright = [i.replace('@owner@', owner) for i in copyright]
+ copyright = [comment + ' ' + i for i in copyright]
+ return copyright
+
+# -----------------------------------------------------------------------------
+def get_module_owner(module):
+ """
+ Get owner of given module.
+
+ Arguments:
+ module (str): Module name.
+
+ Returns:
+ str: Module's owner.
+ """
+ modules_info = os.path.join(os.path.dirname(sys.argv[0]), 'modules.info')
+ owner = None
+ try:
+ with open(modules_info) as fid:
+ lines = fid.readlines()
+ index = search_line(lines, r'^{}:'.format(module), -1)
+ if index >= 0:
+ return get_owner(lines[index].split(":")[1].strip())
+ except IOError:
+ warning("cannot find modules info file")
+ return owner
+
+# -----------------------------------------------------------------------------
+def autodetect_owner(filename):
+ """
+ Auto-detect owner from file path.
+
+ Arguments:
+ filename (str): File path.
+
+ Returns:
+ str: Owner; *None* if owner isn't detected.
+ """
+ filename = os.path.realpath(filename)
+ if os.path.exists(filename):
+ directory = os.path.dirname(filename)
+ while directory != '/':
+ config_file = os.path.join(directory, '.git', 'config')
+ if os.path.exists(config_file):
+ from ConfigParser import ConfigParser
+ from StringIO import StringIO
+ with open(config_file) as fid:
+ gitcfg = fid.readlines()
+ cfg = ConfigParser()
+ data = StringIO(''.join([l.lstrip() for l in gitcfg]))
+ cfg.readfp(data)
+ url = cfg.get('remote "origin"', 'url')
+ module = os.path.split(url)[-1]
+ if module.endswith('.git'):
+ module = module[:-4]
+ return get_module_owner(module)
+ break
+ directory = os.path.dirname(directory)
+ return None
+
+# -----------------------------------------------------------------------------
+def autodetect_format(filename):
+ """
+ Auto-detect format from filename.
+
+ Arguments:
+ filename (str): File path.
+
+ Returns:
+ str: Format of comments; *None* if format isn't detected.
+ """
+ extensions = {
+ 'cpp' : ('c', 'cpp', 'cxx', 'cc', 'c++',
+ 'h', 'hxx', 'hpp', 'hh', 'h++',
+ 'idl', 'i'),
+ 'shell' : ('sh', 'bash', 'csh', 'cmake', 'txt', 'cfg', 'ini', 'm4'),
+ 'python' : ('py',),
+ }
+ if filename and os.path.isfile(filename):
+ extension = os.path.splitext(filename)[1][1:].lower()
+ if extension in ('in',):
+ name = os.path.splitext(filename)[0]
+ extension = os.path.splitext(name)[1][1:].lower()
+ for format in extensions:
+ if extension in extensions[format]:
+ return format
+ return None
+
+# -----------------------------------------------------------------------------
+def insert_copyright(filename, owner, year, format):
+ """
+ Insert copyright note to a file.
+
+ Arguments:
+ filename (str): File path.
+ owner (str): Copyright owner.
+ year (str): Copyright year(s).
+ format (str): Format of comments.
+ """
+ try:
+ with open(filename) as fid:
+ lines = fid.readlines()
+ except IOError:
+ warning("cannot read file: {}".format(filename))
+ return
+
+ if format in ('auto',):
+ format = autodetect_format(filename)
+
+ if owner.lower() in ('auto',):
+ owner = autodetect_owner(filename) or get_owner('all')
+ else:
+ owner = get_owner(owner)
+
+ comment = get_comment(format)
+ if comment is None:
+ warning("cannot detect format")
+ return
+
+ shell_row = search_line(lines, r'^#!') \
+ if format in ('sh', 'bash', 'csh', 'py', 'python') else -1
+ coding_row = search_line(lines, r'-\*- coding:', 3) \
+ if format in ('py', 'python') else -1
+ insert_point = max(0, shell_row+1, coding_row+1)
+
+ copyright = get_copyright(comment, owner, year)
+ if copyright:
+ lines = lines[:insert_point] + copyright + ['\n'] \
+ + lines[insert_point:]
+ try:
+ with open(filename, 'w') as fid:
+ for line in lines:
+ fid.write(line)
+ except IOError:
+ warning("cannot write file: {}".format(filename))
+ return
+
+# -----------------------------------------------------------------------------
+def main():
+ """Main function."""
+
+ # Parse command line.
+ usage = "%prog [options] [FILE] ..."
+ description = "Command line tool to insert copyright notice to a file."
+ parser = optparse.OptionParser(usage=usage, description=description)
+
+ help_string = "copyright owner; if not specified, tool tries to " \
+ "autodetect an owner from the file path; if auto-detection fails, " \
+ "an owner is set to '{owner}'"
+ owner = 'auto'
+ parser.add_option("-o", "--owner", action="store",
+ dest="owner", default=owner,
+ help=help_string.format(owner=get_owner('all')))
+ help_string = "copyright year(s); default: current year ({year})"
+ year = str(time.localtime().tm_year)
+ parser.add_option("-y", "--year", action="store",
+ dest="year", default=year,
+ help=help_string.format(year=year))
+ help_string = "format of comments ({choices}); default: {format}"
+ format = 'auto'
+ parser.add_option("-f", "--format", action="store",
+ type='choice', choices=formats(),
+ dest="format", default=format,
+ help=help_string.format(format=format,
+ choices="|".join(formats())))
+
+ options, files = parser.parse_args(sys.argv[1:])
+
+ owner = options.owner
+ year = options.year
+ format = options.format
+
+ if not files:
+ error_exit('file is not specified')
+
+ for filename in files:
+ insert_copyright(filename, owner, year, format)
+
+ return 0
+
+# -----------------------------------------------------------------------------
+if __name__ == "__main__":
+ sys.exit(main())