Rewrite wrealpath from wutil in Rust (#9613)

* wutil: Rewrite `wrealpath` in Rust

* Reduce use of FFI types in `wrealpath`

* Addressed PR comments regarding allocation

* Replace let binding assignment with regular comparison
This commit is contained in:
Victor Song 2023-02-26 22:13:40 -05:00 committed by GitHub
parent 6f5be9bae4
commit c7ea768a74
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 92 additions and 2 deletions

View File

@ -91,6 +91,8 @@ include_cpp! {
generate!("re::regex_t")
generate!("re::regex_result_ffi")
generate!("re::try_compile_ffi")
generate!("wcs2string")
generate!("str2wcstring")
}
impl parser_t {

View File

@ -143,3 +143,15 @@ impl WCharFromFFI<WString> for cxx::SharedPtr<cxx::CxxWString> {
WString::from_chars(self.as_chars())
}
}
impl WCharFromFFI<Vec<u8>> for cxx::UniquePtr<cxx::CxxString> {
fn from_ffi(&self) -> Vec<u8> {
self.as_bytes().to_vec()
}
}
impl WCharFromFFI<Vec<u8>> for cxx::SharedPtr<cxx::CxxString> {
fn from_ffi(&self) -> Vec<u8> {
self.as_bytes().to_vec()
}
}

View File

@ -1,12 +1,14 @@
pub mod format;
pub mod gettext;
mod wcstoi;
mod wrealpath;
use std::io::Write;
pub(crate) use format::printf::sprintf;
pub(crate) use gettext::{wgettext, wgettext_fmt};
pub use wcstoi::*;
pub use wrealpath::*;
/// Port of the wide-string wperror from `src/wutil.cpp` but for rust `&str`.
pub fn perror(s: &str) {

View File

@ -0,0 +1,74 @@
use std::{
ffi::OsStr,
fs::canonicalize,
os::unix::prelude::{OsStrExt, OsStringExt},
};
use cxx::let_cxx_string;
use crate::{
ffi::{str2wcstring, wcs2string},
wchar::{wstr, WString},
wchar_ffi::{WCharFromFFI, WCharToFFI},
};
/// Wide character realpath. The last path component does not need to be valid. If an error occurs,
/// `wrealpath()` returns `None`
pub fn wrealpath(pathname: &wstr) -> Option<WString> {
if pathname.is_empty() {
return None;
}
let mut narrow_path: Vec<u8> = wcs2string(&pathname.to_ffi()).from_ffi();
// Strip trailing slashes. This is treats "/a//" as equivalent to "/a" if /a is a non-directory.
while narrow_path.len() > 1 && narrow_path[narrow_path.len() - 1] == b'/' {
narrow_path.pop();
}
// `from_bytes` is Unix specific but there isn't really any other way to do this
// since `libc::realpath` is also Unix specific. I also don't think we support Windows
// outside of WSL + Cygwin (which should be fairly Unix-like anyways)
let narrow_res = canonicalize(OsStr::from_bytes(&narrow_path));
let real_path = if let Ok(result) = narrow_res {
result.into_os_string().into_vec()
} else {
// Check if everything up to the last path component is valid.
let pathsep_idx = narrow_path.iter().rposition(|&c| c == b'/');
if pathsep_idx == Some(0) {
// If the only pathsep is the first character then it's an absolute path with a
// single path component and thus doesn't need conversion.
narrow_path
} else {
// Only call realpath() on the portion up to the last component.
let narrow_res = if let Some(pathsep_idx) = pathsep_idx {
// Only call realpath() on the portion up to the last component.
canonicalize(OsStr::from_bytes(&narrow_path[0..pathsep_idx]))
} else {
// If there is no "/", this is a file in $PWD, so give the realpath to that.
canonicalize(".")
};
let Ok(narrow_result) = narrow_res else { return None; };
let pathsep_idx = pathsep_idx.map_or(0, |idx| idx + 1);
let mut real_path = narrow_result.into_os_string().into_vec();
// This test is to deal with cases such as /../../x => //x.
if real_path.len() > 1 {
real_path.push(b'/');
}
real_path.extend_from_slice(&narrow_path[pathsep_idx..]);
real_path
}
};
let_cxx_string!(s = real_path);
Some(str2wcstring(&s).from_ffi())
}

View File

@ -289,10 +289,10 @@ void show_stackframe(int frame_count = 100, int skip_levels = 0);
///
/// This function encodes illegal character sequences in a reversible way using the private use
/// area.
wcstring str2wcstring(const char *in);
wcstring str2wcstring(const char *in, size_t len);
wcstring str2wcstring(const std::string &in);
wcstring str2wcstring(const std::string &in, size_t len);
wcstring str2wcstring(const char *in);
wcstring str2wcstring(const char *in, size_t len);
/// Returns a newly allocated multibyte character string equivalent of the specified wide character
/// string.