fish-shell/src/builtins/eval.rs
2024-02-11 11:40:27 -08:00

80 lines
2.8 KiB
Rust

//! The eval builtin.
use super::prelude::*;
use crate::io::IoBufferfill;
use crate::parser::BlockType;
use crate::wcstringutil::join_strings;
use libc::{STDERR_FILENO, STDOUT_FILENO};
pub fn eval(parser: &Parser, streams: &mut IoStreams, args: &mut [&wstr]) -> Option<c_int> {
let argc = args.len();
if argc <= 1 {
return STATUS_CMD_OK;
}
let new_cmd = join_strings(&args[1..], ' ');
// Copy the full io chain; we may append bufferfills.
let mut ios = streams.io_chain.clone();
// If stdout is piped, then its output must go to the streams, not to the io_chain in our
// streams, because the pipe may be intended to be consumed by a process which
// is not yet launched (#6806). If stdout is NOT redirected, it must see the tty (#6955). So
// create a bufferfill for stdout if and only if stdout is piped.
// Note do not do this if stdout is merely redirected (say, to a file); we don't want to
// buffer in that case.
let mut stdout_fill = None;
if streams.out_is_piped {
match IoBufferfill::create_opts(parser.libdata().pods.read_limit, STDOUT_FILENO) {
Err(_) => {
// We were unable to create a pipe, probably fd exhaustion.
return STATUS_CMD_ERROR;
}
Ok(buffer) => {
stdout_fill = Some(buffer.clone());
ios.push(buffer);
}
}
}
// Of course the same applies to stderr.
let mut stderr_fill = None;
if streams.err_is_piped {
match IoBufferfill::create_opts(parser.libdata().pods.read_limit, STDERR_FILENO) {
Err(_) => {
// We were unable to create a pipe, probably fd exhaustion.
return STATUS_CMD_ERROR;
}
Ok(buffer) => {
stderr_fill = Some(buffer.clone());
ios.push(buffer);
}
}
}
let res = parser.eval_with(&new_cmd, &ios, streams.job_group.as_ref(), BlockType::top);
let status = if res.was_empty {
// Issue #5692, in particular, to catch `eval ""`, `eval "begin; end;"`, etc.
// where we have an argument but nothing is executed.
STATUS_CMD_OK
} else {
Some(res.status.status_value())
};
// Finish the bufferfills - exhaust and close our pipes.
// Copy the output from the bufferfill back to the streams.
// Note it is important that we hold no other references to the bufferfills here - they need to
// deallocate to close.
ios.clear();
if let Some(stdout) = stdout_fill {
let output = IoBufferfill::finish(stdout);
streams.out.append_narrow_buffer(&output);
}
if let Some(stderr) = stderr_fill {
let errput = IoBufferfill::finish(stderr);
streams.err.append_narrow_buffer(&errput);
}
status
}