-
-
Notifications
You must be signed in to change notification settings - Fork 38
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Cat: Implement with support for -snbE
- Loading branch information
Showing
5 changed files
with
220 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,7 @@ | ||
[workspace] | ||
members = [ | ||
"basename", | ||
"cat", | ||
"chroot", | ||
"clear", | ||
"coreutils_core", | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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"] } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} | ||
} | ||
} | ||
} |