From 1a18e8815cb18c2deb47aac542dc68b7889a59c9 Mon Sep 17 00:00:00 2001 From: Sander Saares Date: Thu, 11 Jul 2024 10:59:49 +0300 Subject: [PATCH] Fix gnuplot --version encoding handling on Windows (#755) In some configurations, `gnuplot --version` will emit output as UTF-16 bytes instead of UTF-8. This is not expected by `parse_version()` which always expects UTF-8. This change enhances `parse_version()` with a simple fallback - if UTF-8 parsing fails, it will try again with UTF-16. --- CHANGELOG.md | 4 ++++ plot/src/lib.rs | 27 +++++++++++++++++++++++++-- 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 29d419a3..ec47a13c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## [Unreleased] - MSRV bumped to 1.70 +### Fixed + +- gnuplot version is now correctly detected when using certain Windows binaries/configurations that used to fail + ## [0.5.1] - 2023-05-26 ### Fixed diff --git a/plot/src/lib.rs b/plot/src/lib.rs index 8f31dab6..c4953c2f 100644 --- a/plot/src/lib.rs +++ b/plot/src/lib.rs @@ -995,9 +995,32 @@ pub fn version() -> Result { return Err(VersionError::Error(error)); } - let output = String::from_utf8(command_output.stdout).map_err(|_| VersionError::OutputError)?; + parse_version_utf8(&command_output.stdout).or_else(|utf8_err| { + // gnuplot can emit UTF-16 on some systems/configurations (e.g. some Windows machines). + // If we failed to parse as UTF-8, try again as UTF-16 to account for this. + // If UTF-16 parsing also fails, return the original error we got for UTF-8 to avoid confusing matters more. + parse_version_utf16(&command_output.stdout).map_err(|_| utf8_err) + }) +} + +fn parse_version_utf8(output_bytes: &[u8]) -> Result { + let output = str::from_utf8(output_bytes).map_err(|_| VersionError::OutputError)?; + parse_version(output).map_err(|_| VersionError::ParseError(output.to_owned())) +} + +fn parse_version_utf16(output_bytes: &[u8]) -> Result { + if output_bytes.len() % 2 != 0 { + // Not an even number of bytes, so cannot be UTF-16. + return Err(VersionError::OutputError); + } + + let output_as_u16: Vec = output_bytes + .chunks_exact(2) + .map(|chunk| u16::from_le_bytes([chunk[0], chunk[1]])) + .collect(); - parse_version(&output).map_err(|_| VersionError::ParseError(output.clone())) + let output = String::from_utf16(&output_as_u16).map_err(|_| VersionError::OutputError)?; + parse_version(&output).map_err(|_| VersionError::ParseError(output.to_owned())) } fn parse_version(version_str: &str) -> Result> {