[librsvg] (#426): Make rsvg_rust_handle_new_from_file() work on Windows and the latest glib
- From: Federico Mena Quintero <federico src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [librsvg] (#426): Make rsvg_rust_handle_new_from_file() work on Windows and the latest glib
- Date: Thu, 14 Mar 2019 02:08:45 +0000 (UTC)
commit d966f3b9c9c4ced3fd395478c1e9a74fd89b61d6
Author: Federico Mena Quintero <federico gnome org>
Date: Wed Mar 13 20:02:49 2019 -0600
(#426): Make rsvg_rust_handle_new_from_file() work on Windows and the latest glib
This commit introduces a PathOrUrl enum, which gets created out of
a *const libc::c_char. The idea is that it will figure out whether
the string that comes from C represents a path or a URL, so the code
can decide whether to call gio::File::new_for_path() or
gio::File::new_for_uri().
Thanks to Kleis Auke Wolthuizen for actually making this work :)
Fixes https://gitlab.gnome.org/GNOME/librsvg/issues/426
rsvg_internals/src/c_api.rs | 111 +++++++++++++++++++++++++++++++++++++++-----
1 file changed, 100 insertions(+), 11 deletions(-)
---
diff --git a/rsvg_internals/src/c_api.rs b/rsvg_internals/src/c_api.rs
index b167d4dc..1d45e1db 100644
--- a/rsvg_internals/src/c_api.rs
+++ b/rsvg_internals/src/c_api.rs
@@ -22,7 +22,7 @@ use gobject_sys::{self, GEnumValue, GFlagsValue};
use crate::dpi::Dpi;
use crate::drawing_ctx::RsvgRectangle;
-use crate::error::{set_gerror, RSVG_ERROR_FAILED};
+use crate::error::{set_gerror, LoadingError, RSVG_ERROR_FAILED};
use crate::handle::{Handle, LoadFlags, LoadState};
use crate::length::RsvgLength;
use url::Url;
@@ -783,18 +783,16 @@ pub unsafe extern "C" fn rsvg_rust_handle_new_from_file(
filename: *const libc::c_char,
error: *mut *mut glib_sys::GError,
) -> *const RsvgHandle {
- // This API lets the caller pass a URI, or a file name in the operating system's
- // encoding. So, first we'll see if it's UTF-8, and in that case, try the URL version.
- // Otherwise, we'll try building a path name.
+ let file = match PathOrUrl::new(filename) {
+ Ok(PathOrUrl::Path(path)) => gio::File::new_for_path(path),
- let cstr = CStr::from_ptr(filename);
+ Ok(PathOrUrl::Url(url)) => gio::File::new_for_uri(url.as_str()),
- let file = cstr
- .to_str()
- .map_err(|_| ())
- .and_then(|utf8| Url::parse(utf8).map_err(|_| ()))
- .and_then(|url| Ok(gio::File::new_for_uri(url.as_str())))
- .unwrap_or_else(|_| gio::File::new_for_path(PathBuf::from_glib_none(filename)));
+ Err(e) => {
+ set_gerror(error, 0, &format!("{}", e));
+ return ptr::null_mut();
+ }
+ };
rsvg_rust_handle_new_from_gfile_sync(file.to_glib_none().0, 0, ptr::null_mut(), error)
}
@@ -930,3 +928,94 @@ pub unsafe extern "C" fn rsvg_rust_handle_get_intrinsic_dimensions(
set_out_param(out_has_height, out_height, &h);
set_out_param(out_has_viewbox, out_viewbox, &r);
}
+
+/// Detects whether a `*const libc::c_char` is a path or a URI
+///
+/// `rsvg_handle_new_from_file()` takes a `filename` argument, and advertises
+/// that it will detect either a file system path, or a proper URI. It will then use
+/// `gio::File::new_for_path()` or `gio::File::new_for_uri()` as appropriate.
+///
+/// This enum does the magic heuristics to figure this out.
+enum PathOrUrl {
+ Path(PathBuf),
+ Url(Url),
+}
+
+impl PathOrUrl {
+ unsafe fn new(s: *const libc::c_char) -> Result<PathOrUrl, LoadingError> {
+ let cstr = CStr::from_ptr(s);
+
+ Ok(cstr
+ .to_str()
+ .map_err(|_| ())
+ .and_then(|utf8| Url::parse(utf8).map_err(|_| ()))
+ .and_then(|url| {
+ if url.origin().is_tuple() || url.scheme() == "file" {
+ Ok(PathOrUrl::Url(url))
+ } else {
+ Ok(PathOrUrl::Path(url.to_file_path()?))
+ }
+ })
+ .unwrap_or_else(|_| PathOrUrl::Path(PathBuf::from_glib_none(s))))
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn path_or_url_unix() {
+ unsafe {
+ match PathOrUrl::new(b"/foo/bar\0" as *const u8 as *const _).unwrap() {
+ PathOrUrl::Path(_) => (),
+ _ => panic!("unix filename should be a PathOrUrl::Path"),
+ }
+
+ match PathOrUrl::new(b"foo/bar\0" as *const u8 as *const _).unwrap() {
+ PathOrUrl::Path(_) => (),
+ _ => panic!("unix filename should be a PathOrUrl::Path"),
+ }
+ }
+ }
+
+ #[test]
+ fn path_or_url_windows() {
+ unsafe {
+ match PathOrUrl::new(b"c:/foo/bar\0" as *const u8 as *const _).unwrap() {
+ PathOrUrl::Path(_) => (),
+ _ => panic!("windows filename should be a PathOrUrl::Path"),
+ }
+
+ match PathOrUrl::new(b"C:/foo/bar\0" as *const u8 as *const _).unwrap() {
+ PathOrUrl::Path(_) => (),
+ _ => panic!("windows filename should be a PathOrUrl::Path"),
+ }
+ }
+ }
+
+ #[test]
+ fn path_or_url_unix_url() {
+ unsafe {
+ match PathOrUrl::new(b"file:///foo/bar\0" as *const u8 as *const _).unwrap() {
+ PathOrUrl::Url(_) => (),
+ _ => panic!("file:// unix filename should be a PathOrUrl::Url"),
+ }
+ }
+ }
+
+ #[test]
+ fn path_or_url_windows_url() {
+ unsafe {
+ match PathOrUrl::new(b"file://c:/foo/bar\0" as *const u8 as *const _).unwrap() {
+ PathOrUrl::Url(_) => (),
+ _ => panic!("file:// windows filename should be a PathOrUrl::Url"),
+ }
+
+ match PathOrUrl::new(b"file://C:/foo/bar\0" as *const u8 as *const _).unwrap() {
+ PathOrUrl::Url(_) => (),
+ _ => panic!("file:// windows filename should be a PathOrUrl::Url"),
+ }
+ }
+ }
+}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]