2022-01-18 09:48:28 +01:00
|
|
|
mod config_files;
|
|
|
|
mod logger;
|
2022-02-09 23:08:16 +01:00
|
|
|
mod test_bins;
|
2021-08-10 20:57:08 +02:00
|
|
|
#[cfg(test)]
|
|
|
|
mod tests;
|
|
|
|
|
2022-03-16 19:17:06 +01:00
|
|
|
#[cfg(feature = "plugin")]
|
|
|
|
use crate::config_files::NUSHELL_FOLDER;
|
2022-02-09 23:08:16 +01:00
|
|
|
use crate::logger::{configure, logger};
|
2022-10-21 17:20:21 +02:00
|
|
|
use log::{info, Level};
|
2022-01-18 09:48:28 +01:00
|
|
|
use miette::Result;
|
2022-03-16 19:17:06 +01:00
|
|
|
#[cfg(feature = "plugin")]
|
|
|
|
use nu_cli::read_plugin_file;
|
|
|
|
use nu_cli::{
|
|
|
|
evaluate_commands, evaluate_file, evaluate_repl, gather_parent_env_vars, get_init_cwd,
|
2022-07-14 16:09:27 +02:00
|
|
|
report_error, report_error_new,
|
2022-03-16 19:17:06 +01:00
|
|
|
};
|
2022-01-26 15:42:39 +01:00
|
|
|
use nu_command::{create_default_context, BufferedReader};
|
2022-02-11 19:46:36 +01:00
|
|
|
use nu_engine::{get_full_help, CallExt};
|
2022-06-11 10:52:31 +02:00
|
|
|
use nu_parser::{escape_for_script_arg, escape_quote_string, parse};
|
2022-08-22 18:30:09 +02:00
|
|
|
use nu_path::canonicalize_with;
|
2022-01-26 15:42:39 +01:00
|
|
|
use nu_protocol::{
|
2022-02-15 20:31:14 +01:00
|
|
|
ast::{Call, Expr, Expression},
|
2022-01-26 15:42:39 +01:00
|
|
|
engine::{Command, EngineState, Stack, StateWorkingSet},
|
2022-01-28 19:32:33 +01:00
|
|
|
Category, Example, IntoPipelineData, PipelineData, RawStream, ShellError, Signature, Span,
|
2022-04-19 00:28:01 +02:00
|
|
|
Spanned, SyntaxShape, Value,
|
2022-01-18 09:48:28 +01:00
|
|
|
};
|
2022-05-17 20:28:18 +02:00
|
|
|
use nu_utils::stdout_write_all_and_flush;
|
2022-01-26 15:42:39 +01:00
|
|
|
use std::{
|
2022-05-17 20:28:18 +02:00
|
|
|
io::BufReader,
|
2022-01-26 15:42:39 +01:00
|
|
|
sync::{
|
|
|
|
atomic::{AtomicBool, Ordering},
|
|
|
|
Arc,
|
|
|
|
},
|
|
|
|
};
|
2022-10-21 17:20:21 +02:00
|
|
|
use std::{path::Path, str::FromStr};
|
2022-02-09 23:08:16 +01:00
|
|
|
|
2022-09-29 20:37:48 +02:00
|
|
|
// Inspired by fish's acquire_tty_or_exit
|
|
|
|
#[cfg(unix)]
|
|
|
|
fn take_control(interactive: bool) {
|
|
|
|
use nix::{
|
|
|
|
errno::Errno,
|
|
|
|
sys::signal::{self, SaFlags, SigAction, SigHandler, SigSet, Signal},
|
|
|
|
unistd::{self, Pid},
|
|
|
|
};
|
|
|
|
|
|
|
|
let shell_pgid = unistd::getpgrp();
|
|
|
|
|
2022-11-04 21:06:04 +01:00
|
|
|
match unistd::tcgetpgrp(nix::libc::STDIN_FILENO) {
|
|
|
|
Ok(owner_pgid) if owner_pgid == shell_pgid => {
|
|
|
|
// Common case, nothing to do
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
Ok(owner_pgid) if owner_pgid == unistd::getpid() => {
|
|
|
|
// This can apparently happen with sudo: https://github.com/fish-shell/fish-shell/issues/7388
|
|
|
|
let _ = unistd::setpgid(owner_pgid, owner_pgid);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
_ => (),
|
2022-09-29 20:37:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Reset all signal handlers to default
|
|
|
|
for sig in Signal::iterator() {
|
|
|
|
unsafe {
|
|
|
|
if let Ok(old_act) = signal::sigaction(
|
|
|
|
sig,
|
|
|
|
&SigAction::new(SigHandler::SigDfl, SaFlags::empty(), SigSet::empty()),
|
|
|
|
) {
|
|
|
|
// fish preserves ignored SIGHUP, presumably for nohup support, so let's do the same
|
|
|
|
if sig == Signal::SIGHUP && old_act.handler() == SigHandler::SigIgn {
|
|
|
|
let _ = signal::sigaction(sig, &old_act);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let mut success = false;
|
|
|
|
for _ in 0..4096 {
|
|
|
|
match unistd::tcgetpgrp(nix::libc::STDIN_FILENO) {
|
|
|
|
Ok(owner_pgid) if owner_pgid == shell_pgid => {
|
|
|
|
success = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
Ok(owner_pgid) if owner_pgid == Pid::from_raw(0) => {
|
|
|
|
// Zero basically means something like "not owned" and we can just take it
|
|
|
|
let _ = unistd::tcsetpgrp(nix::libc::STDIN_FILENO, shell_pgid);
|
|
|
|
}
|
|
|
|
Err(Errno::ENOTTY) => {
|
|
|
|
if !interactive {
|
|
|
|
// that's fine
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
eprintln!("ERROR: no TTY for interactive shell");
|
|
|
|
std::process::exit(1);
|
|
|
|
}
|
|
|
|
_ => {
|
|
|
|
// fish also has other heuristics than "too many attempts" for the orphan check, but they're optional
|
|
|
|
if signal::killpg(Pid::from_raw(-shell_pgid.as_raw()), Signal::SIGTTIN).is_err() {
|
2022-10-17 21:08:25 +02:00
|
|
|
if !interactive {
|
|
|
|
// that's fine
|
|
|
|
return;
|
|
|
|
}
|
2022-09-29 20:37:48 +02:00
|
|
|
eprintln!("ERROR: failed to SIGTTIN ourselves");
|
|
|
|
std::process::exit(1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2022-10-17 21:08:25 +02:00
|
|
|
if !success && interactive {
|
2022-09-29 20:37:48 +02:00
|
|
|
eprintln!("ERROR: failed take control of the terminal, we might be orphaned");
|
|
|
|
std::process::exit(1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(unix)]
|
|
|
|
fn acquire_terminal(interactive: bool) {
|
|
|
|
use nix::sys::signal::{signal, SigHandler, Signal};
|
|
|
|
|
|
|
|
if !atty::is(atty::Stream::Stdin) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
take_control(interactive);
|
|
|
|
|
|
|
|
unsafe {
|
|
|
|
// SIGINT and SIGQUIT have special handling above
|
|
|
|
signal(Signal::SIGTSTP, SigHandler::SigIgn).expect("signal ignore");
|
|
|
|
signal(Signal::SIGTTIN, SigHandler::SigIgn).expect("signal ignore");
|
|
|
|
signal(Signal::SIGTTOU, SigHandler::SigIgn).expect("signal ignore");
|
|
|
|
// signal::signal(Signal::SIGCHLD, SigHandler::SigIgn).expect("signal ignore"); // needed for std::command's waitpid usage
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(not(unix))]
|
|
|
|
fn acquire_terminal(_: bool) {}
|
|
|
|
|
2021-09-20 23:37:26 +02:00
|
|
|
fn main() -> Result<()> {
|
2021-11-14 20:25:57 +01:00
|
|
|
// miette::set_panic_hook();
|
2021-10-01 18:39:50 +02:00
|
|
|
let miette_hook = std::panic::take_hook();
|
|
|
|
std::panic::set_hook(Box::new(move |x| {
|
2021-12-04 13:38:21 +01:00
|
|
|
crossterm::terminal::disable_raw_mode().expect("unable to disable raw mode");
|
2021-10-01 18:39:50 +02:00
|
|
|
miette_hook(x);
|
|
|
|
}));
|
2021-09-21 21:37:16 +02:00
|
|
|
|
2022-01-04 23:30:34 +01:00
|
|
|
// Get initial current working directory.
|
2022-03-16 19:17:06 +01:00
|
|
|
let init_cwd = get_init_cwd();
|
2022-07-14 16:09:27 +02:00
|
|
|
let mut engine_state = create_default_context();
|
2021-07-17 08:31:34 +02:00
|
|
|
|
2022-01-24 16:05:19 +01:00
|
|
|
// Custom additions
|
|
|
|
let delta = {
|
|
|
|
let mut working_set = nu_protocol::engine::StateWorkingSet::new(&engine_state);
|
|
|
|
working_set.add_decl(Box::new(nu_cli::NuHighlight));
|
2022-02-18 19:43:34 +01:00
|
|
|
working_set.add_decl(Box::new(nu_cli::Print));
|
2022-01-24 16:05:19 +01:00
|
|
|
|
|
|
|
working_set.render()
|
|
|
|
};
|
2022-07-14 16:09:27 +02:00
|
|
|
|
|
|
|
if let Err(err) = engine_state.merge_delta(delta) {
|
|
|
|
report_error_new(&engine_state, &err);
|
|
|
|
}
|
2022-01-24 16:05:19 +01:00
|
|
|
|
2021-10-28 06:13:10 +02:00
|
|
|
// TODO: make this conditional in the future
|
|
|
|
// Ctrl-c protection section
|
|
|
|
let ctrlc = Arc::new(AtomicBool::new(false));
|
|
|
|
let handler_ctrlc = ctrlc.clone();
|
|
|
|
let engine_state_ctrlc = ctrlc.clone();
|
|
|
|
|
|
|
|
ctrlc::set_handler(move || {
|
|
|
|
handler_ctrlc.store(true, Ordering::SeqCst);
|
|
|
|
})
|
|
|
|
.expect("Error setting Ctrl-C handler");
|
|
|
|
|
|
|
|
engine_state.ctrlc = Some(engine_state_ctrlc);
|
|
|
|
// End ctrl-c protection section
|
|
|
|
|
2022-06-09 14:08:15 +02:00
|
|
|
// SIGQUIT protection section (only works for POSIX system)
|
|
|
|
#[cfg(not(windows))]
|
|
|
|
{
|
|
|
|
use signal_hook::consts::SIGQUIT;
|
|
|
|
let sig_quit = Arc::new(AtomicBool::new(false));
|
|
|
|
signal_hook::flag::register(SIGQUIT, sig_quit.clone()).expect("Error setting SIGQUIT flag");
|
|
|
|
engine_state.set_sig_quit(sig_quit);
|
|
|
|
}
|
|
|
|
// End SIGQUIT protection section
|
|
|
|
|
2022-01-26 15:42:39 +01:00
|
|
|
let mut args_to_nushell = vec![];
|
|
|
|
let mut script_name = String::new();
|
|
|
|
let mut args_to_script = vec![];
|
|
|
|
|
|
|
|
// Would be nice if we had a way to parse this. The first flags we see will be going to nushell
|
|
|
|
// then it'll be the script name
|
|
|
|
// then the args to the script
|
2022-07-26 16:41:05 +02:00
|
|
|
let mut args = std::env::args();
|
|
|
|
let argv0 = args.next();
|
|
|
|
|
2022-04-30 20:23:05 +02:00
|
|
|
while let Some(arg) = args.next() {
|
2022-01-26 15:42:39 +01:00
|
|
|
if !script_name.is_empty() {
|
2022-06-11 10:52:31 +02:00
|
|
|
args_to_script.push(escape_for_script_arg(&arg));
|
2022-01-26 15:42:39 +01:00
|
|
|
} else if arg.starts_with('-') {
|
|
|
|
// Cool, it's a flag
|
2022-04-30 20:23:05 +02:00
|
|
|
let flag_value = match arg.as_ref() {
|
2022-08-18 11:25:52 +02:00
|
|
|
"--commands" | "-c" | "--table-mode" | "-m" | "-e" | "--execute" => {
|
2022-05-11 23:15:31 +02:00
|
|
|
args.next().map(|a| escape_quote_string(&a))
|
|
|
|
}
|
2022-04-30 20:23:05 +02:00
|
|
|
"--config" | "--env-config" => args.next().map(|a| escape_quote_string(&a)),
|
2022-07-17 20:29:19 +02:00
|
|
|
#[cfg(feature = "plugin")]
|
|
|
|
"--plugin-config" => args.next().map(|a| escape_quote_string(&a)),
|
2022-08-09 18:44:37 +02:00
|
|
|
"--log-level" | "--log-target" | "--testbin" | "--threads" | "-t" => args.next(),
|
2022-04-30 20:23:05 +02:00
|
|
|
_ => None,
|
|
|
|
};
|
2022-02-19 21:54:43 +01:00
|
|
|
|
2022-01-26 15:42:39 +01:00
|
|
|
args_to_nushell.push(arg);
|
2022-04-30 20:23:05 +02:00
|
|
|
|
|
|
|
if let Some(flag_value) = flag_value {
|
|
|
|
args_to_nushell.push(flag_value);
|
|
|
|
}
|
2022-01-26 15:42:39 +01:00
|
|
|
} else {
|
|
|
|
// Our script file
|
|
|
|
script_name = arg;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
args_to_nushell.insert(0, "nu".into());
|
|
|
|
|
2022-07-26 16:41:05 +02:00
|
|
|
if let Some(argv0) = argv0 {
|
|
|
|
if argv0.starts_with('-') {
|
|
|
|
args_to_nushell.push("--login".into());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-26 15:42:39 +01:00
|
|
|
let nushell_commandline_args = args_to_nushell.join(" ");
|
|
|
|
|
2022-07-14 16:09:27 +02:00
|
|
|
let parsed_nu_cli_args = parse_commandline_args(&nushell_commandline_args, &mut engine_state);
|
2022-01-26 15:42:39 +01:00
|
|
|
|
2022-08-22 18:30:09 +02:00
|
|
|
if let Ok(ref args) = parsed_nu_cli_args {
|
|
|
|
set_config_path(
|
|
|
|
&mut engine_state,
|
|
|
|
&init_cwd,
|
|
|
|
"config.nu",
|
|
|
|
"config-path",
|
|
|
|
&args.config_file,
|
|
|
|
);
|
|
|
|
|
|
|
|
set_config_path(
|
|
|
|
&mut engine_state,
|
|
|
|
&init_cwd,
|
|
|
|
"env.nu",
|
|
|
|
"env-path",
|
|
|
|
&args.env_file,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2022-02-09 23:08:16 +01:00
|
|
|
match parsed_nu_cli_args {
|
|
|
|
Ok(binary_args) => {
|
2022-09-29 20:37:48 +02:00
|
|
|
// keep this condition in sync with the branches below
|
|
|
|
acquire_terminal(
|
|
|
|
binary_args.commands.is_none()
|
|
|
|
&& (script_name.is_empty() || binary_args.interactive_shell.is_some()),
|
|
|
|
);
|
|
|
|
|
2022-02-11 19:46:36 +01:00
|
|
|
if let Some(t) = binary_args.threads {
|
|
|
|
// 0 means to let rayon decide how many threads to use
|
|
|
|
let threads = t.as_i64().unwrap_or(0);
|
|
|
|
rayon::ThreadPoolBuilder::new()
|
|
|
|
.num_threads(threads as usize)
|
|
|
|
.build_global()
|
|
|
|
.expect("error setting number of threads");
|
|
|
|
}
|
|
|
|
|
2022-10-21 17:20:21 +02:00
|
|
|
if binary_args.log_level.is_some() {
|
|
|
|
let mut level = binary_args
|
2022-02-20 11:08:53 +01:00
|
|
|
.log_level
|
|
|
|
.map(|level| level.item)
|
|
|
|
.unwrap_or_else(|| "info".to_string());
|
|
|
|
|
2022-10-21 17:20:21 +02:00
|
|
|
if Level::from_str(level.as_str()).is_err() {
|
|
|
|
eprintln!("ERROR: log library did not recognize log level '{level}', using default 'info'");
|
|
|
|
level = "info".to_string();
|
|
|
|
}
|
|
|
|
|
2022-08-09 18:44:37 +02:00
|
|
|
let target = binary_args
|
|
|
|
.log_target
|
|
|
|
.map(|target| target.item)
|
|
|
|
.unwrap_or_else(|| "stderr".to_string());
|
|
|
|
|
|
|
|
logger(|builder| configure(level.as_str(), target.as_str(), builder))?;
|
2022-02-09 23:08:16 +01:00
|
|
|
info!("start logging {}:{}:{}", file!(), line!(), column!());
|
|
|
|
}
|
|
|
|
|
|
|
|
if let Some(testbin) = &binary_args.testbin {
|
2022-02-02 21:59:01 +01:00
|
|
|
// Call out to the correct testbin
|
|
|
|
match testbin.item.as_str() {
|
2022-10-20 14:56:44 +02:00
|
|
|
"echo_env" => test_bins::echo_env(true),
|
|
|
|
"echo_env_stderr" => test_bins::echo_env(false),
|
2022-02-02 21:59:01 +01:00
|
|
|
"cococo" => test_bins::cococo(),
|
|
|
|
"meow" => test_bins::meow(),
|
2022-03-27 04:35:59 +02:00
|
|
|
"meowb" => test_bins::meowb(),
|
|
|
|
"relay" => test_bins::relay(),
|
2022-02-02 21:59:01 +01:00
|
|
|
"iecho" => test_bins::iecho(),
|
|
|
|
"fail" => test_bins::fail(),
|
|
|
|
"nonu" => test_bins::nonu(),
|
|
|
|
"chop" => test_bins::chop(),
|
|
|
|
"repeater" => test_bins::repeater(),
|
2022-07-29 22:42:00 +02:00
|
|
|
"nu_repl" => test_bins::nu_repl(),
|
2022-02-02 21:59:01 +01:00
|
|
|
_ => std::process::exit(1),
|
|
|
|
}
|
|
|
|
std::process::exit(0)
|
|
|
|
}
|
2022-02-09 23:08:16 +01:00
|
|
|
let input = if let Some(redirect_stdin) = &binary_args.redirect_stdin {
|
2022-01-26 18:26:43 +01:00
|
|
|
let stdin = std::io::stdin();
|
|
|
|
let buf_reader = BufReader::new(stdin);
|
|
|
|
|
2022-02-25 20:51:31 +01:00
|
|
|
PipelineData::ExternalStream {
|
2022-03-08 02:17:33 +01:00
|
|
|
stdout: Some(RawStream::new(
|
2022-01-28 21:32:46 +01:00
|
|
|
Box::new(BufferedReader::new(buf_reader)),
|
|
|
|
Some(ctrlc),
|
|
|
|
redirect_stdin.span,
|
2022-03-08 02:17:33 +01:00
|
|
|
)),
|
2022-02-25 20:51:31 +01:00
|
|
|
stderr: None,
|
|
|
|
exit_code: None,
|
|
|
|
span: redirect_stdin.span,
|
|
|
|
metadata: None,
|
|
|
|
}
|
2022-01-26 18:26:43 +01:00
|
|
|
} else {
|
|
|
|
PipelineData::new(Span::new(0, 0))
|
|
|
|
};
|
2022-01-26 15:42:39 +01:00
|
|
|
|
2022-10-21 17:20:21 +02:00
|
|
|
info!("redirect_stdin {}:{}:{}", file!(), line!(), column!());
|
2022-02-09 23:08:16 +01:00
|
|
|
|
2022-03-16 19:17:06 +01:00
|
|
|
// First, set up env vars as strings only
|
2022-06-10 20:01:08 +02:00
|
|
|
gather_parent_env_vars(&mut engine_state, &init_cwd);
|
2022-07-14 16:09:27 +02:00
|
|
|
|
2022-03-16 19:17:06 +01:00
|
|
|
let mut stack = nu_protocol::engine::Stack::new();
|
|
|
|
|
2022-02-09 23:08:16 +01:00
|
|
|
if let Some(commands) = &binary_args.commands {
|
2022-02-26 09:57:51 +01:00
|
|
|
#[cfg(feature = "plugin")]
|
2022-04-06 09:45:26 +02:00
|
|
|
read_plugin_file(
|
|
|
|
&mut engine_state,
|
|
|
|
&mut stack,
|
2022-07-17 20:29:19 +02:00
|
|
|
binary_args.plugin_file,
|
2022-04-06 09:45:26 +02:00
|
|
|
NUSHELL_FOLDER,
|
|
|
|
);
|
2022-07-10 17:12:24 +02:00
|
|
|
|
2022-05-23 14:47:08 +02:00
|
|
|
// only want to load config and env if relative argument is provided.
|
2022-07-10 17:12:24 +02:00
|
|
|
if binary_args.env_file.is_some() {
|
2022-05-23 14:47:08 +02:00
|
|
|
config_files::read_config_file(
|
|
|
|
&mut engine_state,
|
|
|
|
&mut stack,
|
2022-07-10 17:12:24 +02:00
|
|
|
binary_args.env_file,
|
|
|
|
true,
|
2022-07-10 14:16:46 +02:00
|
|
|
);
|
2022-07-14 07:53:13 +02:00
|
|
|
} else {
|
2022-10-21 17:20:21 +02:00
|
|
|
config_files::read_default_env_file(&mut engine_state, &mut stack)
|
2022-07-10 14:16:46 +02:00
|
|
|
}
|
2022-07-10 17:12:24 +02:00
|
|
|
|
|
|
|
if binary_args.config_file.is_some() {
|
2022-07-10 14:16:46 +02:00
|
|
|
config_files::read_config_file(
|
|
|
|
&mut engine_state,
|
|
|
|
&mut stack,
|
2022-07-10 17:12:24 +02:00
|
|
|
binary_args.config_file,
|
|
|
|
false,
|
2022-05-23 14:47:08 +02:00
|
|
|
);
|
|
|
|
}
|
2022-02-26 09:57:51 +01:00
|
|
|
|
2022-03-16 19:17:06 +01:00
|
|
|
let ret_val = evaluate_commands(
|
|
|
|
commands,
|
|
|
|
&mut engine_state,
|
|
|
|
&mut stack,
|
|
|
|
input,
|
2022-05-11 23:15:31 +02:00
|
|
|
binary_args.table_mode,
|
2022-03-16 19:17:06 +01:00
|
|
|
);
|
2022-10-21 17:20:21 +02:00
|
|
|
info!("-c command execution {}:{}:{}", file!(), line!(), column!());
|
2022-06-20 16:05:11 +02:00
|
|
|
match ret_val {
|
|
|
|
Ok(Some(exit_code)) => std::process::exit(exit_code as i32),
|
|
|
|
Ok(None) => Ok(()),
|
|
|
|
Err(e) => Err(e),
|
|
|
|
}
|
2022-02-09 23:08:16 +01:00
|
|
|
} else if !script_name.is_empty() && binary_args.interactive_shell.is_none() {
|
2022-02-26 09:57:51 +01:00
|
|
|
#[cfg(feature = "plugin")]
|
2022-04-06 09:45:26 +02:00
|
|
|
read_plugin_file(
|
|
|
|
&mut engine_state,
|
|
|
|
&mut stack,
|
2022-07-17 20:29:19 +02:00
|
|
|
binary_args.plugin_file,
|
2022-04-06 09:45:26 +02:00
|
|
|
NUSHELL_FOLDER,
|
|
|
|
);
|
2022-07-10 17:12:24 +02:00
|
|
|
|
2022-05-23 14:47:08 +02:00
|
|
|
// only want to load config and env if relative argument is provided.
|
2022-07-10 17:12:24 +02:00
|
|
|
if binary_args.env_file.is_some() {
|
2022-05-23 14:47:08 +02:00
|
|
|
config_files::read_config_file(
|
|
|
|
&mut engine_state,
|
|
|
|
&mut stack,
|
2022-07-10 17:12:24 +02:00
|
|
|
binary_args.env_file,
|
|
|
|
true,
|
2022-07-10 14:16:46 +02:00
|
|
|
);
|
2022-07-14 07:53:13 +02:00
|
|
|
} else {
|
2022-10-21 17:20:21 +02:00
|
|
|
config_files::read_default_env_file(&mut engine_state, &mut stack)
|
2022-07-10 14:16:46 +02:00
|
|
|
}
|
2022-07-10 17:12:24 +02:00
|
|
|
|
|
|
|
if binary_args.config_file.is_some() {
|
2022-07-10 14:16:46 +02:00
|
|
|
config_files::read_config_file(
|
|
|
|
&mut engine_state,
|
|
|
|
&mut stack,
|
2022-07-10 17:12:24 +02:00
|
|
|
binary_args.config_file,
|
|
|
|
false,
|
2022-05-23 14:47:08 +02:00
|
|
|
);
|
|
|
|
}
|
2022-02-26 09:57:51 +01:00
|
|
|
|
2022-03-16 19:17:06 +01:00
|
|
|
let ret_val = evaluate_file(
|
|
|
|
script_name,
|
|
|
|
&args_to_script,
|
|
|
|
&mut engine_state,
|
|
|
|
&mut stack,
|
|
|
|
input,
|
|
|
|
);
|
2022-07-25 00:57:10 +02:00
|
|
|
|
|
|
|
let last_exit_code = stack.get_env_var(&engine_state, "LAST_EXIT_CODE");
|
|
|
|
if let Some(last_exit_code) = last_exit_code {
|
|
|
|
let value = last_exit_code.as_integer();
|
|
|
|
if let Ok(value) = value {
|
|
|
|
if value != 0 {
|
|
|
|
std::process::exit(value as i32);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2022-10-21 17:20:21 +02:00
|
|
|
info!("eval_file execution {}:{}:{}", file!(), line!(), column!());
|
2022-02-09 23:08:16 +01:00
|
|
|
|
|
|
|
ret_val
|
2022-01-26 15:42:39 +01:00
|
|
|
} else {
|
2022-04-06 19:11:51 +02:00
|
|
|
setup_config(
|
|
|
|
&mut engine_state,
|
|
|
|
&mut stack,
|
2022-07-17 20:29:19 +02:00
|
|
|
#[cfg(feature = "plugin")]
|
|
|
|
binary_args.plugin_file,
|
2022-04-06 19:11:51 +02:00
|
|
|
binary_args.config_file,
|
|
|
|
binary_args.env_file,
|
2022-06-06 13:52:37 +02:00
|
|
|
binary_args.login_shell.is_some(),
|
2022-04-06 19:11:51 +02:00
|
|
|
);
|
2022-03-16 19:17:06 +01:00
|
|
|
|
2022-06-14 22:53:33 +02:00
|
|
|
let ret_val = evaluate_repl(
|
|
|
|
&mut engine_state,
|
|
|
|
&mut stack,
|
|
|
|
config_files::NUSHELL_FOLDER,
|
2022-08-18 11:25:52 +02:00
|
|
|
binary_args.execute,
|
2022-06-14 22:53:33 +02:00
|
|
|
);
|
2022-10-21 17:20:21 +02:00
|
|
|
info!("repl eval {}:{}:{}", file!(), line!(), column!());
|
2022-02-09 23:08:16 +01:00
|
|
|
|
|
|
|
ret_val
|
2022-01-26 15:42:39 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
Err(_) => std::process::exit(1),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-03-16 19:17:06 +01:00
|
|
|
fn setup_config(
|
|
|
|
engine_state: &mut EngineState,
|
|
|
|
stack: &mut Stack,
|
2022-07-17 20:29:19 +02:00
|
|
|
#[cfg(feature = "plugin")] plugin_file: Option<Spanned<String>>,
|
2022-03-16 19:17:06 +01:00
|
|
|
config_file: Option<Spanned<String>>,
|
2022-04-06 19:11:51 +02:00
|
|
|
env_file: Option<Spanned<String>>,
|
2022-06-06 13:52:37 +02:00
|
|
|
is_login_shell: bool,
|
2022-03-16 19:17:06 +01:00
|
|
|
) {
|
|
|
|
#[cfg(feature = "plugin")]
|
2022-10-21 17:20:21 +02:00
|
|
|
read_plugin_file(engine_state, stack, plugin_file, NUSHELL_FOLDER);
|
2022-03-16 19:17:06 +01:00
|
|
|
|
2022-10-21 17:20:21 +02:00
|
|
|
info!("read_config_file {}:{}:{}", file!(), line!(), column!());
|
|
|
|
|
|
|
|
config_files::read_config_file(engine_state, stack, env_file, true);
|
|
|
|
config_files::read_config_file(engine_state, stack, config_file, false);
|
2022-04-24 22:40:55 +02:00
|
|
|
|
2022-06-06 13:52:37 +02:00
|
|
|
if is_login_shell {
|
2022-10-21 17:20:21 +02:00
|
|
|
config_files::read_loginshell_file(engine_state, stack);
|
2022-06-06 13:52:37 +02:00
|
|
|
}
|
|
|
|
|
2022-04-24 22:40:55 +02:00
|
|
|
// Give a warning if we see `$config` for a few releases
|
|
|
|
{
|
|
|
|
let working_set = StateWorkingSet::new(engine_state);
|
|
|
|
if working_set.find_variable(b"$config").is_some() {
|
|
|
|
println!("warning: use `let-env config = ...` instead of `let config = ...`");
|
|
|
|
}
|
|
|
|
}
|
2022-03-16 19:17:06 +01:00
|
|
|
}
|
|
|
|
|
2022-01-26 15:42:39 +01:00
|
|
|
fn parse_commandline_args(
|
|
|
|
commandline_args: &str,
|
|
|
|
engine_state: &mut EngineState,
|
2022-02-09 23:08:16 +01:00
|
|
|
) -> Result<NushellCliArgs, ShellError> {
|
2022-01-26 15:42:39 +01:00
|
|
|
let (block, delta) = {
|
|
|
|
let mut working_set = StateWorkingSet::new(engine_state);
|
|
|
|
working_set.add_decl(Box::new(Nu));
|
|
|
|
|
2022-03-18 20:03:57 +01:00
|
|
|
let (output, err) = parse(
|
|
|
|
&mut working_set,
|
|
|
|
None,
|
|
|
|
commandline_args.as_bytes(),
|
|
|
|
false,
|
|
|
|
&[],
|
|
|
|
);
|
2022-01-26 15:42:39 +01:00
|
|
|
if let Some(err) = err {
|
|
|
|
report_error(&working_set, &err);
|
|
|
|
|
|
|
|
std::process::exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
working_set.hide_decl(b"nu");
|
|
|
|
(output, working_set.render())
|
|
|
|
};
|
|
|
|
|
2022-07-14 16:09:27 +02:00
|
|
|
engine_state.merge_delta(delta)?;
|
2022-01-26 15:42:39 +01:00
|
|
|
|
|
|
|
let mut stack = Stack::new();
|
|
|
|
|
|
|
|
// We should have a successful parse now
|
2022-02-15 20:31:14 +01:00
|
|
|
if let Some(pipeline) = block.pipelines.get(0) {
|
2022-01-26 15:42:39 +01:00
|
|
|
if let Some(Expression {
|
|
|
|
expr: Expr::Call(call),
|
|
|
|
..
|
2022-02-15 20:31:14 +01:00
|
|
|
}) = pipeline.expressions.get(0)
|
2022-01-26 15:42:39 +01:00
|
|
|
{
|
|
|
|
let redirect_stdin = call.get_named_arg("stdin");
|
2022-01-26 18:26:43 +01:00
|
|
|
let login_shell = call.get_named_arg("login");
|
|
|
|
let interactive_shell = call.get_named_arg("interactive");
|
|
|
|
let commands: Option<Expression> = call.get_flag_expr("commands");
|
2022-02-02 21:59:01 +01:00
|
|
|
let testbin: Option<Expression> = call.get_flag_expr("testbin");
|
2022-07-17 20:29:19 +02:00
|
|
|
#[cfg(feature = "plugin")]
|
|
|
|
let plugin_file: Option<Expression> = call.get_flag_expr("plugin-config");
|
2022-02-19 21:54:43 +01:00
|
|
|
let config_file: Option<Expression> = call.get_flag_expr("config");
|
2022-04-06 19:11:51 +02:00
|
|
|
let env_file: Option<Expression> = call.get_flag_expr("env-config");
|
2022-02-20 11:08:53 +01:00
|
|
|
let log_level: Option<Expression> = call.get_flag_expr("log-level");
|
2022-08-09 18:44:37 +02:00
|
|
|
let log_target: Option<Expression> = call.get_flag_expr("log-target");
|
2022-08-18 11:25:52 +02:00
|
|
|
let execute: Option<Expression> = call.get_flag_expr("execute");
|
2022-02-11 19:46:36 +01:00
|
|
|
let threads: Option<Value> = call.get_flag(engine_state, &mut stack, "threads")?;
|
2022-05-11 23:15:31 +02:00
|
|
|
let table_mode: Option<Value> =
|
|
|
|
call.get_flag(engine_state, &mut stack, "table-mode")?;
|
2022-01-26 18:26:43 +01:00
|
|
|
|
2022-02-19 21:54:43 +01:00
|
|
|
fn extract_contents(
|
|
|
|
expression: Option<Expression>,
|
2022-05-01 23:49:31 +02:00
|
|
|
) -> Result<Option<Spanned<String>>, ShellError> {
|
|
|
|
if let Some(expr) = expression {
|
|
|
|
let str = expr.as_string();
|
|
|
|
if let Some(str) = str {
|
|
|
|
Ok(Some(Spanned {
|
|
|
|
item: str,
|
|
|
|
span: expr.span,
|
|
|
|
}))
|
|
|
|
} else {
|
|
|
|
Err(ShellError::TypeMismatch("string".into(), expr.span))
|
2022-02-19 21:54:43 +01:00
|
|
|
}
|
2022-05-01 23:49:31 +02:00
|
|
|
} else {
|
|
|
|
Ok(None)
|
|
|
|
}
|
2022-02-19 21:54:43 +01:00
|
|
|
}
|
2022-01-26 15:42:39 +01:00
|
|
|
|
2022-05-01 23:49:31 +02:00
|
|
|
let commands = extract_contents(commands)?;
|
|
|
|
let testbin = extract_contents(testbin)?;
|
2022-07-17 20:29:19 +02:00
|
|
|
#[cfg(feature = "plugin")]
|
|
|
|
let plugin_file = extract_contents(plugin_file)?;
|
2022-05-01 23:49:31 +02:00
|
|
|
let config_file = extract_contents(config_file)?;
|
|
|
|
let env_file = extract_contents(env_file)?;
|
|
|
|
let log_level = extract_contents(log_level)?;
|
2022-08-09 18:44:37 +02:00
|
|
|
let log_target = extract_contents(log_target)?;
|
2022-08-18 11:25:52 +02:00
|
|
|
let execute = extract_contents(execute)?;
|
2022-02-02 21:59:01 +01:00
|
|
|
|
2022-01-26 15:42:39 +01:00
|
|
|
let help = call.has_flag("help");
|
|
|
|
|
|
|
|
if help {
|
|
|
|
let full_help =
|
|
|
|
get_full_help(&Nu.signature(), &Nu.examples(), engine_state, &mut stack);
|
2022-02-02 21:59:01 +01:00
|
|
|
|
2022-05-17 20:28:18 +02:00
|
|
|
let _ = std::panic::catch_unwind(move || stdout_write_all_and_flush(full_help));
|
2022-02-02 21:59:01 +01:00
|
|
|
|
2022-01-26 15:42:39 +01:00
|
|
|
std::process::exit(1);
|
|
|
|
}
|
|
|
|
|
2022-02-17 12:02:46 +01:00
|
|
|
if call.has_flag("version") {
|
2022-02-17 13:29:58 +01:00
|
|
|
let version = env!("CARGO_PKG_VERSION").to_string();
|
2022-02-17 12:02:46 +01:00
|
|
|
let _ = std::panic::catch_unwind(move || {
|
2022-05-17 20:28:18 +02:00
|
|
|
stdout_write_all_and_flush(format!("{}\n", version))
|
2022-02-17 12:02:46 +01:00
|
|
|
});
|
|
|
|
|
|
|
|
std::process::exit(0);
|
|
|
|
}
|
|
|
|
|
2022-02-09 23:08:16 +01:00
|
|
|
return Ok(NushellCliArgs {
|
2022-01-26 18:26:43 +01:00
|
|
|
redirect_stdin,
|
|
|
|
login_shell,
|
|
|
|
interactive_shell,
|
|
|
|
commands,
|
2022-02-02 21:59:01 +01:00
|
|
|
testbin,
|
2022-07-17 20:29:19 +02:00
|
|
|
#[cfg(feature = "plugin")]
|
|
|
|
plugin_file,
|
2022-02-19 21:54:43 +01:00
|
|
|
config_file,
|
2022-04-06 19:11:51 +02:00
|
|
|
env_file,
|
2022-02-20 11:08:53 +01:00
|
|
|
log_level,
|
2022-08-09 18:44:37 +02:00
|
|
|
log_target,
|
2022-08-18 11:25:52 +02:00
|
|
|
execute,
|
2022-02-11 19:46:36 +01:00
|
|
|
threads,
|
2022-05-11 23:15:31 +02:00
|
|
|
table_mode,
|
2022-01-26 18:26:43 +01:00
|
|
|
});
|
2022-01-26 15:42:39 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Just give the help and exit if the above fails
|
|
|
|
let full_help = get_full_help(&Nu.signature(), &Nu.examples(), engine_state, &mut stack);
|
|
|
|
print!("{}", full_help);
|
|
|
|
std::process::exit(1);
|
|
|
|
}
|
|
|
|
|
2022-02-09 23:08:16 +01:00
|
|
|
struct NushellCliArgs {
|
2022-01-26 15:42:39 +01:00
|
|
|
redirect_stdin: Option<Spanned<String>>,
|
2022-01-26 18:26:43 +01:00
|
|
|
login_shell: Option<Spanned<String>>,
|
|
|
|
interactive_shell: Option<Spanned<String>>,
|
|
|
|
commands: Option<Spanned<String>>,
|
2022-02-02 21:59:01 +01:00
|
|
|
testbin: Option<Spanned<String>>,
|
2022-07-17 20:29:19 +02:00
|
|
|
#[cfg(feature = "plugin")]
|
|
|
|
plugin_file: Option<Spanned<String>>,
|
2022-02-19 21:54:43 +01:00
|
|
|
config_file: Option<Spanned<String>>,
|
2022-04-06 19:11:51 +02:00
|
|
|
env_file: Option<Spanned<String>>,
|
2022-02-20 11:08:53 +01:00
|
|
|
log_level: Option<Spanned<String>>,
|
2022-08-09 18:44:37 +02:00
|
|
|
log_target: Option<Spanned<String>>,
|
2022-08-18 11:25:52 +02:00
|
|
|
execute: Option<Spanned<String>>,
|
2022-02-11 19:46:36 +01:00
|
|
|
threads: Option<Value>,
|
2022-05-11 23:15:31 +02:00
|
|
|
table_mode: Option<Value>,
|
2022-01-26 15:42:39 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Clone)]
|
|
|
|
struct Nu;
|
|
|
|
|
|
|
|
impl Command for Nu {
|
|
|
|
fn name(&self) -> &str {
|
|
|
|
"nu"
|
|
|
|
}
|
|
|
|
|
|
|
|
fn signature(&self) -> Signature {
|
2022-07-17 20:29:19 +02:00
|
|
|
let signature = Signature::build("nu")
|
2022-03-27 21:25:30 +02:00
|
|
|
.usage("The nushell language and shell.")
|
2022-01-26 15:42:39 +01:00
|
|
|
.switch("stdin", "redirect the stdin", None)
|
2022-01-26 18:26:43 +01:00
|
|
|
.switch("login", "start as a login shell", Some('l'))
|
|
|
|
.switch("interactive", "start as an interactive shell", Some('i'))
|
2022-02-17 12:02:46 +01:00
|
|
|
.switch("version", "print the version", Some('v'))
|
2022-02-02 21:59:01 +01:00
|
|
|
.named(
|
|
|
|
"testbin",
|
|
|
|
SyntaxShape::String,
|
|
|
|
"run internal test binary",
|
|
|
|
None,
|
|
|
|
)
|
2022-01-26 18:26:43 +01:00
|
|
|
.named(
|
|
|
|
"commands",
|
|
|
|
SyntaxShape::String,
|
|
|
|
"run the given commands and then exit",
|
|
|
|
Some('c'),
|
|
|
|
)
|
2022-02-19 21:54:43 +01:00
|
|
|
.named(
|
|
|
|
"config",
|
|
|
|
SyntaxShape::String,
|
2022-02-20 01:25:07 +01:00
|
|
|
"start with an alternate config file",
|
2022-02-19 21:54:43 +01:00
|
|
|
None,
|
|
|
|
)
|
2022-04-06 19:11:51 +02:00
|
|
|
.named(
|
|
|
|
"env-config",
|
|
|
|
SyntaxShape::String,
|
|
|
|
"start with an alternate environment config file",
|
|
|
|
None,
|
|
|
|
)
|
2022-02-20 11:08:53 +01:00
|
|
|
.named(
|
|
|
|
"log-level",
|
|
|
|
SyntaxShape::String,
|
2022-10-21 17:20:21 +02:00
|
|
|
"log level for diagnostic logs (error, warn, info, debug, trace). Off by default",
|
2022-02-20 11:08:53 +01:00
|
|
|
None,
|
|
|
|
)
|
2022-08-09 18:44:37 +02:00
|
|
|
.named(
|
|
|
|
"log-target",
|
|
|
|
SyntaxShape::String,
|
|
|
|
"set the target for the log to output. stdout, stderr(default), mixed or file",
|
|
|
|
None,
|
|
|
|
)
|
2022-08-18 11:25:52 +02:00
|
|
|
.named(
|
|
|
|
"execute",
|
|
|
|
SyntaxShape::String,
|
|
|
|
"run the given commands and then enter an interactive shell",
|
|
|
|
Some('e'),
|
|
|
|
)
|
2022-02-11 19:46:36 +01:00
|
|
|
.named(
|
|
|
|
"threads",
|
|
|
|
SyntaxShape::Int,
|
|
|
|
"threads to use for parallel commands",
|
|
|
|
Some('t'),
|
|
|
|
)
|
2022-05-11 23:15:31 +02:00
|
|
|
.named(
|
|
|
|
"table-mode",
|
|
|
|
SyntaxShape::String,
|
|
|
|
"the table mode to use. rounded is default.",
|
|
|
|
Some('m'),
|
|
|
|
)
|
2022-03-09 14:04:50 +01:00
|
|
|
.optional(
|
|
|
|
"script file",
|
|
|
|
SyntaxShape::Filepath,
|
|
|
|
"name of the optional script file to run",
|
|
|
|
)
|
2022-01-26 15:42:39 +01:00
|
|
|
.rest(
|
|
|
|
"script args",
|
|
|
|
SyntaxShape::String,
|
|
|
|
"parameters to the script file",
|
|
|
|
)
|
2022-07-17 20:29:19 +02:00
|
|
|
.category(Category::System);
|
|
|
|
|
|
|
|
#[cfg(feature = "plugin")]
|
|
|
|
{
|
|
|
|
signature.named(
|
|
|
|
"plugin-config",
|
|
|
|
SyntaxShape::String,
|
|
|
|
"start with an alternate plugin signature file",
|
|
|
|
None,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(not(feature = "plugin"))]
|
|
|
|
{
|
|
|
|
signature
|
|
|
|
}
|
2022-01-26 15:42:39 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
fn usage(&self) -> &str {
|
|
|
|
"The nushell language and shell."
|
|
|
|
}
|
|
|
|
|
|
|
|
fn run(
|
|
|
|
&self,
|
|
|
|
engine_state: &EngineState,
|
|
|
|
stack: &mut Stack,
|
|
|
|
call: &Call,
|
|
|
|
_input: PipelineData,
|
|
|
|
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
|
|
|
Ok(Value::String {
|
|
|
|
val: get_full_help(&Nu.signature(), &Nu.examples(), engine_state, stack),
|
|
|
|
span: call.head,
|
|
|
|
}
|
|
|
|
.into_pipeline_data())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn examples(&self) -> Vec<nu_protocol::Example> {
|
|
|
|
vec![
|
|
|
|
Example {
|
|
|
|
description: "Run a script",
|
|
|
|
example: "nu myfile.nu",
|
|
|
|
result: None,
|
|
|
|
},
|
|
|
|
Example {
|
|
|
|
description: "Run nushell interactively (as a shell or REPL)",
|
|
|
|
example: "nu",
|
|
|
|
result: None,
|
|
|
|
},
|
|
|
|
]
|
2022-01-04 23:30:34 +01:00
|
|
|
}
|
2019-05-15 18:12:38 +02:00
|
|
|
}
|
2022-02-09 23:08:16 +01:00
|
|
|
|
2022-08-22 18:30:09 +02:00
|
|
|
fn set_config_path(
|
|
|
|
engine_state: &mut EngineState,
|
|
|
|
cwd: &Path,
|
|
|
|
default_config_name: &str,
|
|
|
|
key: &str,
|
|
|
|
config_file: &Option<Spanned<String>>,
|
|
|
|
) {
|
|
|
|
let config_path = match config_file {
|
|
|
|
Some(s) => canonicalize_with(&s.item, cwd).ok(),
|
|
|
|
None => nu_path::config_dir().map(|mut p| {
|
|
|
|
p.push(config_files::NUSHELL_FOLDER);
|
|
|
|
p.push(default_config_name);
|
|
|
|
p
|
|
|
|
}),
|
|
|
|
};
|
|
|
|
|
|
|
|
if let Some(path) = config_path {
|
|
|
|
engine_state.set_config_path(key, path);
|
|
|
|
}
|
|
|
|
}
|