fw4spl
forbidtoken.py
1 #!/usr/bin/env python2
2 # -*- coding: utf-8 -*-
3 
4 """
5 Make sure you do not commit TAB, CRLF, ... in matching pattern files.
6 
7 .gitconfig configuration :
8 
9 [fw4spl-hooks]
10  hooks = crlf tab digraphs doxygen
11 
12 [forbidtoken-hooks]
13  crlf= *.cpp *.hpp *.xml
14  lgpl= *.cpp *.hpp *.xml
15  tab= *.cpp *.xml *.py
16 
17 To do the same check on a server to prevent CRLF/CR from being
18 pushed or pulled:
19 
20 Default file pattern is '*'. To specify patterns to check :
21 
22 """
23 
24 import re
25 from functools import partial
26 
27 import common
28 
29 CRLF = lambda x: '\r\n' in x
30 CR = lambda x: '\r' in x
31 TAB = lambda x: '\t' in x
32 LGPL = lambda x: 'Lesser General Public License' in x
33 BSD = lambda x: 'under the terms of the BSD Licence' in x
34 SLM_LOG = lambda x: bool(
35  ('O''SLM_LOG' in x) and not (re.search('#define O''SLM_LOG', x) and x.count('O''SLM_LOG') == 1)
36 )
37 DIGRAPH = lambda x: "<:" in x or ":>" in x
38 DOXYGEN = lambda x: '* @class' in x or '* @date' in x or '* @namespace' in x
39 COPAIN = lambda x: 'copain' in x
40 
41 tr = {
42  'crlf': (CRLF, 'CRLF line endings', '*.cpp *.hpp *.hxx *.cxx *.c *.h *.xml *.txt *.cmake *.py'),
43  'cr': (CR, 'CR line endings', '*.cpp *.hpp *.hxx *.cxx *.c *.h *.xml *.txt *.cmake *.py'),
44  'tab': (TAB, 'TAB', '*.cpp *.hpp *.hxx *.cxx *.c *.h *.xml *.txt *.cmake *.py'),
45  'lgpl': (LGPL, 'LGPL Header', '*.cpp *.hpp *.hxx *.cxx *.c *.h *.xml *.txt *.cmake'),
46  'bsd': (BSD, 'BSD Header', '*.cpp *.hpp *.hxx *.cxx *.c *.h *.xml *.txt *.cmake'),
47  'oslmlog': (SLM_LOG, 'O''SLM_LOG', '*.cpp *.hpp *.hxx *.cxx *.c *.h'),
48  'digraphs': (DIGRAPH, 'Forbiden digraphs: <'':, :''>', '*.cpp *.hpp *.hxx *.cxx *.c *.h'),
49  'doxygen': (DOXYGEN, '@class @date @namespace doxygen tag(s)', '*.cpp *.hpp *.hxx *.cxx *.c *.h'),
50  'copain': (COPAIN, 'copain', '*.cpp *.hpp *.hxx *.cxx *.c *.h'),
51 }
52 
53 WARNING = ('Attempt to commit or push text file(s) containing "%s"')
54 FILEWARN = (' - %s:%s')
55 
56 
57 def forbidtoken(files, config_name):
58  include_patterns = common.get_option('forbidtoken-hook.' + config_name, default=tr[config_name][2]).split()
59 
60  common.note('Checking for "' + config_name + '" tokens on ' + ', '.join(include_patterns) + ' files')
61  abort = False
62  token = tr[config_name][0]
63  line_iter = lambda x: enumerate(re.finditer(".*\n", x, re.MULTILINE), 1)
64  line_match = lambda test, x: (n for n, m in line_iter(x) if test(m.group()))
65 
66  count = 0
67  for f in files:
68  if not any(f.fnmatch(p) for p in include_patterns):
69  continue
70  common.trace('Checking ' + str(f.path) + '...')
71  content = f.contents
72  if not common.binary(content) and token(content):
73  if not abort:
74  common.error(WARNING % (tr[config_name][1]))
75  for n in line_match(token, content):
76  common.error(FILEWARN % (f.path, n))
77  abort = True
78  count += 1
79 
80  if abort:
81  common.error('Hook "' + config_name + '" failed.')
82 
83  common.note('%d file(s) checked.' % count)
84 
85  return abort
86 
87 
88 hooks = dict(
89  [(name, partial(forbidtoken, config_name=name)) for name in tr]
90 )
def error(msg)
Definition: common.py:52
def binary(s)
Definition: common.py:60
def trace(msg)
Definition: common.py:47
def note(msg)
Definition: common.py:43
def get_option(option, default, type="")
Definition: common.py:150