| # Copyright 2016 Google Inc. All Rights Reserved. |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); |
| # you may not use this file except in compliance with the License. |
| # You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| |
| """Port OpenSSL to Starboard (or do a lot of the leg work).""" |
| |
| import logging |
| import os |
| import re |
| import sys |
| |
| # Template to use to replace moved system includes inside a Starboard check. |
| _TEMPLATE = """#include <openssl/opensslconf.h> |
| #if !defined(OPENSSL_SYS_STARBOARD) |
| {includes} |
| #endif // !defined(OPENSSL_SYS_STARBOARD) |
| """ |
| |
| # Included files that match any of these patterns should be left alone. |
| _INCLUDE_EXCEPTIONS = [ |
| r'alloca', # This is defined for SNC compilers |
| r'cryptlib', |
| r'descrip', |
| r'dl[.]', |
| r'e_os', |
| r'err[.]', |
| r'io[.]', |
| r'lib[$]routines', |
| r'libdtdef', |
| r'malloc', # Only used inside a _WIN32 block |
| r'nwfileio', |
| r'openssl', |
| r'rld_interface', |
| r'sys/stat[.]', # Only included inside NO_POSIX_IO checks |
| r'sys/types[.]', # Only included inside NO_SYS_TYPES_H checks |
| r'windows', |
| ] |
| |
| # References to these symbols should be wrapped with OPENSSL_port_ so that we |
| # can redirect them for Starboard. |
| _SYSTEM_FUNCTIONS = [ |
| r'abort', |
| r'assert', |
| r'atoi', |
| r'free', |
| r'getenv', |
| r'isalnum', |
| r'isdigit', |
| r'isspace', |
| r'isupper', |
| r'isxdigit', |
| r'malloc', |
| r'memchr', |
| r'memcmp', |
| r'memcpy', |
| r'memmove', |
| r'memset', |
| r'printf', |
| r'qsort', |
| r'realloc', |
| r'sscanf', |
| r'strcasecmp', |
| r'strcat', |
| r'strchr', |
| r'strcmp', |
| r'strcpy', |
| r'strdup', |
| r'strerror', |
| r'strlen', |
| r'strncasecmp', |
| r'strncmp', |
| r'strncpy', |
| r'strrchr', |
| r'strtoul', |
| r'time', |
| r'tolower', |
| r'toupper', |
| ] |
| |
| # Matches all "system" include lines. Note that this does NOT match include |
| # lines where there is more than one space between the # and the 'include', in |
| # an attempt to avoid touching includes that are already conditional. |
| # |
| # In one case, rand_unix.c, extra spaces were manually introduced to exclude |
| # a large set of includes that were already not being processed. |
| _RE_INCLUDE = re.compile(r'^#\s?include <.*>$') |
| |
| # Matches include lines that look like system includes, but should not be |
| # relocated. |
| _RE_INCLUDE_EXCEPTION = re.compile(r'^#\s*include <(?:' + |
| r'|'.join(_INCLUDE_EXCEPTIONS) + |
| r').*>$') |
| |
| # Matches system function calls that should be wrapped. |
| _RE_SYSTEM_FUNCTION = re.compile(r'(?<!\w)(' + |
| r'|'.join(_SYSTEM_FUNCTIONS) + |
| r')(?=[(])') |
| |
| # Matches calls to fprintf(stderr, ...) |
| _RE_FPRINTF = re.compile(r'(?<!\w)fprintf\s*\(\s*stderr\s*,\s*') |
| |
| # Matches time_t so it can be wrapped. |
| _RE_TIME = re.compile(r'(?<!\w)(time_t)(?!\w)') |
| |
| # Matches errno so it can be wrapped. |
| _RE_ERRNO = re.compile(r'(?<![\w<])(errno)(?![\w.])') |
| |
| # Matches time.h structs tm and timeval, so they can be wrapped. |
| _RE_TIME_STRUCTS = re.compile(r'(?<!\w)struct\s+(timeval|tm)(?!\w)') |
| |
| |
| def _IsSystemInclude(line): |
| return _RE_INCLUDE.match(line) and not _RE_INCLUDE_EXCEPTION.match(line) |
| |
| |
| def _Replace(line): |
| replaced_line = _RE_SYSTEM_FUNCTION.sub(r'OPENSSL_port_\1', line) |
| replaced_line = _RE_FPRINTF.sub(r'OPENSSL_port_printferr(', replaced_line) |
| replaced_line = _RE_TIME.sub(r'OPENSSL_port_\1', replaced_line) |
| replaced_line = _RE_ERRNO.sub(r'OPENSSL_port_\1', replaced_line) |
| replaced_line = _RE_TIME_STRUCTS.sub(r'OPENSSL_port_\1', replaced_line) |
| return replaced_line |
| |
| |
| def _NeedsReplacement(line): |
| return _Replace(line) != line |
| |
| |
| def PortFile(input_file_path): |
| """Reads a source file, ports it, and writes it again. |
| |
| Args: |
| input_file_path: The path to the source file to port. |
| Returns: |
| 0 on success. |
| """ |
| lines = None |
| with open(input_file_path, 'r') as f: |
| lines = f.read().splitlines() |
| |
| # Find all system include lines, keeping track of their line numbers. |
| includes_to_move = [] |
| line_numbers_to_delete = [] |
| needs_update = False |
| for line_number in range(0, len(lines)): |
| line = lines[line_number] |
| if _IsSystemInclude(line): |
| needs_update = True |
| line_numbers_to_delete.append(line_number) |
| includes_to_move.append(line) |
| if _NeedsReplacement(line): |
| needs_update = True |
| |
| # Nothing to do for this file. |
| if not needs_update: |
| return 0 |
| |
| logging.info('Porting: %s', input_file_path) |
| |
| if line_numbers_to_delete: |
| # Delete the include lines. |
| for line_number in reversed(line_numbers_to_delete): |
| del lines[line_number] |
| |
| # Insert them back inside the template. |
| text_to_add = _TEMPLATE.format(includes='\n'.join(sorted(includes_to_move))) |
| lines_to_add = text_to_add.splitlines() |
| insertion_point = line_numbers_to_delete[0] |
| for l in range(0, len(lines_to_add)): |
| logging.debug('line %6d: %s', l + insertion_point, lines_to_add[l]) |
| lines[insertion_point:insertion_point] = lines_to_add |
| |
| for line_number in range(0, len(lines)): |
| line = lines[line_number] |
| replaced_line = _Replace(line) |
| if replaced_line != line: |
| logging.debug('line %6d: %s', line_number, replaced_line) |
| lines[line_number] = replaced_line |
| |
| os.unlink(input_file_path) |
| with open(input_file_path, 'w') as f: |
| f.write('\n'.join(lines) + '\n') |
| |
| return 0 |
| |
| |
| def PortAll(file_list): |
| result = 0 |
| for file_path in file_list: |
| this_result = PortFile(file_path) |
| if this_result != 0: |
| result = this_result |
| print '\n' |
| return result |
| |
| |
| if __name__ == '__main__': |
| logging.basicConfig(format='%(levelname)-7s:%(message)s', level=logging.INFO) |
| sys.exit(PortAll(sys.argv[1:])) |