From 0e1f8f7b34823ed48cf8bb4dadabbbdb77dfb1d6 Mon Sep 17 00:00:00 2001 From: Jan Verbeek Date: Sun, 29 Aug 2021 20:09:58 +0200 Subject: [PATCH] Move verbatim path printing to uucore::Display --- src/uu/pwd/src/pwd.rs | 31 +++++------------------------- src/uucore/src/lib/mods/display.rs | 30 +++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 26 deletions(-) diff --git a/src/uu/pwd/src/pwd.rs b/src/uu/pwd/src/pwd.rs index e8f0c0013..1138dba8e 100644 --- a/src/uu/pwd/src/pwd.rs +++ b/src/uu/pwd/src/pwd.rs @@ -10,9 +10,10 @@ extern crate uucore; use clap::{crate_version, App, Arg}; use std::env; -use std::io::{self, Write}; -use std::path::{Path, PathBuf}; +use std::io; +use std::path::PathBuf; +use uucore::display::println_verbatim; use uucore::error::{FromIo, UResult}; static ABOUT: &str = "Display the full filename of the current working directory."; @@ -57,6 +58,7 @@ fn logical_path() -> io::Result { // POSIX: https://pubs.opengroup.org/onlinepubs/9699919799/utilities/pwd.html #[cfg(not(windows))] { + use std::path::Path; fn looks_reasonable(path: &Path) -> bool { // First, check if it's an absolute path. if !path.has_root() { @@ -148,30 +150,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { .map(Into::into) .unwrap_or(cwd); - print_path(&cwd).map_err_context(|| "failed to print current directory".to_owned())?; - - Ok(()) -} - -fn print_path(path: &Path) -> io::Result<()> { - let stdout = io::stdout(); - let mut stdout = stdout.lock(); - - // On Unix we print non-lossily. - #[cfg(unix)] - { - use std::os::unix::ffi::OsStrExt; - stdout.write_all(path.as_os_str().as_bytes())?; - stdout.write_all(b"\n")?; - } - - // On other platforms we potentially mangle it. - // There might be some clever way to do it correctly on Windows, but - // invalid unicode in filenames is rare there. - #[cfg(not(unix))] - { - writeln!(stdout, "{}", path.display())?; - } + println_verbatim(&cwd).map_err_context(|| "failed to print current directory".to_owned())?; Ok(()) } diff --git a/src/uucore/src/lib/mods/display.rs b/src/uucore/src/lib/mods/display.rs index 42e9fceba..b941a4b15 100644 --- a/src/uucore/src/lib/mods/display.rs +++ b/src/uucore/src/lib/mods/display.rs @@ -5,6 +5,9 @@ /// will wrap quotes around the filename and add the necessary escapes to make /// it copy/paste-able into a shell. /// +/// For writing raw paths to stdout when the output should not be quoted or escaped, +/// use `println_verbatim`. This will preserve invalid unicode. +/// /// # Examples /// ``` /// use std::path::Path; @@ -13,6 +16,7 @@ /// let path = Path::new("foo/bar.baz"); /// /// println!("Found file {}", path.quote()); // Prints "Found file 'foo/bar.baz'" +/// println_verbatim(path)?; // Prints "foo/bar.baz" /// # Ok::<(), std::io::Error>(()) /// ``` // spell-checker:ignore Fbar @@ -20,6 +24,7 @@ use std::ffi::OsStr; #[cfg(any(unix, target_os = "wasi", windows))] use std::fmt::Write as FmtWrite; use std::fmt::{self, Display, Formatter}; +use std::io::{self, Write as IoWrite}; #[cfg(unix)] use std::os::unix::ffi::OsStrExt; @@ -268,6 +273,31 @@ fn from_utf8_iter(mut bytes: &[u8]) -> impl Iterator> { }) } +/// Print a path (or `OsStr`-like object) directly to stdout, with a trailing newline, +/// without losing any information if its encoding is invalid. +/// +/// This function is appropriate for commands where printing paths is the point and the +/// output is likely to be captured, like `pwd` and `basename`. For informational output +/// use `Quotable::quote`. +/// +/// FIXME: This is lossy on Windows. It could probably be implemented using some low-level +/// API that takes UTF-16, without going through io::Write. This is not a big priority +/// because broken filenames are much rarer on Windows than on Unix. +pub fn println_verbatim>(text: S) -> io::Result<()> { + let stdout = io::stdout(); + let mut stdout = stdout.lock(); + #[cfg(any(unix, target_os = "wasi"))] + { + stdout.write_all(text.as_ref().as_bytes())?; + stdout.write_all(b"\n")?; + } + #[cfg(not(any(unix, target_os = "wasi")))] + { + writeln!(stdout, "{}", std::path::Path::new(text.as_ref()).display())?; + } + Ok(()) +} + #[cfg(test)] mod tests { use super::*;