copy script/static-checks folder from tf-a-ci-scripts

This is a complete copy of the folder [1], based on working dir
from commit [2]. Some checks DO NOT apply to TF-M project, but we
introduce the code as it is from TF-A project for completeness and
then further commits will remove/change relevant tests.

[1] https://git.trustedfirmware.org/ci/tf-a-ci-scripts.git/tree/script/static-checks
[2] https://git.trustedfirmware.org/ci/tf-a-ci-scripts.git/commit/?id=a16c8e2b37e2a8446e586f45c3e9b406496ad7b8

Change-Id: I024c30dc59caa6e635fcd43b1a30086231b29bc3
Signed-off-by: Leonardo Sandoval <leonardo.sandoval@linaro.org>
diff --git a/script/static-checks/check-banned-api.py b/script/static-checks/check-banned-api.py
new file mode 100755
index 0000000..0bfadeb
--- /dev/null
+++ b/script/static-checks/check-banned-api.py
@@ -0,0 +1,217 @@
+#!/usr/bin/env python3
+#
+# Copyright (c) 2019, Arm Limited. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+
+import argparse
+import os
+import re
+import sys
+import utils
+
+# File extensions to check
+VALID_FILE_EXTENSIONS = ('.c', '.S', '.h')
+
+# Paths inside the tree to ignore. Hidden folders and files are always ignored.
+# They mustn't end in '/'.
+IGNORED_FOLDERS = (
+    "tools",
+    "docs"
+)
+
+# List of ignored files in folders that aren't ignored
+IGNORED_FILES = ()
+
+# Regular expression for searching the Banned APIs. This is taken from the
+# Coding guideline in TF-A repo
+BANNED_APIS = ["strcpy", "wcscpy", "strncpy", "strcat", "wcscat", "strncat",
+               "sprintf", "vsprintf", "strtok", "atoi", "atol", "atoll",
+               "itoa", "ltoa", "lltoa"]
+BANNED_PATTERN = re.compile('|'.join(BANNED_APIS))
+
+COMMENTS_PATTERN = re.compile(r"//|/\*|\*/")
+
+
+def filter_comments(f):
+    '''
+    filter_comments(f) -> iterator for line number, filtered line
+
+    Given an iterable of lines (such as a file), return another iterable of
+    lines, with the comments filtered out and removed.
+    '''
+
+    in_comment = False
+    for line_num, line in enumerate(f):
+        line = line.rstrip('\n')
+
+        temp = ""
+        breaker = len(line) if in_comment else 0
+        for match in COMMENTS_PATTERN.finditer(line):
+            content = match.group(0)
+            start, end = match.span()
+
+            if in_comment:
+                if content == "*/":
+                    in_comment = False
+                    breaker = end
+            else:
+                if content == "/*":
+                    in_comment = True
+                    temp += line[breaker:start]
+                    breaker = len(line)
+                elif content == "//":
+                    temp += line[breaker:start]
+                    breaker = len(line)
+                    break
+
+        temp += line[breaker:]
+        if temp:
+            yield line_num + 1, temp
+
+
+def file_check_banned_api(path, encoding='utf-8'):
+    '''
+    Reads all lines from a file in path and checks for any banned APIs.
+    The combined number of errors and uses of banned APIs is returned. If the
+    result is equal to 0, the file is clean and contains no banned APIs.
+    '''
+
+    count = 0
+
+    try:
+        f = open(path, encoding=encoding)
+    except FileNotFoundError:
+        print("ERROR: could not open " + path)
+        utils.print_exception_info()
+        return True
+
+    try:
+        for line_num, line in filter_comments(f):
+            match = BANNED_PATTERN.search(line)
+            if match:
+                location = "line {} of file {}".format(line_num, path)
+                print("BANNED API: in " + location)
+
+                # NOTE: this preview of the error is not perfect if comments
+                # have been removed - however, it does good enough most of the
+                # time.
+                start, end = match.span()
+                print(">>> {}".format(line))
+                print("    {}^{}".format(start * " ", (end - start - 1) * "~"))
+
+                count += 1
+    except:
+        print("ERROR: unexpected exception while parsing " + path)
+        utils.print_exception_info()
+        count += 1
+
+    f.close()
+
+    return count
+
+
+def get_tree_files():
+    '''
+    Get all files in the git repository
+    '''
+
+    # Get patches of the affected commits with one line of context.
+    (rc, stdout, stderr) = utils.shell_command(['git', 'ls-files'])
+    if rc != 0:
+        return False
+
+    lines = stdout.splitlines()
+    return lines
+
+
+def get_patch_files(base_commit, end_commit):
+    '''
+    Get all files that have changed in a given patch
+    '''
+
+    # Get patches of the affected commits with one line of context.
+    (rc, stdout, stderr) = utils.shell_command([
+        'git', 'diff-tree', '--diff-filter=ACMRT', '-r', '--name-only',
+        base_commit, end_commit])
+
+    if rc != 0:
+        return False
+
+    paths = stdout.splitlines()
+    return paths
+
+
+def parse_cmd_line():
+    parser = argparse.ArgumentParser(
+        description="Check Banned APIs",
+        epilog="""
+            For each source file in the tree, checks whether Banned APIs as
+            described in the list are used or not.
+        """
+    )
+
+    parser.add_argument("--tree", "-t",
+                        help="""
+                        Path to the source tree to check (default: %(default)s)
+                        """,
+                        default=os.curdir)
+    parser.add_argument("--patch", "-p",
+                        help="""
+                        Patch mode. Instead of checking all files in
+                        the source tree, the script will consider only files
+                        that are modified by the latest patch(es).
+                        """,
+                        action="store_true")
+    parser.add_argument("--from-ref",
+                        help="""
+                        Base commit in patch mode (default: %(default)s)
+                        """,
+                        default="master")
+    parser.add_argument("--to-ref",
+                        help="""
+                        Final commit in patch mode (default: %(default)s)
+                        """,
+                        default="HEAD")
+    parser.add_argument("--verbose", "-v",
+                        help="Print verbose output",
+                        action="store_true")
+    args = parser.parse_args()
+    return args
+
+
+if __name__ == "__main__":
+    args = parse_cmd_line()
+
+    os.chdir(args.tree)
+
+    if args.patch:
+        print("Checking files modified between patches " + args.from_ref +
+              " and " + args.to_ref + "...\n")
+        files = get_patch_files(args.from_ref, args.to_ref)
+    else:
+        print("Checking all files git repo " + os.path.abspath(args.tree) +
+              "...\n")
+        files = get_tree_files()
+
+    total_errors = 0
+    for filename in files:
+        ignored = utils.file_is_ignored(filename, VALID_FILE_EXTENSIONS,
+                                        IGNORED_FILES, IGNORED_FOLDERS)
+        if ignored:
+            if args.verbose:
+                print("INFO: Skipping ignored file " + filename)
+            continue
+
+        if args.verbose:
+            print("INFO: Checking " + filename)
+
+        total_errors += file_check_banned_api(filename)
+
+    print(str(total_errors) + " errors found")
+
+    if total_errors == 0:
+        sys.exit(0)
+    else:
+        sys.exit(1)