What happens
plan_names refuses two entries that would be written under one name, which is the guarantee that stops a rename from quietly destroying a file. The comparison is byte for byte.
Windows and macOS default filesystems are case-insensitive. Two entries whose planned names differ only in case are one file there, and the guard does not see it.
Reproduction
rewrite_entry("Report.txt") = "Report.txt"
rewrite_entry("report.txt") = "report.txt"
byte-equal? false -> no collision reported
Both are then written, the second over the first, and the returned listing names two files where the disk holds one. The user is told both were extracted.
Where it bites hardest
A rename can create the collision, which is the case the guard exists for. WHAT?.txt answered with _ and what_.txt already in the archive collide on Windows and not in the comparison, so the very substitution the user was asked to supply is what loses their data.
Note on scope
Case folding is not a one-liner and should not pretend to be. Simple ASCII lowercasing covers the realistic archive and is honest about its limits; full Unicode case folding is a different conversation, and the Turkish dotless i is the usual reason. Worth deciding which one this guard promises, and saying so in the doc comment either way.
The rules are already data (NameRules::windows() versus ::unix()), so case-insensitivity belongs there as another field rather than as a #[cfg], which is what keeps it testable from a Mac.
Found while auditing the Windows CI failure that led to #88.
What happens
plan_namesrefuses two entries that would be written under one name, which is the guarantee that stops a rename from quietly destroying a file. The comparison is byte for byte.Windows and macOS default filesystems are case-insensitive. Two entries whose planned names differ only in case are one file there, and the guard does not see it.
Reproduction
Both are then written, the second over the first, and the returned listing names two files where the disk holds one. The user is told both were extracted.
Where it bites hardest
A rename can create the collision, which is the case the guard exists for.
WHAT?.txtanswered with_andwhat_.txtalready in the archive collide on Windows and not in the comparison, so the very substitution the user was asked to supply is what loses their data.Note on scope
Case folding is not a one-liner and should not pretend to be. Simple ASCII lowercasing covers the realistic archive and is honest about its limits; full Unicode case folding is a different conversation, and the Turkish dotless i is the usual reason. Worth deciding which one this guard promises, and saying so in the doc comment either way.
The rules are already data (
NameRules::windows()versus::unix()), so case-insensitivity belongs there as another field rather than as a#[cfg], which is what keeps it testable from a Mac.Found while auditing the Windows CI failure that led to #88.