use super::prelude::*;
use crate::{
builtins::error::Error, err_fmt, err_raw, err_str, fds::wopen_cloexec, nix::isatty,
parser::Block, reader::reader_read,
};
use fish_common::{escape, FilenameRef};
use nix::{fcntl::OFlag, sys::stat::Mode};
use std::os::fd::AsRawFd as _;
pub fn source(parser: &Parser, streams: &mut IoStreams, args: &mut [&wstr]) -> BuiltinResult {
let argc = args.len();
let opts = HelpOnlyCmdOpts::parse(args, parser, streams)?;
let cmd = match args.first() {
Some(&cmd) => cmd,
None => return Err(STATUS_INVALID_ARGS),
};
if opts.print_help {
builtin_print_help(parser, streams, cmd);
return Ok(SUCCESS);
}
let opened_file;
let fd;
let func_filename;
let optind = opts.optind;
if argc == optind || args[optind] == "-" {
if streams.is_stdin_closed() {
err_str!(Error::STDIN_CLOSED).cmd(cmd).finish(streams);
return Err(STATUS_CMD_ERROR);
}
if argc == optind && isatty(streams.stdin_fd()) {
err_str!("missing filename argument or input redirection")
.cmd(cmd)
.finish(streams);
return Err(STATUS_CMD_ERROR);
}
func_filename = FilenameRef::new(L!("-").to_owned());
fd = streams.stdin_fd();
} else {
match wopen_cloexec(args[optind], OFlag::O_RDONLY, Mode::empty()) {
Ok(file) => {
opened_file = file;
}
Err(_) => {
let esc = escape(args[optind]);
err_fmt!("Error encountered while sourcing file '%s':", &esc)
.append_to_msg('\n')
.append_to_msg(&err_raw!(&builtin_strerror()).cmd(cmd).to_string())
.cmd(cmd)
.finish(streams);
return Err(STATUS_CMD_ERROR);
}
}
fd = opened_file.as_raw_fd();
func_filename = FilenameRef::new(args[optind].to_owned());
}
assert!(fd >= 0, "Should have a valid fd");
let sb = parser.push_block(Block::source_block(func_filename.clone()));
let _filename_push = parser
.library_data
.scoped_set(Some(func_filename.clone()), |s| &mut s.current_filename);
let remaining_args = &args[optind + if argc == optind { 0 } else { 1 }..];
let argv_list = remaining_args.iter().map(|&arg| arg.to_owned()).collect();
parser.vars().set_argv(argv_list, parser.is_repainting());
let retval = reader_read(parser, fd, streams.io_chain);
parser.pop_block(sb);
match retval {
Ok(_) => BuiltinResult::from_dynamic(parser.get_last_status()),
Err(err) => {
let esc = escape(&func_filename);
err_fmt!(
"Error while reading file '%s'",
if esc == "-" { L!("<stdin>") } else { &esc }
)
.cmd(cmd)
.finish(streams);
Err(err)
}
}
}