check_stdlib_usage.py 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  1. #!/usr/bin/env python3
  2. #
  3. # Simple DirectMedia Layer
  4. # Copyright (C) 1997-2023 Sam Lantinga <[email protected]>
  5. #
  6. # This software is provided 'as-is', without any express or implied
  7. # warranty. In no event will the authors be held liable for any damages
  8. # arising from the use of this software.
  9. #
  10. # Permission is granted to anyone to use this software for any purpose,
  11. # including commercial applications, and to alter it and redistribute it
  12. # freely, subject to the following restrictions:
  13. #
  14. # 1. The origin of this software must not be misrepresented; you must not
  15. # claim that you wrote the original software. If you use this software
  16. # in a product, an acknowledgment in the product documentation would be
  17. # appreciated but is not required.
  18. # 2. Altered source versions must be plainly marked as such, and must not be
  19. # misrepresented as being the original software.
  20. # 3. This notice may not be removed or altered from any source distribution.
  21. #
  22. # This script detects use of stdlib function in SDL code
  23. import argparse
  24. import os
  25. import pathlib
  26. import re
  27. import sys
  28. SDL_ROOT = pathlib.Path(__file__).resolve().parents[1]
  29. words = [
  30. 'abs',
  31. 'acos',
  32. 'acosf',
  33. 'asin',
  34. 'asinf',
  35. 'asprintf',
  36. 'atan',
  37. 'atan2',
  38. 'atan2f',
  39. 'atanf',
  40. 'atof',
  41. 'atoi',
  42. 'calloc',
  43. 'ceil',
  44. 'ceilf',
  45. 'copysign',
  46. 'copysignf',
  47. 'cos',
  48. 'cosf',
  49. 'crc32',
  50. 'exp',
  51. 'expf',
  52. 'fabs',
  53. 'fabsf',
  54. 'floor',
  55. 'floorf',
  56. 'fmod',
  57. 'fmodf',
  58. 'free',
  59. 'getenv',
  60. 'isalnum',
  61. 'isalpha',
  62. 'isblank',
  63. 'iscntrl',
  64. 'isdigit',
  65. 'isgraph',
  66. 'islower',
  67. 'isprint',
  68. 'ispunct',
  69. 'isspace',
  70. 'isupper',
  71. 'isxdigit',
  72. 'itoa',
  73. 'lltoa',
  74. 'log10',
  75. 'log10f',
  76. 'logf',
  77. 'lround',
  78. 'lroundf',
  79. 'ltoa',
  80. 'malloc',
  81. 'memalign',
  82. 'memcmp',
  83. 'memcpy',
  84. 'memcpy4',
  85. 'memmove',
  86. 'memset',
  87. 'pow',
  88. 'powf',
  89. 'qsort',
  90. 'realloc',
  91. 'round',
  92. 'roundf',
  93. 'scalbn',
  94. 'scalbnf',
  95. 'setenv',
  96. 'sin',
  97. 'sinf',
  98. 'snprintf',
  99. 'sqrt',
  100. 'sqrtf',
  101. 'sscanf',
  102. 'strcasecmp',
  103. 'strchr',
  104. 'strcmp',
  105. 'strdup',
  106. 'strlcat',
  107. 'strlcpy',
  108. 'strlen',
  109. 'strlwr',
  110. 'strncasecmp',
  111. 'strncmp',
  112. 'strrchr',
  113. 'strrev',
  114. 'strstr',
  115. 'strtod',
  116. 'strtokr',
  117. 'strtol',
  118. 'strtoll',
  119. 'strtoul',
  120. 'strupr',
  121. 'tan',
  122. 'tanf',
  123. 'tolower',
  124. 'toupper',
  125. 'trunc',
  126. 'truncf',
  127. 'uitoa',
  128. 'ulltoa',
  129. 'ultoa',
  130. 'utf8strlcpy',
  131. 'utf8strlen',
  132. 'vasprintf',
  133. 'vsnprintf',
  134. 'vsscanf',
  135. 'wcscasecmp',
  136. 'wcscmp',
  137. 'wcsdup',
  138. 'wcslcat',
  139. 'wcslcpy',
  140. 'wcslen',
  141. 'wcsncasecmp',
  142. 'wcsncmp',
  143. 'wcsstr' ]
  144. reg_comment_remove_content = re.compile('\/\*.*\*/')
  145. reg_comment_remove_content2 = re.compile('".*"')
  146. reg_comment_remove_content3 = re.compile(':strlen')
  147. reg_comment_remove_content4 = re.compile('->free')
  148. def find_symbols_in_file(file, regex):
  149. allowed_extensions = [ ".c", ".cpp", ".m", ".h", ".hpp", ".cc" ]
  150. excluded_paths = [
  151. "src/stdlib",
  152. "src/libm",
  153. "src/hidapi",
  154. "src/video/khronos",
  155. "include/SDL3",
  156. "build-scripts/gen_audio_resampler_filter.c",
  157. "build-scripts/gen_audio_channel_conversion.c" ]
  158. filename = pathlib.Path(file)
  159. for ep in excluded_paths:
  160. if ep in filename.as_posix():
  161. # skip
  162. return
  163. if filename.suffix not in allowed_extensions:
  164. # skip
  165. return
  166. # print("Parse %s" % file)
  167. try:
  168. with file.open("r", encoding="UTF-8", newline="") as rfp:
  169. parsing_comment = False
  170. for l in rfp:
  171. l = l.strip()
  172. # Get the comment block /* ... */ across several lines
  173. match_start = "/*" in l
  174. match_end = "*/" in l
  175. if match_start and match_end:
  176. continue
  177. if match_start:
  178. parsing_comment = True
  179. continue
  180. if match_end:
  181. parsing_comment = False
  182. continue
  183. if parsing_comment:
  184. continue
  185. if regex.match(l):
  186. # free() allowed here
  187. if "This should NOT be SDL_" in l:
  188. continue
  189. # double check
  190. # Remove one line comment /* ... */
  191. # eg: extern DECLSPEC SDL_hid_device * SDLCALL SDL_hid_open_path(const char *path, int bExclusive /* = false */);
  192. l = reg_comment_remove_content.sub('', l)
  193. # Remove strings " ... "
  194. l = reg_comment_remove_content2.sub('', l)
  195. # :strlen
  196. l = reg_comment_remove_content3.sub('', l)
  197. # ->free
  198. l = reg_comment_remove_content4.sub('', l)
  199. if regex.match(l):
  200. print("File %s" % filename)
  201. print(" %s" % l)
  202. print("")
  203. except UnicodeDecodeError:
  204. print("%s is not text, skipping" % file)
  205. except Exception as err:
  206. print("%s" % err)
  207. def find_symbols_in_dir(path, regex):
  208. for entry in path.glob("*"):
  209. if entry.is_dir():
  210. find_symbols_in_dir(entry, regex)
  211. else:
  212. find_symbols_in_file(entry, regex)
  213. def main():
  214. str = ".*\\b("
  215. for w in words:
  216. str += w + "|"
  217. str = str[:-1]
  218. str += ")\("
  219. regex = re.compile(str)
  220. find_symbols_in_dir(SDL_ROOT, regex)
  221. if __name__ == "__main__":
  222. parser = argparse.ArgumentParser(fromfile_prefix_chars='@')
  223. args = parser.parse_args()
  224. try:
  225. main()
  226. except Exception as e:
  227. print(e)
  228. exit(-1)
  229. exit(0)