mirror of
https://github.com/storytold/gridcraft.git
synced 2026-10-24 02:36:48 -06:00
Merge pull request #1947 from Mouhand-Kaddo:fix/xls-dialog-sheets
This commit is contained in:
@@ -454,6 +454,20 @@ fn hostile_xls_input_never_panics_or_hangs() {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dialog_sheets_are_skipped_not_read_as_worksheets() {
|
||||
let boundsheet = |pos: u32, name: &str| rec(0x0085, &cat(&[&pos.to_le_bytes(), &[0, 0, name.len() as u8, 0], name.as_bytes()]));
|
||||
let globals = |positions: [u32; 2]| cat(&[&bof(0x0005), &boundsheet(positions[0], "Dialog1"), &boundsheet(positions[1], "Sheet1"), &eof()]);
|
||||
let dialog = cat(&[&bof(0x0010), &rec(0x0081, &[0x10, 0x00]), &rec(0x0203, &cat(&[&cell(0, 0, 0), &1.0f64.to_le_bytes()])), &eof()]);
|
||||
let worksheet = cat(&[&bof(0x0010), &rec(0x0081, &[0x01, 0x04]), &rec(0x0203, &cat(&[&cell(0, 0, 0), &7.0f64.to_le_bytes()])), &eof()]);
|
||||
let first = globals([0; 2]).len() as u32;
|
||||
let stream = cat(&[&globals([first, first + dialog.len() as u32]), &dialog, &worksheet]);
|
||||
let (wb, report) = read_xls(&compound("Workbook", &stream)).unwrap();
|
||||
assert_eq!(wb.sheets.iter().map(|s| s.name.as_str()).collect::<Vec<_>>(), ["Sheet1"]);
|
||||
assert_eq!(v(&wb, 0, "A1"), Value::Number(7.0));
|
||||
assert!(report.warnings.iter().any(|w| w.contains("Dialog sheets")), "{:?}", report.warnings);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sheets_share_one_record_budget() {
|
||||
// Many BoundSheet records point into one long substream (at successive nested BOFs), so each
|
||||
|
||||
@@ -40,6 +40,9 @@ const MAX_RECORDS: usize = 40_000_000;
|
||||
const BOF: u16 = 0x0809;
|
||||
const EOF: u16 = 0x000A;
|
||||
const BIFF8: u16 = 0x0600;
|
||||
/// WsBool (MS-XLS 2.4.351): its first byte's `fDialog` bit marks a dialog sheet, whose BOF says worksheet.
|
||||
const WSBOOL: u16 = 0x0081;
|
||||
const WSBOOL_DIALOG: u8 = 0x10;
|
||||
|
||||
pub(crate) fn invalid(msg: impl Into<String>) -> IoError {
|
||||
IoError::Format(format!("XLS: {}", msg.into()))
|
||||
@@ -524,6 +527,7 @@ fn worksheet(
|
||||
continue;
|
||||
}
|
||||
_ if depth > 1 => continue,
|
||||
WSBOOL if rec.parts.first().and_then(|p| p.first()).is_some_and(|b| b & WSBOOL_DIALOG != 0) => return Ok(None),
|
||||
_ => {}
|
||||
}
|
||||
sheet_record(&rec, &mut st, budget, report)?;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# File-format parity
|
||||
|
||||
> **Last reviewed:** 2026-10-11 · **Last updated:** 2026-10-14 · **Change:** trivial (formulas over Excel's 8,192 characters, and `%` or call chains past the nesting limit, are parse errors instead of stack overflows when a file is opened; previously trivial: XLSX chart sheets are kept on save (#727)) · **Target:** Microsoft Excel (Microsoft 365), Excel for Mac 16.113.4
|
||||
> **Last reviewed:** 2026-10-11 · **Last updated:** 2026-10-19 · **Change:** minor (.xls dialog sheets are skipped instead of read as worksheets) · **Target:** Microsoft Excel (Microsoft 365), Excel for Mac 16.113.4
|
||||
|
||||
Every format Excel opens or saves, with GridCraft's read and write support, fidelity and tests.
|
||||
Part of [target-app-parity.md](target-app-parity.md). **File formats: ~50% ready, 80–120 h**
|
||||
@@ -85,6 +85,7 @@ refresh. Counted under features (Get & Transform, ~10% ready), not here.
|
||||
|
||||
| Date | Change | Summary |
|
||||
|---|---|---|
|
||||
| 2026-10-19 | minor | .xls import skips dialog sheets (WsBool `fDialog`), which share the worksheet BOF type and were read as empty worksheets |
|
||||
| 2026-10-14 | trivial | Formula parser: text longer than Excel's 8,192 characters, and postfix chains (`1%%%…`, `f(1)(1)…`) past the 256-level nesting limit, are parse errors (the cell keeps its formula text and cached value) instead of trees deep enough to overflow the stack when a hostile file is opened, saved or dropped |
|
||||
| 2026-10-12 | trivial | XLSX chart sheets are kept on save (#727) |
|
||||
| 2026-10-12 | trivial | CSV and TSV export is capped at 512 MiB and refuses a larger used range with an error (#528) |
|
||||
|
||||
Reference in New Issue
Block a user