Add printf!/eprintf! convenience wrappers around fprintf!

We often want to format and print a string to a fd, usually stdout/stderr.

In general we can't use "format!", "print!", "eprint!" etc. because they don't
know about our use of WString where we encode of invalid Unicode characters
in the private use area.

Instead we use "wwrite_to_fd()".
Since we unfortunately don't have a "wformat!()" yet, we use "sprintf!()"
to create a formatted wstring to pass to "wwrite_to_fd()".

Add "printf!" and "eprintf!" to stand in for "print!" and "eprint!".
For printing to files other than stdout and stderr, keep "fwprintf!" but
drop the "w" since our "sprintf!" always produces wide strings.

Replace "fputws" with "fprintf" though we could also use "wwrite_to_fd"
if performance matters.

Unlike std::io::stdout(), we don't use locking yet.

Remaining work:

- There are more places where we use \be?print(ln)?!
  Usually we print strings that are guaranteed to be valid UTF-8, but not
  always. We should probably make all of them respect our WString semantics
  but preferrably keep using the native Rust format strings (#9948).
- I think flog.rs currently uses String so it won't handle invalid Unicode
  characters. We should probably fix this as well.
This commit is contained in:
Johannes Altmanninger
2023-12-05 08:38:07 +01:00
parent e88e7dbf7a
commit f5712af132
8 changed files with 65 additions and 63 deletions

View File

@@ -5,7 +5,7 @@
use crate::tokenizer::tok_command;
use crate::wutil::perror;
use crate::{env::EnvMode, proc::TtyTransfer};
use libc::{STDERR_FILENO, STDIN_FILENO, TCSADRAIN};
use libc::{STDIN_FILENO, TCSADRAIN};
use super::prelude::*;
@@ -126,11 +126,7 @@ pub fn fg(parser: &Parser, streams: &mut IoStreams, argv: &mut [&wstr]) -> Optio
} else {
// If we aren't redirecting, send output to real stderr, since stuff in sb_err won't get
// printed until the command finishes.
fwprintf!(
STDERR_FILENO,
"%s",
wgettext_fmt!(FG_MSG, job.job_id(), job.command())
);
eprintf!("%s", wgettext_fmt!(FG_MSG, job.job_id(), job.command()));
}
let ft = tok_command(job.command());

View File

