mirror of
https://github.com/Abdess/retroarch_system.git
synced 2026-10-23 20:29:06 -05:00
fix: read the file a detected literal names
A printf conversion glued to a name, a name with spaces cut to its last word, an extension list, and an identifier in code were reported as new files.
This commit is contained in:
1 parent
5fba72f1a3
commit
97c960484c
2 files changed
+118
-6
No files matched your search
+80
-6
@@ -2300,23 +2300,95 @@ def declared_hashes(profile: dict) -> set[str]:
|
||||
return hashes
|
||||
|
||||
|
||||
# Sources where a filename is a string. Outside a literal or a comment,
|
||||
# `EE.bios` is a config key and `psm_s.pal` a struct field (pcee2).
|
||||
CODE_SUFFIXES = (
|
||||
".c", ".cc", ".cpp", ".cxx", ".h", ".hh", ".hpp", ".hxx", ".inc", ".inl",
|
||||
".m", ".mm", ".rs", ".java", ".kt", ".cs", ".swift",
|
||||
)
|
||||
_PRINTF_SPEC_RE = re.compile(
|
||||
r"[-+ #0]*(?:\d+|\*)?(?:\.\d+)?(?:hh|h|ll|l|z|j|t|L)?[diouxXsScpfFeEgGaA]"
|
||||
)
|
||||
_TITLE_WORD_RE = re.compile(r"[A-Z0-9][\w.()&'+-]*")
|
||||
|
||||
|
||||
def _string_literal(line: str, index: int) -> tuple[int, int] | None:
|
||||
"""Bounds of the double-quoted literal holding index, None outside one."""
|
||||
start = None
|
||||
position = 0
|
||||
while position < len(line):
|
||||
char = line[position]
|
||||
if char == "\\" and start is not None:
|
||||
position += 2
|
||||
continue
|
||||
if char == '"':
|
||||
if start is None:
|
||||
start = position + 1
|
||||
else:
|
||||
if start <= index < position:
|
||||
return start, position
|
||||
start = None
|
||||
position += 1
|
||||
return None
|
||||
|
||||
|
||||
def _in_comment(line: str, index: int) -> bool:
|
||||
before = line[:index]
|
||||
return "//" in before or "/*" in before or line.lstrip().startswith(("*", "#"))
|
||||
|
||||
|
||||
def file_literal(line: str, start: int, end: int, code: bool) -> str | None:
|
||||
"""The filename a match stands for, None when it names no file.
|
||||
|
||||
A printf conversion glued to the name is not part of it: sdl2trs writes
|
||||
`"%slevel2.rom"` with ROM_PATH in front. A name with spaces is read
|
||||
whole: virtualjaguar opens `"Jaguar CD BIOS.rom"`, and the match alone
|
||||
was `BIOS.rom`. Such a name is written as a title, so `"Please insert
|
||||
bios.bin"` and `"Packing patches.zip"` still yield the file alone. In
|
||||
a code file a match outside any string or comment is an identifier and
|
||||
names nothing.
|
||||
"""
|
||||
name = line[start:end]
|
||||
if start and line[start - 1] == "%":
|
||||
spec = _PRINTF_SPEC_RE.match(name)
|
||||
if spec:
|
||||
name = name[spec.end():]
|
||||
literal = _string_literal(line, start)
|
||||
if literal is None:
|
||||
if code and not _in_comment(line, start):
|
||||
return None
|
||||
elif literal[1] == end and line[start - 1:start] == " ":
|
||||
words = line[literal[0]:start].split() + [name]
|
||||
if all(_TITLE_WORD_RE.fullmatch(word) for word in words):
|
||||
return line[literal[0]:end]
|
||||
base = os.path.basename(name)
|
||||
# `".bin.hex.iii.rom"` (sdl2trs) lists extensions, it names no file.
|
||||
if base.startswith(".") or not _FILE_RE.fullmatch(base):
|
||||
return None
|
||||
return base
|
||||
|
||||
|
||||
def detect_new_files(
|
||||
head_lines: list[str], known: set[str], cap: int = 25
|
||||
head_lines: list[str], known: set[str], cap: int = 25, path: str = ""
|
||||
) -> tuple[list[tuple[int, str]], int]:
|
||||
"""Filename literals at HEAD the profile does not declare."""
|
||||
found: list[tuple[int, str]] = []
|
||||
seen: set[str] = set()
|
||||
elided = 0
|
||||
code = path.lower().endswith(CODE_SUFFIXES)
|
||||
for number, line in enumerate(head_lines, start=1):
|
||||
for match in _FILE_RE.findall(line):
|
||||
name = os.path.basename(match).casefold()
|
||||
for match in _FILE_RE.finditer(line):
|
||||
literal = file_literal(line, match.start(), match.end(), code)
|
||||
if literal is None:
|
||||
continue
|
||||
name = literal.casefold()
|
||||
if name in known or name in seen:
|
||||
continue
|
||||
seen.add(name)
|
||||
if len(found) >= cap:
|
||||
elided += 1
|
||||
continue
|
||||
found.append((number, os.path.basename(match)))
|
||||
found.append((number, literal))
|
||||
return found, elided
|
||||
|
||||
|
||||
@@ -4050,10 +4122,12 @@ def _detect(
|
||||
or []
|
||||
)
|
||||
if args.detect_new_files:
|
||||
found, elided = detect_new_files(head_lines, names)
|
||||
found, elided = detect_new_files(head_lines, names, path=path)
|
||||
at_pin = {
|
||||
name.casefold()
|
||||
for _, name in detect_new_files(pin_lines, names, cap=sys.maxsize)[0]
|
||||
for _, name in detect_new_files(
|
||||
pin_lines, names, cap=sys.maxsize, path=path
|
||||
)[0]
|
||||
}
|
||||
new_files.extend(
|
||||
{"path": path, "line": number, "name": name,
|
||||
|
||||
@@ -3310,6 +3310,44 @@ class TestDetectNewFiles(unittest.TestCase):
|
||||
found, _ = detect_new_files(['#include "driver.cpp"'], set())
|
||||
self.assertEqual(found, [])
|
||||
|
||||
def test_a_printf_conversion_is_not_part_of_the_name(self):
|
||||
# sdl2trs src/trs_options.c:864-866 builds "%slevel2.rom" and the
|
||||
# report named slevel2.rom.
|
||||
lines = [
|
||||
'snprintf(romfile1, sizeof(romfile1), "%slevel2.rom", ROM_PATH);',
|
||||
'snprintf(name, 64, "%02dcart.bin", slot);',
|
||||
]
|
||||
found, _ = detect_new_files(lines, set(), path="src/trs_options.c")
|
||||
self.assertEqual(found, [(1, "level2.rom"), (2, "cart.bin")])
|
||||
|
||||
def test_a_quoted_name_with_spaces_is_read_whole(self):
|
||||
# virtualjaguar libretro.c:5815-5816 opens "Jaguar CD BIOS.rom",
|
||||
# reported as BIOS.rom. A sentence still yields the name it holds.
|
||||
lines = [
|
||||
'snprintf(path, n, "%s%c%s", dir, slash, "Jaguar CD BIOS.rom");',
|
||||
'log("Please insert bios.bin first");',
|
||||
'message(STATUS "Packing patches.zip")',
|
||||
'#define ROM ".bin.hex.iii.rom"',
|
||||
]
|
||||
found, _ = detect_new_files(lines, set(), path="libretro.c")
|
||||
self.assertEqual(
|
||||
found,
|
||||
[(1, "Jaguar CD BIOS.rom"), (2, "bios.bin"), (3, "patches.zip")],
|
||||
)
|
||||
|
||||
def test_an_identifier_in_code_is_not_a_file(self):
|
||||
# pcee2 reads config keys EE.bios and IOP.bios and a field psm_s.pal.
|
||||
lines = [
|
||||
"cfg.EE.bios = load(path);",
|
||||
"if (psm_s.pal) {",
|
||||
"// loads boot.rom from the system directory",
|
||||
'open("ipl.rom");',
|
||||
]
|
||||
found, _ = detect_new_files(lines, set(), path="src/core/config.cpp")
|
||||
self.assertEqual(found, [(3, "boot.rom"), (4, "ipl.rom")])
|
||||
found, _ = detect_new_files(["Copy EE.bios here"], set(), path="README.md")
|
||||
self.assertEqual(found, [(1, "EE.bios")])
|
||||
|
||||
|
||||
class TestWatchHashes(unittest.TestCase):
|
||||
def test_finds_new_sha1(self):
|
||||
|
||||
Reference in new issue
Block a user