mirror of
https://github.com/Abdess/retroarch_system.git
synced 2026-10-26 21:57:24 -05:00
fix: print found paths as the terminal shows them
The line output printed the --from folder, the files found there and the BIOS folder paths as they are: a name holding ESC drove the terminal. screen_text moves out of the interface section and cleans every line of the check, the copy plan and the unreadable list.
This commit is contained in:
1 parent
3523e46595
commit
246b6c6efc
4 files changed
+60
-29
No files matched your search
+1
-1
@@ -11,7 +11,7 @@
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
$defaultInstallUrl = "https://raw.githubusercontent.com/Abdess/retrobios/main/install.py"
|
||||
$defaultInstallSha256 = "5a7dc2fe2aa2abed511a94b3711f1846461a306ea185cd34407efac6c526cab0"
|
||||
$defaultInstallSha256 = "1097dfe33e56238089af275bfea662d29613ff592f68ecdb427a86b5bcbc3e81"
|
||||
$maximumInstallerBytes = 2MB
|
||||
$installerExit = 0
|
||||
$installer = if ($PSScriptRoot) { Join-Path $PSScriptRoot "install.py" } else { $null }
|
||||
|
||||
+35
-27
@@ -7003,6 +7003,24 @@ _MEMBER_ERRORS = (OSError, EOFError, RuntimeError, NotImplementedError,
|
||||
zipfile.BadZipFile, zlib.error)
|
||||
|
||||
|
||||
# C0 and C1 controls, DEL and lone surrogates: a file, folder or core name
|
||||
# holding ESC would drive the terminal (clear it, or set the clipboard with
|
||||
# OSC 52) instead of being shown.
|
||||
_CONTROLS = re.compile("[\x00-\x1f\x7f-\x9f\ud800-\udfff]")
|
||||
|
||||
|
||||
def screen_text(text: str) -> str:
|
||||
"""Text as a terminal shows it, every control character a "?"."""
|
||||
return _CONTROLS.sub("?", text)
|
||||
|
||||
|
||||
def shown_lines(lines: list[str]) -> list[str]:
|
||||
"""Lines for the terminal, a blank line opening one kept as it is: the
|
||||
paths in them come from folders anyone may have filled."""
|
||||
return [("\n" + screen_text(line[1:])) if line.startswith("\n") else screen_text(line)
|
||||
for line in lines]
|
||||
|
||||
|
||||
class LocalCopy(NamedTuple):
|
||||
"""A file found on this machine with the contents an entry wants."""
|
||||
index: FolderIndex
|
||||
@@ -7016,11 +7034,12 @@ class LocalCopy(NamedTuple):
|
||||
def where(self) -> str:
|
||||
"""The copy as the folder it was found in names it."""
|
||||
rel = self.index.relative(self.path)
|
||||
return f"{rel}, inside as {self.member}" if self.member else rel
|
||||
return screen_text(f"{rel}, inside as {self.member}" if self.member else rel)
|
||||
|
||||
def named(self) -> str:
|
||||
"""The copy by its full path, the member first when it is one."""
|
||||
return f"{self.member} inside {self.path}" if self.member else str(self.path)
|
||||
return screen_text(f"{self.member} inside {self.path}" if self.member
|
||||
else str(self.path))
|
||||
|
||||
|
||||
# The archives a FolderIndex keeps open at once.
|
||||
@@ -7727,7 +7746,7 @@ def check_lines(
|
||||
if len(shown) < len(plain):
|
||||
lines.append(f" ... and {len(plain) - len(shown)} more missing "
|
||||
"(--verbose lists them all)")
|
||||
return lines
|
||||
return shown_lines(lines)
|
||||
|
||||
|
||||
def bios_folder_index(
|
||||
@@ -7829,7 +7848,7 @@ def _check_copy_lines(
|
||||
them = "it" if count == 1 else "them"
|
||||
lines.append(f" {label} holds {count} of these files: a run without --check "
|
||||
f"copies {them}.")
|
||||
return lines
|
||||
return shown_lines(lines)
|
||||
|
||||
|
||||
def _counts_by_folder(
|
||||
@@ -7885,7 +7904,7 @@ def copy_plan_lines(
|
||||
lines.append(f" {entry['dest']}: {why}" if why else f" {entry['dest']}")
|
||||
if len(chosen) > 10 and not every:
|
||||
lines.append(f" ... and {len(chosen) - 10} more (--verbose lists them all)")
|
||||
return lines
|
||||
return shown_lines(lines)
|
||||
|
||||
|
||||
def emit_copies(
|
||||
@@ -7914,7 +7933,7 @@ class Unreadable(NamedTuple):
|
||||
|
||||
def line(self) -> str:
|
||||
where = f"{self.member} inside {self.path}" if self.member else str(self.path)
|
||||
return f"{where} could not be read: {self.why}"
|
||||
return screen_text(f"{where} could not be read: {self.why}")
|
||||
|
||||
|
||||
def unreadable_copies(
|
||||
@@ -7936,7 +7955,7 @@ def unreadable_lines(found: list[Unreadable], every: bool) -> list[str]:
|
||||
if len(found) > len(lines):
|
||||
lines.append(f" ... and {len(found) - len(lines)} more could not be read "
|
||||
"(--verbose lists them all)")
|
||||
return lines
|
||||
return shown_lines(lines)
|
||||
|
||||
|
||||
def emit_unreadable(plat: str, found: list[Unreadable]) -> None:
|
||||
@@ -7951,7 +7970,7 @@ def sources_line(
|
||||
) -> str:
|
||||
"""Where the files of a run that copied some came from, in words:
|
||||
"38 files copied from E:\\, 4 downloaded, 2 still missing." """
|
||||
parts = [(count, f"copied from {label}") for label, count in folders]
|
||||
parts = [(count, f"copied from {screen_text(label)}") for label, count in folders]
|
||||
parts.append((fixed, "fixed from copies already in the BIOS folder"))
|
||||
parts.append((shared, "copied from another place of the BIOS folder"))
|
||||
if not any(count for count, _what in parts):
|
||||
@@ -8515,17 +8534,17 @@ def _open_from_folders(folders: list[Path]) -> None:
|
||||
reason, what = problem
|
||||
message = f"{label} {what}: --from names the folder to copy BIOS files from"
|
||||
EVENTS.emit("error", stage="from", reason=reason, platform="", message=message)
|
||||
print(f"Error: {message}.", file=sys.stderr)
|
||||
print(f"Error: {screen_text(message)}.", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
index = FolderIndex(Path(os.path.expanduser(label)), label)
|
||||
LOCAL.folders.append(index)
|
||||
print(f" Copying BIOS files from: {label}")
|
||||
print(f" Copying BIOS files from: {screen_text(label)}")
|
||||
if index.truncated:
|
||||
print(f" Read the first {MAX_SCANNED_FILES:,} files of {label} only. Name "
|
||||
"the folder that holds the BIOS files to search it all.")
|
||||
print(f" Read the first {MAX_SCANNED_FILES:,} files of {screen_text(label)} "
|
||||
"only. Name the folder that holds the BIOS files to search it all.")
|
||||
for drive in index.other_drives:
|
||||
print(f" Did not go into {drive}: it is another drive. Name it with --from "
|
||||
"to read it too.")
|
||||
print(f" Did not go into {screen_text(str(drive))}: it is another drive. Name "
|
||||
"it with --from to read it too.")
|
||||
EVENTS.emit("scanned", folder=label, files=index.files, truncated=index.truncated,
|
||||
other_drives=[str(drive) for drive in index.other_drives])
|
||||
|
||||
@@ -9701,17 +9720,6 @@ def cell_width(text: str) -> int:
|
||||
return sum(_char_width(char) for char in text)
|
||||
|
||||
|
||||
# C0 and C1 controls, DEL and lone surrogates: a file, folder or core name
|
||||
# holding ESC would drive the terminal (clear it, or set the clipboard with
|
||||
# OSC 52) instead of being shown.
|
||||
_CONTROLS = re.compile("[\x00-\x1f\x7f-\x9f\ud800-\udfff]")
|
||||
|
||||
|
||||
def screen_text(text: str) -> str:
|
||||
"""Text as a terminal shows it, every control character a "?"."""
|
||||
return _CONTROLS.sub("?", text)
|
||||
|
||||
|
||||
def ascii_text(text: str) -> str:
|
||||
"""The text in ASCII, accents dropped (Edition) and any other character
|
||||
as one ? per cell it took, so the columns stay where they were."""
|
||||
@@ -12726,8 +12734,8 @@ def _tui_startup(args: argparse.Namespace) -> tuple[TuiState, list[tuple]] | Non
|
||||
for folder in folders:
|
||||
problem = from_folder_problem(str(folder))
|
||||
if problem is not None:
|
||||
print(f"Error: {folder} {problem[1]}: --from names the folder to copy BIOS "
|
||||
"files from.", file=sys.stderr)
|
||||
print(f"Error: {screen_text(str(folder))} {problem[1]}: --from names the folder "
|
||||
"to copy BIOS files from.", file=sys.stderr)
|
||||
return None
|
||||
return tui_start(args, os_type)
|
||||
|
||||
|
||||
+1
-1
@@ -18,7 +18,7 @@ esac
|
||||
TEMP_INSTALLER=""
|
||||
TEMP_DIRECTORY=""
|
||||
DEFAULT_INSTALL_URL="https://raw.githubusercontent.com/Abdess/retrobios/main/install.py"
|
||||
DEFAULT_INSTALL_SHA256="5a7dc2fe2aa2abed511a94b3711f1846461a306ea185cd34407efac6c526cab0"
|
||||
DEFAULT_INSTALL_SHA256="1097dfe33e56238089af275bfea662d29613ff592f68ecdb427a86b5bcbc3e81"
|
||||
MAX_INSTALLER_BYTES=2097152
|
||||
|
||||
cleanup() {
|
||||
|
||||
@@ -17,6 +17,7 @@ import errno
|
||||
import io
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
import threading
|
||||
import sys
|
||||
@@ -597,6 +598,28 @@ class UnreadableCopies(FromFolder):
|
||||
self.assertIn(line, proc.stdout)
|
||||
self.assertNotIn("notes.txt", proc.stdout)
|
||||
|
||||
def test_names_on_the_card_cannot_drive_the_terminal(self):
|
||||
"""The plain output printed the paths it found as they are: a name
|
||||
holding ESC cleared the screen or set the clipboard (OSC 52)."""
|
||||
hostile = "x\x1b]52;c;eA==\x07\x1b[2J"
|
||||
self.locked(f"{hostile}/scph5501.bin", US)
|
||||
self.keep(f"{hostile}/gba_bios.bin", GBA)
|
||||
(self.dest / f"pcsx{hostile}").mkdir(parents=True)
|
||||
(self.dest / f"pcsx{hostile}" / "extra.bin").write_bytes(EXTRA)
|
||||
for options in (("--check",), ("--dry-run",), ("--check", "--verbose"), ()):
|
||||
with self.subTest(options=options):
|
||||
proc = self.install(*options)
|
||||
self.assertNotIn("Traceback", proc.stderr)
|
||||
for stream in (proc.stdout, proc.stderr):
|
||||
self.assertIsNone(re.search(r"[\x00-\x09\x0b-\x1f\x7f-\x9f]", stream),
|
||||
stream)
|
||||
self.assertIn("x?]52;c;eA==??[2J", proc.stdout)
|
||||
proc = self.run_installer("--manifest", str(self.list), "--no-input",
|
||||
"--from", str(self.root / hostile))
|
||||
self.assertEqual(proc.returncode, 1)
|
||||
self.assertNotIn("\x1b", proc.stderr)
|
||||
self.assertIn("x?]52;c;eA==??[2J", proc.stderr)
|
||||
|
||||
def test_an_unreadable_copy_is_an_event(self):
|
||||
path = self.locked("psx/scph5501.bin", US).resolve()
|
||||
proc = self.install("--check", "--events")
|
||||
|
||||
Reference in new issue
Block a user