use async_trait::async_trait;
use std::path::Path;
use super::{Builtin, Context, read_text_file};
use crate::error::Result;
use crate::interpreter::ExecResult;
pub struct Cat;
#[async_trait]
impl Builtin for Cat {
async fn execute(&self, ctx: Context<'_>) -> Result<ExecResult> {
if let Some(r) = super::check_help_version(
ctx.args,
"Usage: cat [OPTION]... [FILE]...\nConcatenate FILE(s) to standard output.\n\n -n\t\tnumber all output lines\n -v\t\tuse ^ and M- notation\n -e\t\tequivalent to -v (simplified)\n -t\t\tequivalent to -v (simplified)\n --help\tdisplay this help and exit\n --version\toutput version information and exit\n",
Some("cat (bashkit) 0.1"),
) {
return Ok(r);
}
let mut output = String::new();
let mut show_nonprinting = false;
let mut number_lines = false;
let mut files: Vec<&str> = Vec::new();
for arg in ctx.args {
if arg.starts_with('-') && arg.len() > 1 && !arg.starts_with("--") {
for ch in arg[1..].chars() {
match ch {
'v' => show_nonprinting = true,
'n' => number_lines = true,
'e' => show_nonprinting = true, 't' => show_nonprinting = true, _ => {}
}
}
} else {
files.push(arg);
}
}
let mut raw = String::new();
if files.is_empty() {
if let Some(stdin) = ctx.stdin {
raw.push_str(stdin);
}
} else {
for file in &files {
if *file == "-" {
if let Some(stdin) = ctx.stdin {
raw.push_str(stdin);
}
} else {
let path = if Path::new(file).is_absolute() {
file.to_string()
} else {
ctx.cwd.join(file).to_string_lossy().to_string()
};
let text = match read_text_file(&*ctx.fs, Path::new(&path), "cat").await {
Ok(t) => t,
Err(e) => return Ok(e),
};
raw.push_str(&text);
}
}
}
if show_nonprinting {
for ch in raw.chars() {
match ch {
'\n' | '\t' => output.push(ch), c if (c as u32) < 32 => {
output.push('^');
output.push((c as u8 + 64) as char);
}
'\x7f' => {
output.push('^');
output.push('?');
}
c => output.push(c),
}
}
} else {
output = raw;
}
if number_lines {
let lines: Vec<&str> = output.split('\n').collect();
let mut numbered = String::new();
for (i, line) in lines.iter().enumerate() {
if i < lines.len() - 1 || !line.is_empty() {
numbered.push_str(&format!(" {}\t{}", i + 1, line));
if i < lines.len() - 1 {
numbered.push('\n');
}
}
}
output = numbered;
}
Ok(ExecResult::ok(output))
}
}