From 25fa607f30a20bb21f5e9b63b06ba31af86c47aa Mon Sep 17 00:00:00 2001 From: Anubhab Ghosh Date: Mon, 28 Oct 2019 14:43:23 +0530 Subject: [PATCH] Cat: Implement with support for -snbE --- Cargo.toml | 1 + cat/Cargo.toml | 14 +++++ cat/build.rs | 19 ++++++ cat/src/cat.yml | 25 ++++++++ cat/src/main.rs | 161 ++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 220 insertions(+) create mode 100644 cat/Cargo.toml create mode 100644 cat/build.rs create mode 100644 cat/src/cat.yml create mode 100644 cat/src/main.rs diff --git a/Cargo.toml b/Cargo.toml index d6f9b85a..d5a0ef61 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,7 @@ [workspace] members = [ "basename", + "cat", "chroot", "clear", "coreutils_core", diff --git a/cat/Cargo.toml b/cat/Cargo.toml new file mode 100644 index 00000000..3c8736bb --- /dev/null +++ b/cat/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "cat" +version = "0.1.0" +authors = ["Anubhab Ghosh "] +build = "build.rs" +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[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..55193326 --- /dev/null +++ b/cat/src/cat.yml @@ -0,0 +1,25 @@ +name: cat +version: "0.0.0" +author: Anubhab Ghosh +about: "Concatenate and display files" +args: + - number: + help: Number all output lines + long: number + short: n + - numberNonblank: + help: Number nonempty output lines, overrides -n + long: number-nonblank + short: b + - showEnds: + help: Display $ at end of each line + long: show-ends + short: E + - squeezeBlank: + help: Squeeze multiple adjacent empty lines, causing the output to be single spaced + long: squeeze-blank + short: s + - FILE: + help: "The file operands are processed in command-line order" + long_help: "The file operands are processed in command-line order. If file is a single dash (‘-’) or absent, cat reads from the standard input." + multiple: true \ No newline at end of file diff --git a/cat/src/main.rs b/cat/src/main.rs new file mode 100644 index 00000000..b73a5663 --- /dev/null +++ b/cat/src/main.rs @@ -0,0 +1,161 @@ +use std::{ + fs::File, + io::{prelude::*, stdin, BufReader, ErrorKind}, +}; + +use clap::{load_yaml, App, AppSettings::ColoredHelp, ArgMatches}; + +fn main() { + let yaml = load_yaml!("cat.yml"); + let matches = App::from_yaml(yaml).settings(&[ColoredHelp]).get_matches(); + + let flags = CatFlags::from_matches(&matches); + + let files: Vec = match matches.values_of("FILE") { + Some(m) => m.map(String::from).collect(), + None => vec![String::from("-")], + }; + + let mut exit_code = 0; + + let mut line_number = 1; + let mut last_line_empty = false; + for filename in files.iter() { + if filename == "-" { + loop { + let mut line = String::new(); + + match stdin().read_line(&mut line) { + Ok(0) => break, // EOF + Ok(_) => { + line.pop(); + print_line(line, flags, &mut line_number, &mut last_line_empty) + }, + Err(error) => eprintln!("cat: stdin: {}", error), + } + } + } else { + match File::open(filename) { + Ok(file) => { + for line in BufReader::new(file).lines() { + let line = match line { + Ok(line) => line, + Err(error) => { + eprintln!("cat: {}: I/O error: {}", filename, error); + exit_code = 1; + break; + }, + }; + print_line(line, flags, &mut line_number, &mut last_line_empty); + } + }, + Err(e) => { + exit_code = 1; + match e.kind() { + ErrorKind::NotFound => { + eprintln!("cat: {}: No such file or directory", filename) + }, + ErrorKind::PermissionDenied => { + eprintln!("cat: {}: Permission denied", filename) + }, + _ => eprintln!("cat: {}: Unknown error", filename), + } + }, + }; + } + } + + std::process::exit(exit_code); +} + +#[derive(Debug, Clone, Copy)] +struct CatFlags { + pub number: bool, + pub number_nonblank: bool, + pub show_ends: bool, + pub squeeze_blank: bool, +} + +impl CatFlags { + pub fn from_matches(matches: &ArgMatches) -> Self { + let flags = CatFlags { + number: matches.is_present("number"), + number_nonblank: matches.is_present("numberNonblank"), + show_ends: matches.is_present("showEnds"), + squeeze_blank: matches.is_present("squeezeBlank"), + }; + + flags + } +} + +fn print_line(line: String, flags: CatFlags, line_number: &mut usize, last_line_empty: &mut bool) { + if flags.squeeze_blank { + if line.is_empty() { + if !*last_line_empty { + if !flags.number_nonblank && flags.number { + if flags.show_ends { + println!("{:6} $", line_number); + } else { + println!("{:6} ", line_number); + } + + *line_number += 1; + } else { + if flags.show_ends { + println!("$"); + } else { + println!(""); + } + } + } + *last_line_empty = true; + } else { + if flags.number || flags.number_nonblank { + if flags.show_ends { + println!("{:6} {}$", line_number, line); + } else { + println!("{:6} {}", line_number, line); + } + *line_number += 1; + } else { + if flags.show_ends { + println!("{}$", line); + } else { + println!("{}", line); + } + } + *last_line_empty = false; + } + } else { + if flags.number_nonblank { + if !line.is_empty() { + if flags.show_ends { + println!("{:6} {}$", line_number, line); + } else { + println!("{:6} {}", line_number, line); + } + *line_number += 1; + } else { + if flags.show_ends { + println!("$"); + } else { + println!(""); + } + } + } else if flags.number { + if flags.show_ends { + println!("{:6} {}$", line_number, line); + } else { + println!("{:6} {}", line_number, line); + } + *line_number += 1; + } else { + if flags.show_ends { + println!("{}$", line); + } else { + println!("{}", line); + } + } + } +}