Skip to content

Commit

Permalink
Cat: Implement with support for -snbE
Browse files Browse the repository at this point in the history
  • Loading branch information
argentite committed Nov 4, 2019
1 parent 57d1c46 commit 25fa607
Show file tree
Hide file tree
Showing 5 changed files with 220 additions and 0 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
[workspace]
members = [
"basename",
"cat",
"chroot",
"clear",
"coreutils_core",
Expand Down
14 changes: 14 additions & 0 deletions cat/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[package]
name = "cat"
version = "0.1.0"
authors = ["Anubhab Ghosh <anubhabghosh.me@gmail.com>"]
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"] }
19 changes: 19 additions & 0 deletions cat/build.rs
Original file line number Diff line number Diff line change
@@ -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);
}
25 changes: 25 additions & 0 deletions cat/src/cat.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
name: cat
version: "0.0.0"
author: Anubhab Ghosh <anubhabghosh.me@gmail.com>
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
161 changes: 161 additions & 0 deletions cat/src/main.rs
Original file line number Diff line number Diff line change
@@ -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<String> = 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);
}
}
}
}

0 comments on commit 25fa607

Please sign in to comment.