diff --git a/Cargo.toml b/Cargo.toml index dc91a0f6..a368737f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,6 +3,7 @@ members = [ "coreutils_core", "basename", "dirname", + "cat", "echo", "env", "false", diff --git a/cat/Cargo.toml b/cat/Cargo.toml new file mode 100644 index 00000000..8f2fe644 --- /dev/null +++ b/cat/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "cat" +version = "0.1.0" +authors = ["Edu Renesto "] +build = "build.rs" +edition = "2018" + +[dependencies] +clap = { version = "^2.33.0", features = ["yaml", "wrap_help"] } + +[build-dependencies] +clap = { version = "^2.33.0", features = ["yaml"] } diff --git a/cat/build.rs b/cat/build.rs new file mode 100644 index 00000000..3f79eee9 --- /dev/null +++ b/cat/build.rs @@ -0,0 +1,19 @@ +use std::env; + +use clap::{load_yaml, App, Shell}; + +fn main() { + let yaml = load_yaml!("src/cat.yml"); + let mut app = App::from_yaml(yaml); + + let out_dir = match env::var("OUT_DIR") { + Ok(dir) => dir, + _ => return, + }; + + app.gen_completions("cat", Shell::Zsh, out_dir.clone()); + app.gen_completions("cat", Shell::Fish, out_dir.clone()); + app.gen_completions("cat", Shell::Bash, out_dir.clone()); + app.gen_completions("cat", Shell::PowerShell, out_dir.clone()); + app.gen_completions("cat", Shell::Elvish, out_dir); +} diff --git a/cat/src/cat.yml b/cat/src/cat.yml new file mode 100644 index 00000000..805b1e1c --- /dev/null +++ b/cat/src/cat.yml @@ -0,0 +1,34 @@ +name: cat +version: "0.0.0" +author: Edu Renesto +about: "Concatenate FILE(s) to standard output.\n +\ With no FILE, or when FILE is -, read standard input." +args: + - FILE: + help: The files to be concatenated. + required: false + multiple: true + - show_all: + long: show-all + short: A + help: equivalent to -vET + - number_nonblank: + long: number-nonblank + short: b + help: number nonempty output lines, overrides -n + - show_ends: + long: show-ends + short: E + help: display $ at end of each line + - number: + long: number + short: n + help: number all output lines + - squeeze_blank: + long: squeeze-blank + short: s + help: collapse blank lines + - show_tabs: + long: show-tabs + short: T + help: show tab characters diff --git a/cat/src/main.rs b/cat/src/main.rs new file mode 100644 index 00000000..487edcc9 --- /dev/null +++ b/cat/src/main.rs @@ -0,0 +1,106 @@ +use std::{ + fs::File, + io::{self, BufRead, BufReader, Read}, + path::PathBuf, +}; + +use clap::{load_yaml, App, ArgMatches}; + +#[derive(Copy, Clone)] +struct CatArgs { + number_nonblank: bool, // done + number: bool, // done + show_ends: bool, // done + squeeze_blank: bool, // done + show_tabs: bool, // done +} + +impl Default for CatArgs { + fn default() -> CatArgs { + CatArgs { + number_nonblank: false, + number: false, + show_ends: false, + squeeze_blank: false, + show_tabs: false, + } + } +} + +impl CatArgs { + pub fn from(matches: &ArgMatches) -> CatArgs { + CatArgs { + number_nonblank: matches.is_present("number_nonblank"), + number: matches.is_present("number"), + show_ends: matches.is_present("show_all") || matches.is_present("show_ends"), + squeeze_blank: matches.is_present("squeeze_blank"), + show_tabs: matches.is_present("show_all") || matches.is_present("show_tabs"), + } + } +} + +fn main() { + let yaml = load_yaml!("cat.yml"); + let matches = App::from_yaml(yaml).get_matches(); + + let files = { + if let Some(vals) = matches.values_of("FILE") { + vals.map(|value| PathBuf::from(value.to_owned())).collect() + } else { + vec![PathBuf::from("-")] + } + }; + + let args = CatArgs::from(&matches); + + for file in &files { + let result = { + if *file == PathBuf::from("-") { + let stdin = io::stdin(); + cat(stdin.lock(), args) + } else { + match File::open(file) { + Ok(file) => cat(file, args), + Err(e) => Err(e), + } + } + }; + + match result { + Ok(_) => {}, + Err(e) => eprintln!("cat: Failed to read from {:?}.\n{}", file, e), + } + } +} + +fn cat(file: R, args: CatArgs) -> io::Result<()> { + let reader = BufReader::new(file); + + let mut lines = reader.lines(); + + let mut line_no = 1; + + while let Some(line) = lines.next() { + let mut line = line?; + + if args.squeeze_blank && line == "" { + continue; + } + + if args.number && !args.number_nonblank { + print!("{:6} ", line_no); + line_no += 1; + } else if args.number_nonblank && line != "" { + print!("{:6} ", line_no); + line_no += 1; + } + + if args.show_tabs { + line = line.replace("\t", "^I"); + } + + println!("{}{}", line, if args.show_ends { "$" } else { "" }); + } + + Ok(()) +}