From 5ea9722c00aecf772883e02cc5df7e7aa401a9c0 Mon Sep 17 00:00:00 2001 From: Edu Renesto Date: Fri, 27 Sep 2019 20:08:21 -0300 Subject: [PATCH 1/3] Core: Cat: Add initial code --- Cargo.toml | 1 + cat/Cargo.toml | 12 +++++ cat/build.rs | 19 ++++++++ cat/src/cat.yml | 42 +++++++++++++++++ cat/src/main.rs | 118 ++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 192 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 f313aafb..d97bcec3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,6 +3,7 @@ members = [ "coreutils_core", "basename", "dirname", + "cat", "echo", "false", "groups", 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..b99a098b --- /dev/null +++ b/cat/build.rs @@ -0,0 +1,19 @@ +use std::env; + +use clap::{ App, Shell, load_yaml }; + +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..b97a9da9 --- /dev/null +++ b/cat/src/cat.yml @@ -0,0 +1,42 @@ +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_nonprinting: + short: e + help: equivalent to -vE + - 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 + - show_tabs_nonprinting: + short: t + help: equivalent to -vT + - show_tabs: + long: show-tabs + short: T + - show_nonprinting: + long: show-nonprinting + short: v + help: use ^ and M- notation, except for LFD and TAB diff --git a/cat/src/main.rs b/cat/src/main.rs new file mode 100644 index 00000000..e93a9ec0 --- /dev/null +++ b/cat/src/main.rs @@ -0,0 +1,118 @@ +use std::path::PathBuf; +use std::io; +use std::io::BufReader; +use std::io::BufRead; +use std::fs::File; + +use clap::{load_yaml, App}; + +struct CatArgs { + number_nonblank: bool, + number: bool, + show_ends: bool, + squeeze_blank: bool, + show_tabs: bool, + show_nonprinting: bool +} + +impl Default for CatArgs { + fn default() -> CatArgs { + CatArgs { + number_nonblank: false, + number: false, + show_ends: false, + squeeze_blank: false, + show_tabs: false, + show_nonprinting: false + } + } +} + +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 mut args: CatArgs = CatArgs::default(); + + if matches.is_present("show_all") { + args.show_nonprinting = true; + args.show_ends = true; + args.show_tabs = true; + } + + if matches.is_present("number_nonblank") { + args.number_nonblank = true; + } + + if matches.is_present("show_ends_nonprinting") { + args.show_ends = true; + args.show_nonprinting = true; + } + + if matches.is_present("show_ends") { + args.show_ends = true; + } + + if matches.is_present("number") { + args.number = true; + } + + if matches.is_present("squeeze_blank") { + args.squeeze_blank = true; + } + + if matches.is_present("show_tabs_nonprinting") { + args.show_tabs = true; + args.show_nonprinting = true; + } + + if matches.is_present("show_tabs") { + args.show_tabs = true; + } + + if matches.is_present("show_nonprinting") { + args.show_nonprinting = true; + } + + for file in files { + match cat(&file, &args) { + Ok(_) => (), + Err(e) => { + eprintln!("cat: Failed to read from {:?}.\n{}", file, e); + } + } + } +} + +fn cat(file: &PathBuf, args: &CatArgs) -> io::Result<()> { + let file = File::open(file)?; + let reader = BufReader::new(file); + + let mut lines = reader.lines(); + + let mut line_no = 1; + + while let Some(line) = lines.next() { + let line = line?; + + 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; + } + + println!("{}{}", line, if args.show_ends { "$" } else { "" }); + } + + Ok(()) +} From 37d53ce2d2d0b28344b07b1a4d4b656a6b477daa Mon Sep 17 00:00:00 2001 From: Edu Renesto Date: Fri, 27 Sep 2019 20:20:09 -0300 Subject: [PATCH 2/3] Core: Cat: Add more flags --- cat/src/main.rs | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/cat/src/main.rs b/cat/src/main.rs index e93a9ec0..67af722b 100644 --- a/cat/src/main.rs +++ b/cat/src/main.rs @@ -7,12 +7,12 @@ use std::fs::File; use clap::{load_yaml, App}; struct CatArgs { - number_nonblank: bool, - number: bool, - show_ends: bool, - squeeze_blank: bool, - show_tabs: bool, - show_nonprinting: bool + number_nonblank: bool, // done + number: bool, // done + show_ends: bool, // done + squeeze_blank: bool, // done + show_tabs: bool, // done + show_nonprinting: bool // TODO } impl Default for CatArgs { @@ -101,7 +101,11 @@ fn cat(file: &PathBuf, args: &CatArgs) -> io::Result<()> { let mut line_no = 1; while let Some(line) = lines.next() { - let line = line?; + let mut line = line?; + + if args.squeeze_blank && line == "" { + continue; + } if args.number && !args.number_nonblank { print!("{:6} ", line_no); @@ -111,6 +115,10 @@ fn cat(file: &PathBuf, args: &CatArgs) -> io::Result<()> { line_no += 1; } + if args.show_tabs { + line = line.replace("\t", "^I"); + } + println!("{}{}", line, if args.show_ends { "$" } else { "" }); } From 74432838582176b70277f4ca3f5b5c9baee94c4b Mon Sep 17 00:00:00 2001 From: Edu Renesto Date: Thu, 3 Oct 2019 18:41:02 -0300 Subject: [PATCH 3/3] Core: Cat: Complete basic implementation --- cat/build.rs | 4 +- cat/src/cat.yml | 12 +----- cat/src/main.rs | 104 +++++++++++++++++++----------------------------- 3 files changed, 46 insertions(+), 74 deletions(-) diff --git a/cat/build.rs b/cat/build.rs index b99a098b..3f79eee9 100644 --- a/cat/build.rs +++ b/cat/build.rs @@ -1,6 +1,6 @@ use std::env; -use clap::{ App, Shell, load_yaml }; +use clap::{load_yaml, App, Shell}; fn main() { let yaml = load_yaml!("src/cat.yml"); @@ -8,7 +8,7 @@ fn main() { let out_dir = match env::var("OUT_DIR") { Ok(dir) => dir, - _ => return + _ => return, }; app.gen_completions("cat", Shell::Zsh, out_dir.clone()); diff --git a/cat/src/cat.yml b/cat/src/cat.yml index b97a9da9..805b1e1c 100644 --- a/cat/src/cat.yml +++ b/cat/src/cat.yml @@ -16,9 +16,6 @@ args: long: number-nonblank short: b help: number nonempty output lines, overrides -n - - show_ends_nonprinting: - short: e - help: equivalent to -vE - show_ends: long: show-ends short: E @@ -30,13 +27,8 @@ args: - squeeze_blank: long: squeeze-blank short: s - - show_tabs_nonprinting: - short: t - help: equivalent to -vT + help: collapse blank lines - show_tabs: long: show-tabs short: T - - show_nonprinting: - long: show-nonprinting - short: v - help: use ^ and M- notation, except for LFD and TAB + help: show tab characters diff --git a/cat/src/main.rs b/cat/src/main.rs index 67af722b..487edcc9 100644 --- a/cat/src/main.rs +++ b/cat/src/main.rs @@ -1,18 +1,18 @@ -use std::path::PathBuf; -use std::io; -use std::io::BufReader; -use std::io::BufRead; -use std::fs::File; +use std::{ + fs::File, + io::{self, BufRead, BufReader, Read}, + path::PathBuf, +}; -use clap::{load_yaml, App}; +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 - show_nonprinting: bool // TODO + number: bool, // done + show_ends: bool, // done + squeeze_blank: bool, // done + show_tabs: bool, // done } impl Default for CatArgs { @@ -23,9 +23,20 @@ impl Default for CatArgs { show_ends: false, squeeze_blank: false, show_tabs: false, - show_nonprinting: 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() { @@ -40,60 +51,29 @@ fn main() { } }; - let mut args: CatArgs = CatArgs::default(); - - if matches.is_present("show_all") { - args.show_nonprinting = true; - args.show_ends = true; - args.show_tabs = true; - } - - if matches.is_present("number_nonblank") { - args.number_nonblank = true; - } - - if matches.is_present("show_ends_nonprinting") { - args.show_ends = true; - args.show_nonprinting = true; - } - - if matches.is_present("show_ends") { - args.show_ends = true; - } - - if matches.is_present("number") { - args.number = true; - } - - if matches.is_present("squeeze_blank") { - args.squeeze_blank = true; - } - - if matches.is_present("show_tabs_nonprinting") { - args.show_tabs = true; - args.show_nonprinting = true; - } - - if matches.is_present("show_tabs") { - args.show_tabs = true; - } - - if matches.is_present("show_nonprinting") { - args.show_nonprinting = true; - } - - for file in files { - match cat(&file, &args) { - Ok(_) => (), - Err(e) => { - eprintln!("cat: Failed to read from {:?}.\n{}", file, e); + 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: &PathBuf, args: &CatArgs) -> io::Result<()> { - let file = File::open(file)?; +fn cat(file: R, args: CatArgs) -> io::Result<()> { let reader = BufReader::new(file); let mut lines = reader.lines();