@@ -16,7 +16,7 @@
use crate::wcstringutil::wcs2string_callback;
use crate::wildcard::{ANY_CHAR, ANY_STRING, ANY_STRING_RECURSIVE};
use crate::wutil::encoding::{mbrtowc, wcrtomb, zero_mbstate, AT_LEAST_MB_LEN_MAX};
use crate::wutil::{fish_iswalnum, wwrite_to_fd};
use crate::wutil::fish_iswalnum;
use bitflags::bitflags;
use core::slice;
use cxx::{CxxWString, UniquePtr};
@@ -2143,16 +2143,31 @@ fn to_cstring(self) -> CString {
}
}
#[allow(unused_macros)]
#[deprecated = "use printf!, eprintf! or fprintf"]
macro_rules! fwprintf {
($args:tt) => {
panic!()
};
}
#[allow(unused_macros)]
#[deprecated = "use printf!"]
pub fn fputws(_s: &wstr, _fd: RawFd) {
panic!()
}
// test-only
#[allow(unused_macros)]
#[deprecated = "use printf!"]
macro_rules! err {
($format:expr $(, $args:expr)* $(,)? ) => {
println!($format $(, $args )*);
printf!($format $(, $args )*);
}
}
macro_rules! fwprintf {
($fd:expr, $format:expr $(, $arg:expr)*) => {
macro_rules! fprintf {
($fd:expr, $format:expr $(, $arg:expr)* $(,)?) => {
{
let wide = crate::wutil::sprintf!($format, $( $arg ),*);
crate::wutil::wwrite_to_fd(&wide, $fd);
@@ -2160,8 +2175,16 @@ macro_rules! fwprintf {
}
}
pub fn fputws(s: &wstr, fd: RawFd) {
wwrite_to_fd(s, fd);
macro_rules! printf {
($format:expr $(, $arg:expr)* $(,)?) => {
fprintf!(libc::STDOUT_FILENO, $format $(, $arg)*)
}
}
macro_rules! eprintf {
($format:expr $(, $arg:expr)* $(,)?) => {
fprintf!(libc::STDERR_FILENO, $format $(, $arg)*)
}
}
#[cxx::bridge]

View File

@@ -55,7 +55,6 @@
wchar::prelude::*,
wutil::waccess,
};
use libc::STDERR_FILENO;
use std::env;
use std::ffi::{CString, OsStr, OsString};
use std::fs::File;
@@ -331,7 +330,7 @@ fn run_command_list(parser: &Parser, cmds: &[OsString]) -> i32 {
retval = STATUS_CMD_OK;
} else {
let backtrace = parser.get_backtrace(&cmd_wcs, &errors);
fwprintf!(STDERR_FILENO, "%s", backtrace);
eprintf!("%s", backtrace);
// XXX: Why is this the return for "unknown command"?
retval = STATUS_CMD_UNKNOWN;
}
@@ -436,8 +435,8 @@ fn fish_parse_opt(args: &mut [WString], opts: &mut FishCmdOpts) -> usize {
}
'P' => opts.enable_private_mode = true,
'v' => {
print!(
"{}",
printf!(
"%s",
wgettext_fmt!("%s, version %s\n", PACKAGE_NAME, crate::BUILD_VERSION)
);
std::process::exit(0);

View File

@@ -19,10 +19,7 @@
use crate::wutil::{perror, perror_io, wdirname, wstat, wwrite_to_fd};
use cxx::CxxWString;
use errno::Errno;
use libc::{
EAGAIN, EEXIST, EINTR, ENOENT, ENOTDIR, EPIPE, EWOULDBLOCK, O_EXCL, STDERR_FILENO,
STDOUT_FILENO,
};
use libc::{EAGAIN, EEXIST, EINTR, ENOENT, ENOTDIR, EPIPE, EWOULDBLOCK, O_EXCL, STDOUT_FILENO};
use std::cell::{RefCell, UnsafeCell};
use std::os::fd::RawFd;
use std::sync::atomic::{AtomicU64, Ordering};
@@ -237,7 +234,7 @@ fn source_fd(&self) -> RawFd {
-1
}
fn print(&self) {
fwprintf!(STDERR_FILENO, "close %d\n", self.fd)
eprintf!("close %d\n", self.fd)
}
fn as_ptr(&self) -> *const () {
(self as *const Self).cast()
@@ -266,7 +263,7 @@ fn source_fd(&self) -> RawFd {
self.source_fd
}
fn print(&self) {
fwprintf!(STDERR_FILENO, "FD map %d -> %d\n", self.source_fd, self.fd)
eprintf!("FD map %d -> %d\n", self.source_fd, self.fd)
}
fn as_ptr(&self) -> *const () {
(self as *const Self).cast()
@@ -298,7 +295,7 @@ fn source_fd(&self) -> RawFd {
self.file_fd.fd()
}
fn print(&self) {
fwprintf!(STDERR_FILENO, "file %d -> %d\n", self.file_fd.fd(), self.fd)
eprintf!("file %d -> %d\n", self.file_fd.fd(), self.fd)
}
fn as_ptr(&self) -> *const () {
(self as *const Self).cast()
@@ -334,8 +331,7 @@ fn source_fd(&self) -> RawFd {
self.pipe_fd.fd()
}
fn print(&self) {
fwprintf!(
STDERR_FILENO,
eprintf!(
"pipe {%d} (input: %s) -> %d\n",
self.source_fd(),
if self.is_input { "yes" } else { "no" },
@@ -425,12 +421,7 @@ fn source_fd(&self) -> RawFd {
self.write_fd.fd()
}
fn print(&self) {
fwprintf!(
STDERR_FILENO,
"bufferfill %d -> %d\n",
self.write_fd.fd(),
self.fd()
)
eprintf!("bufferfill %d -> %d\n", self.write_fd.fd(), self.fd())
}
fn as_ptr(&self) -> *const () {
(self as *const Self).cast()
@@ -736,22 +727,20 @@ pub fn append_from_specs(&mut self, specs: &RedirectionSpecList, pwd: &wstr) ->
/// Output debugging information to stderr.
pub fn print(&self) {
if self.0.is_empty() {
fwprintf!(
STDERR_FILENO,
eprintf!(
"Empty chain %s\n",
format!("{:p}", std::ptr::addr_of!(self))
);
return;
}
fwprintf!(
STDERR_FILENO,
eprintf!(
"Chain %s (%ld items):\n",
format!("{:p}", std::ptr::addr_of!(self)),
self.0.len()
);
for (i, io) in self.0.iter().enumerate() {
fwprintf!(STDERR_FILENO, "\t%lu: fd:%d, ", i, io.fd());
eprintf!("\t%lu: fd:%d, ", i, io.fd());
io.print();
}
}

View File

@@ -297,7 +297,7 @@ fn report_errors(
// Print it.
if !should_suppress_stderr_for_tests() {
fwprintf!(STDERR_FILENO, "%ls", backtrace_and_desc);
eprintf!("%s", backtrace_and_desc);
}
// Mark status.

View File

@@ -38,7 +38,7 @@
use crate::wutil::{perror, wgettext, wgettext_fmt};
use cxx::{CxxWString, UniquePtr};
use libc::c_int;
use libc::{O_RDONLY, STDERR_FILENO};
use libc::O_RDONLY;
use once_cell::sync::Lazy;
pub use parser_ffi::{library_data_pod_t, BlockType, LoopStatus};
use printf_compat::sprintf;
@@ -467,7 +467,7 @@ pub fn eval_with(
let backtrace_and_desc = self.get_backtrace(cmd, &error_list);
// Print it.
fwprintf!(STDERR_FILENO, "%ls\n", backtrace_and_desc);
eprintf!("%s\n", backtrace_and_desc);
// Set a valid status.
self.set_last_statuses(Statuses::just(STATUS_ILLEGAL_CMD.unwrap()));
@@ -962,7 +962,7 @@ pub fn emit_profiling(&self, path: &[u8]) {
return;
}
};
fwprintf!(f.as_raw_fd(), "Time\tSum\tCommand\n");
fprintf!(f.as_raw_fd(), "Time\tSum\tCommand\n");
print_profile(&self.profile_items.borrow(), f.as_raw_fd());
}
@@ -1128,12 +1128,12 @@ fn print_profile(items: &[ProfileItem], out: RawFd) {
}
}
fwprintf!(out, "%lld\t%lld\t", self_time, total_time);
fprintf!(out, "%lld\t%lld\t", self_time, total_time);
for _i in 0..item.level {
fwprintf!(out, "-");
fprintf!(out, "-");
}
fwprintf!(out, "> %ls\n", item.cmd);
fprintf!(out, "> %ls\n", item.cmd);
}
}

View File

@@ -4,7 +4,7 @@
use crate::ast;
use crate::common::{
charptr2wcstring, escape, fputws, redirect_tty_output, scoped_push_replacer, timef, Timepoint,
charptr2wcstring, escape, redirect_tty_output, scoped_push_replacer, timef, Timepoint,
};
use crate::curses::term;
use crate::env::Statuses;
@@ -25,9 +25,9 @@
use crate::wutil::{perror, wbasename, wgettext, wperror};
use libc::{
EBADF, EINVAL, ENOTTY, EPERM, EXIT_SUCCESS, SIGABRT, SIGBUS, SIGCONT, SIGFPE, SIGHUP, SIGILL,
SIGINT, SIGPIPE, SIGQUIT, SIGSEGV, SIGSYS, SIGTTOU, SIG_DFL, SIG_IGN, STDIN_FILENO,
STDOUT_FILENO, WCONTINUED, WEXITSTATUS, WIFCONTINUED, WIFEXITED, WIFSIGNALED, WIFSTOPPED,
WNOHANG, WTERMSIG, WUNTRACED, _SC_CLK_TCK,
SIGINT, SIGPIPE, SIGQUIT, SIGSEGV, SIGSYS, SIGTTOU, SIG_DFL, SIG_IGN, STDIN_FILENO, WCONTINUED,
WEXITSTATUS, WIFCONTINUED, WIFEXITED, WIFSIGNALED, WIFSTOPPED, WNOHANG, WTERMSIG, WUNTRACED,
_SC_CLK_TCK,
};
use once_cell::sync::Lazy;
use printf_compat::sprintf;
@@ -1192,24 +1192,19 @@ pub fn jobs_requiring_warning_on_exit(parser: &Parser) -> JobList {
/// jobs_requiring_warning_on_exit().
#[widestrs]
pub fn print_exit_warning_for_jobs(jobs: &JobList) {
fputws(wgettext!("There are still jobs active:\n"), STDOUT_FILENO);
fputws(wgettext!("\n PID Command\n"), STDOUT_FILENO);
printf!("%s", wgettext!("There are still jobs active:\n"));
printf!("%s", wgettext!("\n PID Command\n"));
for j in jobs {
fwprintf!(
STDOUT_FILENO,
"%6d %ls\n",
j.processes()[0].pid(),
j.command()
);
printf!("%6d %ls\n", j.processes()[0].pid(), j.command());
}
fputws("\n"L, STDOUT_FILENO);
fputws(
printf!("\n");
printf!(
"%s",
wgettext!("A second attempt to exit will terminate them.\n"),
STDOUT_FILENO,
);
fputws(
printf!(
"%s",
wgettext!("Use 'disown PID' to remove jobs from the list without terminating them.\n"),
STDOUT_FILENO,
);
reader_schedule_prompt_repaint();
}

View File

@@ -304,20 +304,20 @@ fn test_history_races_pound_on_history(item_count: usize, idx: usize) {
// Remove everything from this item on
let removed = list.splice(position.., []);
for line in removed.into_iter().rev() {
err!("Item dropped from history: {line}");
println!("Item dropped from history: {line}");
}
found = true;
break;
}
if !found {
err!(
println!(
"Line '{}' found in history, but not found in some array",
item.str()
);
for list in &expected_lines {
if !list.is_empty() {
fwprintf!(STDERR_FILENO, "\tRemaining: %ls\n", list.last().unwrap())
printf!("\tRemaining: %ls\n", list.last().unwrap())
}
}
}