Introduce scripts automating wrapping structs members with MBEDTLS_PRIVATE.

Usage: run setup_and_run_MBEDTLS_PRIVATE.sh

Signed-off-by: Mateusz Starzyk <mateusz.starzyk@mobica.com>
diff --git a/apply_MBEDTLS_PRIVATE.py b/apply_MBEDTLS_PRIVATE.py
new file mode 100644
index 0000000..4160287
--- /dev/null
+++ b/apply_MBEDTLS_PRIVATE.py
@@ -0,0 +1,84 @@
+import re
+import fileinput
+import glob
+import pprint
+import os
+import xml.etree.ElementTree as ET
+
+
+files_to_visit = {}
+
+struct_files = glob.glob("apidoc/xml/structmbedtls*.xml") + glob.glob("apidoc/xml/structpsa*.xml")
+
+for struct_file in struct_files:
+  struct_file_tree = ET.parse(struct_file)
+  all_struct_members_definitions = struct_file_tree.getroot().findall(".//memberdef[@kind='variable']")
+
+  # Create dictionary with following structre
+  # "filepath" : { "variable_name1": (1, 2, 40, 61),  # line numbers
+  #                "variable_name2": (60, 64),
+  #              }
+  for struct_member_def in all_struct_members_definitions:
+    # find file path for this variable
+    member_id = struct_member_def.attrib["id"]
+    location = struct_member_def.find("location")
+    file_path = location.attrib["file"]
+    variable_name = struct_member_def.find("name").text
+    # if path not yet in dictionary, create empty record to initialize
+    if file_path not in files_to_visit:
+      files_to_visit[file_path] = {}
+    # if variable is not yet in this file's dictionary, create empty set to initialize
+    if variable_name not in files_to_visit[file_path]:
+      files_to_visit[file_path][variable_name] = set()
+
+    # add variable definition
+    files_to_visit[file_path][variable_name].add(int(location.attrib["line"]))
+
+    # check where the variable was referenced
+    references = struct_member_def.findall("referencedby")
+    for reference in references:
+      refid = reference.attrib["refid"]
+      # assuming that compound name is related to header's xml file
+      header_file = "apidoc/xml/" + reference.attrib["compoundref"] + ".xml"
+      header_file_tree = ET.parse(header_file)
+      # check if this reference is created by static inline function
+      static_inline_function_definition = header_file_tree.getroot().find(f".//memberdef[@id='{refid}'][@kind='function'][@static='yes'][@inline='yes']")
+      if static_inline_function_definition:
+        static_inline_function_file_path = static_inline_function_definition.find("location").attrib["file"]
+        # if path not yet in dictionary, create empty record to initialize.
+        # This could happen if reference is inside header file which was not yet processed in search for variable definitions
+        if static_inline_function_file_path not in files_to_visit:
+          files_to_visit[static_inline_function_file_path] = {}
+        # if variable is not yet in this file's dictionary, create empty set to initialize
+        if variable_name not in files_to_visit[static_inline_function_file_path]:
+          files_to_visit[static_inline_function_file_path][variable_name] = set()
+        # function block scope
+        function_lines_from = int(reference.attrib["startline"])
+        function_lines_to = int(reference.attrib["endline"])
+        # find codelines referencing struct's variable
+        codelines_xml = header_file_tree.getroot().findall(f".//ref[@refid='{member_id}']/../..")
+        # filter by function's scope
+        codelines = [int(line.attrib["lineno"]) for line in codelines_xml if int(line.attrib["lineno"]) >= function_lines_from and int(line.attrib["lineno"]) <= function_lines_to]
+        # add variable reference
+        files_to_visit[static_inline_function_file_path][variable_name].update(codelines)
+
+pp = pprint.PrettyPrinter(indent=4)
+pp.pprint(files_to_visit)
+
+for file_path, variables in files_to_visit.items():
+  with fileinput.FileInput(file_path, inplace=True) as file:
+    output_line_number = 1
+    re_include_guard = re.compile(r"^#define.*{name}$".format(name=os.path.basename(file_path).replace('.','_').upper()))
+    for line in file:
+      insert_allow_private_include = False
+      if re_include_guard.match(line):
+        insert_allow_private_include = True
+      for variable, var_lines in variables.items():
+        for var_line in var_lines:
+          if output_line_number == var_line:
+            line = re.sub(r"(^.*?\W+)({var})(\W+.*$)".format(var=variable), r"\1MBEDTLS_PRIVATE(\2)\3", line)
+      output_line_number += 1
+      print(line, end='') # fileinput redirects stdout to the target file
+      if insert_allow_private_include:
+        insert_allow_private_include = False
+        print("#include \"mbedtls/private_access.h\"")