mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-11 13:04:50 +00:00
Automation roots: refuse COM¹ to LPT³ as Windows device names
This commit is contained in:
committed by
bflatastic
parent
91efec6cec
commit
a455e72bf8
@@ -11,7 +11,7 @@
|
||||
//! folder, the rest of the path being plain names (`..` below a missing folder is refused, and so
|
||||
//! is a link that leads nowhere).
|
||||
//! - The result must lie inside one of the roots of its kind, compared by whole components
|
||||
//! (`/work2` isn't inside `/work`), without regard to case on Windows.
|
||||
//! (`/work2` isn't inside `/work`), exactly apart from the case of a Windows drive or share prefix.
|
||||
//! - Read and write access are separate: a root given alone grants only its own kind of access.
|
||||
//!
|
||||
//! Roots are in force on a thread inside [`confine`], and in the whole process after
|
||||
@@ -260,7 +260,7 @@ fn resolve(path: &str) -> Result<PathBuf, String> {
|
||||
}
|
||||
|
||||
/// Is `path` (resolved) `root` or inside it, component by component (without regard to case in a
|
||||
/// Windows drive prefix only: resolved folder names have their real case, and a folder that differs
|
||||
/// Windows drive or share prefix only: resolved folder names have their real case, and a folder that differs
|
||||
/// in case alone may be another, on a case-sensitive Windows folder)?
|
||||
fn inside(path: &Path, root: &Path) -> bool {
|
||||
let mut path = path.components();
|
||||
@@ -280,10 +280,11 @@ fn same_component(a: Component, b: Component) -> bool {
|
||||
}
|
||||
}
|
||||
|
||||
/// A Windows device name (`CON`, `NUL`, `COM1`… also with an extension), which names no file.
|
||||
/// A Windows device name (`CON`, `NUL`, `COM1`, `COM¹`… also with an extension), which names no file.
|
||||
pub(crate) fn is_device_name(component: &str) -> bool {
|
||||
let stem = component.split('.').next().unwrap_or(component).trim_end_matches(' ').to_ascii_uppercase();
|
||||
let numbered = |prefix: &str| stem.strip_prefix(prefix).is_some_and(|n| matches!(n, "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"));
|
||||
let numbered =
|
||||
|prefix: &str| stem.strip_prefix(prefix).is_some_and(|n| matches!(n, "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" | "¹" | "²" | "³"));
|
||||
matches!(stem.as_str(), "CON" | "PRN" | "AUX" | "NUL" | "CLOCK$" | "CONIN$" | "CONOUT$") || numbered("COM") || numbered("LPT")
|
||||
}
|
||||
|
||||
@@ -499,6 +500,8 @@ mod tests {
|
||||
let e = roots.check(&s(base.join("out/NUL")), Access::Write).unwrap_err();
|
||||
assert!(e.contains("reserved device names"), "{e}");
|
||||
assert!(roots.check(&s(base.join("out/com1.txt")), Access::Write).is_err());
|
||||
let e = roots.check(&s(base.join("out/COM¹.txt")), Access::Write).unwrap_err();
|
||||
assert!(e.contains("reserved device names"), "{e}");
|
||||
assert_eq!(roots.check(&s(base.join("out/console.txt")), Access::Write), Ok(()));
|
||||
let _ = std::fs::remove_dir_all(base);
|
||||
}
|
||||
@@ -527,10 +530,10 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn device_names() {
|
||||
for d in ["CON", "nul", "Com1", "LPT9.txt", "aux.svg", "NUL "] {
|
||||
for d in ["CON", "nul", "Com1", "LPT9.txt", "aux.svg", "NUL ", "COM¹", "Lpt²", "lpt³.txt"] {
|
||||
assert!(is_device_name(d), "{d}");
|
||||
}
|
||||
for f in ["console", "com10", "COM0", "lpt", "a.svg", "nul-x"] {
|
||||
for f in ["console", "com10", "COM0", "lpt", "a.svg", "nul-x", "COM⁴", "com¹0", "lpt¹x"] {
|
||||
assert!(!is_device_name(f), "{f}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -769,9 +769,17 @@ fn a_pattern_library_adds_only_the_picked_pattern_and_it_undoes_and_saves() {
|
||||
#[test]
|
||||
fn a_library_file_name_is_never_a_windows_device_name() {
|
||||
use crate::cmd::swatchlib::library_file_name;
|
||||
for (title, ext) in
|
||||
[("CON", "json"), ("nul", "json"), ("Com1", "acb"), ("LPT9", "ase"), ("aux.", "json"), ("CON.txt", "json"), ("nul.tar.gz", "acb")]
|
||||
{
|
||||
for (title, ext) in [
|
||||
("CON", "json"),
|
||||
("nul", "json"),
|
||||
("Com1", "acb"),
|
||||
("LPT9", "ase"),
|
||||
("aux.", "json"),
|
||||
("CON.txt", "json"),
|
||||
("nul.tar.gz", "acb"),
|
||||
("COM¹", "json"),
|
||||
("lpt³.x", "ase"),
|
||||
] {
|
||||
let file = library_file_name(title, ext);
|
||||
assert!(!crate::file_access::is_device_name(&file), "{title}: {file}");
|
||||
assert!(
|
||||
|
||||
Reference in New Issue
Block a user