mirror of
https://github.com/Abdess/retroarch_system.git
synced 2026-11-08 11:03:46 -06: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
|
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(
|
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]:
|
) -> tuple[list[tuple[int, str]], int]:
|
||||||
"""Filename literals at HEAD the profile does not declare."""
|
"""Filename literals at HEAD the profile does not declare."""
|
||||||
found: list[tuple[int, str]] = []
|
found: list[tuple[int, str]] = []
|
||||||
seen: set[str] = set()
|
seen: set[str] = set()
|
||||||
elided = 0
|
elided = 0
|
||||||
|
code = path.lower().endswith(CODE_SUFFIXES)
|
||||||
for number, line in enumerate(head_lines, start=1):
|
for number, line in enumerate(head_lines, start=1):
|
||||||
for match in _FILE_RE.findall(line):
|
for match in _FILE_RE.finditer(line):
|
||||||
name = os.path.basename(match).casefold()
|
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:
|
if name in known or name in seen:
|
||||||
continue
|
continue
|
||||||
seen.add(name)
|
seen.add(name)
|
||||||
if len(found) >= cap:
|
if len(found) >= cap:
|
||||||
elided += 1
|
elided += 1
|
||||||
continue
|
continue
|
||||||
found.append((number, os.path.basename(match)))
|
found.append((number, literal))
|
||||||
return found, elided
|
return found, elided
|
||||||
|
|
||||||
|
|
||||||
@@ -4050,10 +4122,12 @@ def _detect(
|
|||||||
or []
|
or []
|
||||||
)
|
)
|
||||||
if args.detect_new_files:
|
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 = {
|
at_pin = {
|
||||||
name.casefold()
|
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(
|
new_files.extend(
|
||||||
{"path": path, "line": number, "name": name,
|
{"path": path, "line": number, "name": name,
|
||||||
|
|||||||
@@ -3310,6 +3310,44 @@ class TestDetectNewFiles(unittest.TestCase):
|
|||||||
found, _ = detect_new_files(['#include "driver.cpp"'], set())
|
found, _ = detect_new_files(['#include "driver.cpp"'], set())
|
||||||
self.assertEqual(found, [])
|
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):
|
class TestWatchHashes(unittest.TestCase):
|
||||||
def test_finds_new_sha1(self):
|
def test_finds_new_sha1(self):
|
||||||
|
|||||||
Reference in new issue
Block a user