summaryrefslogtreecommitdiffstats
path: root/src/lib.rs
diff options
context:
space:
mode:
authorToby Vincent <tobyv13@gmail.com>2022-12-02 19:17:45 -0600
committerToby Vincent <tobyv13@gmail.com>2022-12-02 19:17:45 -0600
commit81516ffa14bc06f91b7e51cafe175cb620dc1be5 (patch)
tree89a8979a03ede219a5cfc5a38b772e2f5117f46e /src/lib.rs
parent5428e226a372e29f7e757c51e13c91416a30e59a (diff)
feat: impl nonprintable parsing
Diffstat (limited to 'src/lib.rs')
-rw-r--r--src/lib.rs100
1 files changed, 100 insertions, 0 deletions
diff --git a/src/lib.rs b/src/lib.rs
index ab2ff99..0d45606 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -5,3 +5,103 @@ 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'?']
+}