| #!/usr/bin/env python3 |
| # |
| # Simple DirectMedia Layer |
| # Copyright (C) 1997-2024 Sam Lantinga <slouken@libsdl.org> |
| # |
| # This software is provided 'as-is', without any express or implied |
| # warranty. In no event will the authors be held liable for any damages |
| # arising from the use of this software. |
| # |
| # Permission is granted to anyone to use this software for any purpose, |
| # including commercial applications, and to alter it and redistribute it |
| # freely, subject to the following restrictions: |
| # |
| # 1. The origin of this software must not be misrepresented; you must not |
| # claim that you wrote the original software. If you use this software |
| # in a product, an acknowledgment in the product documentation would be |
| # appreciated but is not required. |
| # 2. Altered source versions must be plainly marked as such, and must not be |
| # misrepresented as being the original software. |
| # 3. This notice may not be removed or altered from any source distribution. |
| # |
| # This script detects use of stdlib function in SDL code |
| |
| import argparse |
| import os |
| import pathlib |
| import re |
| import sys |
| |
| SDL_ROOT = pathlib.Path(__file__).resolve().parents[1] |
| |
| words = [ |
| 'abs', |
| 'acos', |
| 'acosf', |
| 'asin', |
| 'asinf', |
| 'asprintf', |
| 'atan', |
| 'atan2', |
| 'atan2f', |
| 'atanf', |
| 'atof', |
| 'atoi', |
| 'bsearch', |
| 'calloc', |
| 'ceil', |
| 'ceilf', |
| 'copysign', |
| 'copysignf', |
| 'cos', |
| 'cosf', |
| 'crc32', |
| 'exp', |
| 'expf', |
| 'fabs', |
| 'fabsf', |
| 'floor', |
| 'floorf', |
| 'fmod', |
| 'fmodf', |
| 'free', |
| 'getenv', |
| 'isalnum', |
| 'isalpha', |
| 'isblank', |
| 'iscntrl', |
| 'isdigit', |
| 'isgraph', |
| 'islower', |
| 'isprint', |
| 'ispunct', |
| 'isspace', |
| 'isupper', |
| 'isxdigit', |
| 'itoa', |
| 'lltoa', |
| 'log10', |
| 'log10f', |
| 'logf', |
| 'lround', |
| 'lroundf', |
| 'ltoa', |
| 'malloc', |
| 'memalign', |
| 'memcmp', |
| 'memcpy', |
| 'memcpy4', |
| 'memmove', |
| 'memset', |
| 'pow', |
| 'powf', |
| 'qsort', |
| 'qsort_r', |
| 'qsort_s', |
| 'realloc', |
| 'round', |
| 'roundf', |
| 'scalbn', |
| 'scalbnf', |
| 'setenv', |
| 'sin', |
| 'sinf', |
| 'snprintf', |
| 'sqrt', |
| 'sqrtf', |
| 'sscanf', |
| 'strcasecmp', |
| 'strchr', |
| 'strcmp', |
| 'strdup', |
| 'strlcat', |
| 'strlcpy', |
| 'strlen', |
| 'strlwr', |
| 'strncasecmp', |
| 'strncmp', |
| 'strrchr', |
| 'strrev', |
| 'strstr', |
| 'strtod', |
| 'strtokr', |
| 'strtol', |
| 'strtoll', |
| 'strtoul', |
| 'strupr', |
| 'tan', |
| 'tanf', |
| 'tolower', |
| 'toupper', |
| 'trunc', |
| 'truncf', |
| 'uitoa', |
| 'ulltoa', |
| 'ultoa', |
| 'utf8strlcpy', |
| 'utf8strlen', |
| 'vasprintf', |
| 'vsnprintf', |
| 'vsscanf', |
| 'wcscasecmp', |
| 'wcscmp', |
| 'wcsdup', |
| 'wcslcat', |
| 'wcslcpy', |
| 'wcslen', |
| 'wcsncasecmp', |
| 'wcsncmp', |
| 'wcsstr' ] |
| |
| |
| reg_comment_remove_content = re.compile('\/\*.*\*/') |
| reg_comment_remove_content2 = re.compile('".*"') |
| reg_comment_remove_content3 = re.compile(':strlen') |
| reg_comment_remove_content4 = re.compile('->free') |
| |
| def find_symbols_in_file(file, regex): |
| |
| allowed_extensions = [ ".c", ".cpp", ".m", ".h", ".hpp", ".cc" ] |
| |
| excluded_paths = [ |
| "src/stdlib", |
| "src/libm", |
| "src/hidapi", |
| "src/video/khronos", |
| "include/SDL3", |
| "build-scripts/gen_audio_resampler_filter.c", |
| "build-scripts/gen_audio_channel_conversion.c" ] |
| |
| filename = pathlib.Path(file) |
| |
| for ep in excluded_paths: |
| if ep in filename.as_posix(): |
| # skip |
| return |
| |
| if filename.suffix not in allowed_extensions: |
| # skip |
| return |
| |
| # print("Parse %s" % file) |
| |
| try: |
| with file.open("r", encoding="UTF-8", newline="") as rfp: |
| parsing_comment = False |
| for l in rfp: |
| l = l.strip() |
| |
| # Get the comment block /* ... */ across several lines |
| match_start = "/*" in l |
| match_end = "*/" in l |
| if match_start and match_end: |
| continue |
| if match_start: |
| parsing_comment = True |
| continue |
| if match_end: |
| parsing_comment = False |
| continue |
| if parsing_comment: |
| continue |
| |
| if regex.match(l): |
| |
| # free() allowed here |
| if "This should NOT be SDL_" in l: |
| continue |
| |
| # double check |
| # Remove one line comment /* ... */ |
| # eg: extern SDL_DECLSPEC SDL_hid_device * SDLCALL SDL_hid_open_path(const char *path, int bExclusive /* = false */); |
| l = reg_comment_remove_content.sub('', l) |
| |
| # Remove strings " ... " |
| l = reg_comment_remove_content2.sub('', l) |
| |
| # :strlen |
| l = reg_comment_remove_content3.sub('', l) |
| |
| # ->free |
| l = reg_comment_remove_content4.sub('', l) |
| |
| if regex.match(l): |
| print("File %s" % filename) |
| print(" %s" % l) |
| print("") |
| |
| except UnicodeDecodeError: |
| print("%s is not text, skipping" % file) |
| except Exception as err: |
| print("%s" % err) |
| |
| def find_symbols_in_dir(path, regex): |
| |
| for entry in path.glob("*"): |
| if entry.is_dir(): |
| find_symbols_in_dir(entry, regex) |
| else: |
| find_symbols_in_file(entry, regex) |
| |
| def main(): |
| str = ".*\\b(" |
| for w in words: |
| str += w + "|" |
| str = str[:-1] |
| str += ")\(" |
| regex = re.compile(str) |
| find_symbols_in_dir(SDL_ROOT, regex) |
| |
| if __name__ == "__main__": |
| |
| parser = argparse.ArgumentParser(fromfile_prefix_chars='@') |
| args = parser.parse_args() |
| |
| try: |
| main() |
| except Exception as e: |
| print(e) |
| exit(-1) |
| |
| exit(0) |
| |
| |