HP-GL: a .plt file needs at least one instruction the reader knows

A file read as HP-GL by its extension that holds only instructions of
another set (`GC5;`) is now refused as "not an HP-GL file" instead of
opening as an empty plot. At least one of the instructions the check
reads must be one the reader knows.
This commit is contained in:
dotCooCoo
2026-11-11 18:05:05 -08:00
parent 64988aaa49
commit 925300c159
2 changed files with 14 additions and 5 deletions
+9 -5
View File
@@ -71,16 +71,18 @@ pub fn is_hpgl(bytes: &[u8]) -> bool {
/// Does `bytes` hold HP-GL instructions as [`is_hpgl`] checks? When `strict` is false, any of them
/// may come first: a file picked by its extension may start with one a plot file seldom starts
/// with (`SI`, `DI`).
/// with (`SI`, `DI`). Either way, at least one of the instructions checked must be one the reader
/// knows.
fn holds_instructions(bytes: &[u8], strict: bool) -> bool {
let mut lex = Lexer::new(bytes);
let mut terminator = lex::ETX;
let mut first = 0;
let mut any_known = false;
for n in 0..8 {
if n > 0 && lex.pos() > first + 4096 {
return true;
return any_known;
}
let Some(op) = lex.op(true) else { return n > 0 && lex.done() };
let Some(op) = lex.op(true) else { return n > 0 && any_known && lex.done() };
if n == 0 {
first = lex.pos();
}
@@ -88,6 +90,7 @@ fn holds_instructions(bytes: &[u8], strict: bool) -> bool {
return false;
}
let known = PLAYED.contains(&op) || IGNORED.contains(&op);
any_known |= known;
match &op {
b"CO" => {
if !lex.comment() {
@@ -114,11 +117,12 @@ fn holds_instructions(bytes: &[u8], strict: bool) -> bool {
}
}
}
true
any_known
}
/// Read an HP-GL file. It may start with any instruction the reader knows, or one of another set
/// that a semicolon ends; the rest of [`is_hpgl`]'s check applies.
/// that a semicolon ends; the rest of [`is_hpgl`]'s check applies, and at least one instruction the
/// reader knows must be among those it checks.
pub fn import(bytes: &[u8]) -> Result<Imported, String> {
if !holds_instructions(bytes, false) {
return Err("not an HP-GL file".into());
+5
View File
@@ -451,6 +451,11 @@ fn a_plot_file_may_start_with_any_instruction() {
for f in [&b"GC5"[..], b"SIhello world", b"set terminal png\n"] {
assert_eq!(import(f).err().as_deref(), Some("not an HP-GL file"), "{:?}", String::from_utf8_lossy(f));
}
// Instructions of another set alone, however many or long, are not taken for HP-GL.
let long = [&b"GC"[..], "1".repeat(5000).as_bytes(), b";ZZ1;"].concat();
for f in [&b"GC5;"[..], b"GC5;ZZ1;", b"GC5;ZZ1;YY2;XX3;WW4;VV5;UU6;TT7;", &long] {
assert_eq!(import(f).err().as_deref(), Some("not an HP-GL file"), "{:?}", String::from_utf8_lossy(f));
}
}
#[test]