1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
|
pub use crate::cli::{Cli, Opts};
pub use crate::error::{Error, Result};
pub use crate::input::Input;
mod cli;
mod error;
mod input;
pub const HELP: &str = r#"Usage: cat [OPTION]... [FILE]...
Concatenate FILE(s) to standard output.
With no FILE, or when FILE is -, read standard input.
-A, --show-all equivalent to -vET
-b, --number-nonblank number nonempty output lines, overrides -n
-e equivalent to -vE
-E, --show-ends display $ at end of each line
-n, --number number all output lines
-s, --squeeze-blank suppress repeated empty output lines
-t equivalent to -vT
-T, --show-tabs display TAB characters as ^I
-u (ignored)
-v, --show-nonprinting use ^ and M- notation, except for LFD and TAB
--help display this help and exit
--version output version information and exit
Examples:
cat f - g Output f's contents, then standard input, then g's contents.
cat Copy standard input to standard output."#;
pub fn run(cli: Cli) -> Result<()> {
use std::io::{BufReader, Read, Write};
let stdout = std::io::stdout();
let mut writer = stdout.lock();
for file in cli.files.into_iter() {
let mut reader = BufReader::new(file.reader()?);
let mut nonblank_line_nr = 0;
let mut buf = Vec::new();
reader.read_to_end(&mut buf)?;
for (index, arr) in buf.split(|c| *c == b'\n').enumerate() {
if arr.is_empty() && cli.opts.squeeze_blank {
continue;
}
let mut line = Vec::new();
for c in arr {
let parsed = match *c {
b'\t' if cli.opts.show_tabs => vec![b'^', b'I'],
b'\t' => vec![b'\t'],
c if cli.opts.show_nonprinting => parse_nonprinting_char(c),
c => vec![c],
};
line.extend(parsed)
}
if cli.opts.show_ends {
line.push(b'$');
}
line.push(b'\n');
if cli.opts.number_nonblank {
if !line.is_empty() {
nonblank_line_nr += 1;
write!(writer, "{:>6} ", nonblank_line_nr)?;
}
} else if cli.opts.number {
write!(writer, "{:>6} ", index + 1)?;
}
writer.write_all(&line)?
}
}
Ok(())
}
fn parse_nonprinting_char(c: u8) -> Vec<u8> {
match c {
c @ 0..=31 => into_ctrl_char(c),
c @ 32..=126 => vec![c],
127 => into_unknown_char(),
c @ 128.. => into_meta_char(c),
}
}
fn into_ctrl_char(c: u8) -> Vec<u8> {
vec![b'^', c + 64]
}
fn into_meta_char(c: u8) -> Vec<u8> {
let mut buf = "M-^".as_bytes().to_vec();
match c - 128 {
c @ 0..=31 => buf.extend(vec![b'^', c + 64]),
c @ 32..=127 => buf.push(c),
128.. => buf.extend(into_unknown_char()),
};
buf
}
fn into_unknown_char() -> Vec<u8> {
vec![b'^', b'?']
}
|