use crate::builtins::{calc::calc, cat::cat, cd::cd, echo::echo, help::help, ls::ls};
use crate::shared_functions::lex_tokenized_input;
use crate::ShellState;
use sflib::ensure_directory;
use std::io::{Read, Write};
use std::path::Path;
use std::process::{Command, Stdio};
#[derive(Debug, Clone)]
pub enum Redirection {
Overwrite,
Append,
NoOp,
}
#[derive(Debug, Clone)]
pub struct ShellCommand {
pub name: String,
pub args: Vec<String>,
pub redirection: Redirection,
}
pub fn return_shellcommand(name: String, args: Vec<String>, redirection: Redirection) -> ShellCommand {
ShellCommand {
name,
args,
redirection,
}
}
impl std::fmt::Display for ShellCommand {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{} {}", self.name, self.args.join(" "))
}
}
impl ShellCommand {
pub fn new(input: &str) -> ShellCommand {
fn get_redirection_type(input: &str) -> Redirection {
if input.contains(">>") {
Redirection::Append
} else if input.contains('>') {
Redirection::Overwrite
} else {
Redirection::NoOp
}
}
let lexed_vec = lex_tokenized_input(input);
ShellCommand {
name: lexed_vec[0].clone(),
args: lexed_vec[1..].to_vec(),
redirection: get_redirection_type(input),
}
}
pub fn run(shell_state: &mut ShellState, command: ShellCommand) {
if command.args.contains(&String::from("|"))
|| command.args.contains(&String::from(">>"))
|| command.args.contains(&String::from(">"))
{
println!("{}", piped_cmd(&PipedShellCommand::from(&command)));
} else {
match command.name.as_str() {
"calc" => println!("{}", calc(&command.args)),
"cat" => println!("{}", cat(&command.args)),
"cd" => cd(shell_state, &command),
"echo" => println!("{}", echo(&command.args)),
"help" => help(&command.args),
"ls" => print!("{}", ls(command.args)),
"pwd" => println!("{}", std::env::current_dir().unwrap().display()),
_ => {
cmd(&command);
}
}
}
}
}
#[derive(Debug)]
pub struct PipedShellCommand {
pub commands: Vec<ShellCommand>,
}
impl PipedShellCommand {
pub fn from(input: &ShellCommand) -> PipedShellCommand {
fn get_redirection_type(input: &ShellCommand) -> Redirection {
if input.args.contains(&String::from(">>")) {
Redirection::Append
} else if input.args.contains(&String::from(">")) {
Redirection::Overwrite
} else {
Redirection::NoOp
}
}
let parts = input.args.split(|arg| {
arg == &String::from("|") ||
arg == &String::from(">>") ||
arg == &String::from(">")
});
let mut commands: Vec<ShellCommand> = Vec::new();
for (idx, part) in parts.enumerate() {
if idx == 0 {
let command = ShellCommand {
name: input.name.clone(),
args: part[0..].to_vec(),
redirection: get_redirection_type(input),
};
commands.push(command);
} else {
let command = ShellCommand {
name: part[0].clone(),
args: part[1..].to_vec(),
redirection: get_redirection_type(input),
};
commands.push(command);
}
}
PipedShellCommand { commands }
}
}
pub fn cmd(command: &ShellCommand) {
let child = Command::new(&command.name)
.args(&command.args)
.spawn();
if let Ok(..) = child {
let output = child.unwrap().wait_with_output().unwrap().stdout;
let usable_output = std::str::from_utf8(&output).unwrap();
println!("{}", usable_output);
} else {
println!("Sorry, '{}' was not found!", command.name);
}
}
pub fn cmd_with_output(command: &ShellCommand) -> String {
let child = Command::new(&command.name)
.args(&command.args)
.output();
if let Ok(..) = child {
std::string::String::from_utf8_lossy(&child.unwrap().stdout).to_string()
} else {
String::from("Sorry, '{}' was not found!".to_string())
}
}
pub fn is_piped(args: &[String], cmd: &str) {
fn run_pipe(cmd: &str, args: &[String], redirection: Redirection) {
let command = return_shellcommand(cmd.to_string(), args.to_vec(), redirection);
let pipe = PipedShellCommand::from(&command);
piped_cmd(&pipe);
}
if args.contains(&"|".to_string()) {
run_pipe(cmd, args, Redirection::NoOp);
} else if args.contains(&">>".to_string()) {
run_pipe(cmd, args, Redirection::Append);
} else if args.contains(&">".to_string()) {
run_pipe(cmd, args, Redirection::Overwrite);
} else {
}
}
fn return_child(cmd: &str, args: &[String]) -> Result<std::process::Child, ()> {
Command::new(cmd)
.args(args)
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.spawn()
.or(Err(()))
}
pub fn piped_cmd(pipe: &PipedShellCommand) -> String {
let mut output_prev = String::new();
match pipe.commands[0].name.as_str() {
"cat" => output_prev = cat(&pipe.commands[0].args.clone()),
"echo" => output_prev = echo(&pipe.commands[0].args.clone()),
"calc" => output_prev = calc(&pipe.commands[0].args.clone()),
"ls" => output_prev = ls(pipe.commands[0].args.clone()),
_ => {
let child = return_child(&pipe.commands[0].name.clone(), &pipe.commands[0].args);
if child.is_err() {
println!("{} failed", pipe.commands[0].name.clone());
}
child.unwrap().stdout.unwrap().read_to_string(&mut output_prev).unwrap();
}
}
for (idx, command) in pipe.commands.iter().enumerate() {
if idx == 0 {
continue;
}
if idx == pipe.commands.len() - 1 {
break;
}
match command.name.as_str() {
"cat" => output_prev = cat(&command.args.clone()),
"echo" => output_prev = echo(&command.args.clone()),
"calc" => output_prev = calc(&command.args.clone()),
"ls" => output_prev = ls(command.args.clone()),
_ => {
let child = return_child(&command.name.clone(), &command.args);
match child {
Ok(mut child) => {
child.stdin.take().unwrap().write_all(output_prev.as_bytes()).unwrap();
output_prev = "".to_string();
child.stdout.unwrap().read_to_string(&mut output_prev).unwrap();
}
Err(_) => println!("{} failed", command.name.clone()),
}
}
}
}
let file_string = &pipe.commands[pipe.commands.len() - 1].name;
if file_string.contains('/') {
let file_vec: Vec<&str> = file_string.split('/').collect();
let mut parent_dir = String::new();
for (id, chunk) in file_vec.iter().enumerate() {
if id == file_vec.len() - 1 {
break;
}
let part = format!("{}/", chunk);
parent_dir.push_str(&part);
}
ensure_directory(&parent_dir, true).unwrap();
}
let file_path = &Path::new(file_string);
match pipe.commands[pipe.commands.len() - 1].redirection {
Redirection::Overwrite => {
let mut file = std::fs::File::create(file_path).unwrap();
file.write_all(output_prev.as_bytes()).unwrap();
return String::new();
}
Redirection::Append => {
let mut file = std::fs::OpenOptions::new()
.write(true)
.append(true)
.create(true)
.open(file_path)
.unwrap();
writeln!(file, "{}", output_prev).unwrap();
return String::new();
}
Redirection::NoOp => (),
}
match pipe.commands[pipe.commands.len() - 1].name.as_str() {
"cat" => cat(&pipe.commands[pipe.commands.len() - 1].args.clone()),
"echo" => echo(&pipe.commands[pipe.commands.len() - 1].args.clone()),
"calc" => calc(&pipe.commands[pipe.commands.len() - 1].args.clone()),
"ls" => ls(pipe.commands[pipe.commands.len() - 1].args.clone()),
_ => {
let child = return_child(
&pipe.commands[pipe.commands.len() - 1].name.clone(),
&pipe.commands[pipe.commands.len() - 1].args,
);
match child {
Ok(mut child) => {
child.stdin.take().unwrap().write_all(output_prev.as_bytes()).unwrap();
let mut output = String::new();
match child.stdout.take().unwrap().read_to_string(&mut output) {
Err(why) => return format!("ERROR: could not read cmd2 stdout: {}", why),
Ok(_) => output,
}
}
Err(_) => pipe.commands[pipe.commands.len() - 1].name.clone(),
}
}
}
}