|  | @@ -1,6 +1,21 @@
 | 
	
		
			
				|  |  |  #!/usr/bin/python3
 | 
	
		
			
				|  |  |  # Copyright 2018 The Tor Project, Inc.  See LICENSE file for licensing info.
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +"""This script looks through all the directories for files matching *.c or
 | 
	
		
			
				|  |  | +   *.h, and checks their #include directives to make sure that only "permitted"
 | 
	
		
			
				|  |  | +   headers are included.
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +   Any #include directives with angle brackets (like #include <stdio.h>) are
 | 
	
		
			
				|  |  | +   ignored -- only directives with quotes (like #include "foo.h") are
 | 
	
		
			
				|  |  | +   considered.
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +   To decide what includes are permitted, this script looks at a .may_include
 | 
	
		
			
				|  |  | +   file in each directory.  This file contains empty lines, #-prefixed
 | 
	
		
			
				|  |  | +   comments, filenames (like "lib/foo/bar.h") and file globs (like lib/*/*.h)
 | 
	
		
			
				|  |  | +   for files that are permitted.
 | 
	
		
			
				|  |  | +"""
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  from __future__ import print_function
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  import fnmatch
 | 
	
	
		
			
				|  | @@ -8,20 +23,26 @@ import os
 | 
	
		
			
				|  |  |  import re
 | 
	
		
			
				|  |  |  import sys
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +# Global: Have there been any errors?
 | 
	
		
			
				|  |  |  trouble = False
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  def err(msg):
 | 
	
		
			
				|  |  | +    """ Declare that an error has happened, and remember that there has
 | 
	
		
			
				|  |  | +        been an error. """
 | 
	
		
			
				|  |  |      global trouble
 | 
	
		
			
				|  |  |      trouble = True
 | 
	
		
			
				|  |  |      print(msg, file=sys.stderr)
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  def fname_is_c(fname):
 | 
	
		
			
				|  |  | +    """ Return true iff 'fname' is the name of a file that we should
 | 
	
		
			
				|  |  | +        search for possibly disallowed #include directives. """
 | 
	
		
			
				|  |  |      return fname.endswith(".h") or fname.endswith(".c")
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  INCLUDE_PATTERN = re.compile(r'\s*#\s*include\s+"([^"]*)"')
 | 
	
		
			
				|  |  |  RULES_FNAME = ".may_include"
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  class Rules(object):
 | 
	
		
			
				|  |  | +    """ A 'Rules' object is the parsed version of a .may_include file. """
 | 
	
		
			
				|  |  |      def __init__(self, dirpath):
 | 
	
		
			
				|  |  |          self.dirpath = dirpath
 | 
	
		
			
				|  |  |          self.patterns = []
 | 
	
	
		
			
				|  | @@ -59,6 +80,7 @@ class Rules(object):
 | 
	
		
			
				|  |  |                  print("Pattern {} in {} was never used.".format(p, self.dirpath))
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  def load_include_rules(fname):
 | 
	
		
			
				|  |  | +    """ Read a rules file from 'fname', and return it as a Rules object. """
 | 
	
		
			
				|  |  |      result = Rules(os.path.split(fname)[0])
 | 
	
		
			
				|  |  |      with open(fname, 'r') as f:
 | 
	
		
			
				|  |  |          for line in f:
 | 
	
	
		
			
				|  | @@ -81,6 +103,6 @@ for dirpath, dirnames, fnames in os.walk("src"):
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  if trouble:
 | 
	
		
			
				|  |  |      err(
 | 
	
		
			
				|  |  | -"""To change which includes are allowed in a C file, edit the {} files in its
 | 
	
		
			
				|  |  | -enclosing directory.""".format(RULES_FNAME))
 | 
	
		
			
				|  |  | +"""To change which includes are allowed in a C file, edit the {}
 | 
	
		
			
				|  |  | +files in its enclosing directory.""".format(RULES_FNAME))
 | 
	
		
			
				|  |  |      sys.exit(1)
 |