diff --git a/.gitea/workflows/cargo-publish.yaml b/.gitea/workflows/cargo-publish.yaml deleted file mode 100644 index a76d7bf..0000000 --- a/.gitea/workflows/cargo-publish.yaml +++ /dev/null @@ -1,34 +0,0 @@ -# name: Cargo Check, Format, Fix, Test and publish -# on: -# push: -# branches: -# - master -# -# jobs: -# format: -# name: cargo CI -# permissions: -# # Give the default GITHUB_TOKEN write permission to commit and push the -# # added or changed files to the repository. -# contents: write -# package: write -# steps: -# - name: get repo -# uses: actions/checkout@v4 -# - name: install rust -# uses: dtolnay/rust-toolchain@stable -# run: cargo install cargo-workspaces -# - name: config custom registry -# run: | -# mkdir -p ~/.cargo/ -# echo "" > ~/.cargo/config.toml -# echo "[registry]" >> ~/.cargo/config.toml -# # echo 'token = "Bearer ${{ secrets.CARGO_PUBLISH_CRATESIO }}"' >> ~/.cargo/config.toml -# echo 'cscherr = "cscherr"' >> ~/.cargo/config.toml -# echo '[registries.cscherr]' >> ~/.cargo/config.toml -# echo 'index = "https://git.cscherr.de/PlexSheep/_cargo-index.git"' >> ~/.cargo/config.toml -# echo 'token = "Bearer ${{ GITEA_TOKEN }}"' >> ~/.cargo/config.toml -# cat ~/.cargo/config.toml -# - name: publish crates on git.cscherr.de -# run: | -# cargo workspaces publish --registry cscherr patch diff --git a/members/libpt-bintols/src/hedu/mod.rs b/members/libpt-bintols/src/hedu/mod.rs index bf4ce94..7d3e8a2 100644 --- a/members/libpt-bintols/src/hedu/mod.rs +++ b/members/libpt-bintols/src/hedu/mod.rs @@ -10,12 +10,13 @@ use anyhow::{bail, Result}; use libpt_log::{debug, error, trace, warn}; use std::io::{prelude::*, Read, SeekFrom}; -const BYTES_PER_LINE: usize = 16; -const LINE_SEP_HORIZ: char = '─'; -const LINE_SEP_VERT: char = '│'; +pub const BYTES_PER_LINE: usize = 16; +pub const LINE_SEP_HORIZ: char = '─'; +pub const LINE_SEP_VERT: char = '│'; +pub const CHAR_BORDER: &'static str = "|"; #[derive(Debug)] -pub struct HeduConfig { +pub struct Hedu { pub chars: bool, pub skip: usize, pub show_identical: bool, @@ -26,21 +27,182 @@ pub struct HeduConfig { rd_counter: usize, buf: [[u8; BYTES_PER_LINE]; 2], alt_buf: usize, + pub display_buf: String, + first_iter: bool, } -impl HeduConfig { +impl Hedu { pub fn new(chars: bool, skip: usize, show_identical: bool, limit: usize) -> Self { - HeduConfig { + Hedu { chars, skip, show_identical, limit, stop: false, - len: usize::MIN, - data_idx: usize::MIN, - rd_counter: usize::MIN, + len: 0, + data_idx: 0, + rd_counter: 0, buf: [[0; BYTES_PER_LINE]; 2], alt_buf: 0, + display_buf: String::new(), + first_iter: true, + } + } + #[inline] + pub fn display(&mut self) { + println!("{}", self.display_buf); + self.display_buf = String::new(); + } + #[inline] + pub fn sep(&mut self) { + if self.chars { + self.display_buf += &format!("{LINE_SEP_HORIZ}").repeat(80); + } else { + self.display_buf += &format!("{LINE_SEP_HORIZ}").repeat(59); + } + self.display(); + } + #[inline] + pub fn newline(&mut self) { + self.display_buf += "\n"; + self.display(); + } + fn dump_a_line(&mut self) { + self.display_buf += &format!("{:08X} {LINE_SEP_VERT} ", self.data_idx); + if self.len != 0 { + for i in 0..self.len { + if i as usize % BYTES_PER_LINE == BYTES_PER_LINE / 2 { + self.display_buf += " "; + } + self.display_buf += &format!("{:02X} ", self.buf[self.alt_buf][i]); + } + if self.len == BYTES_PER_LINE / 2 { + self.display_buf += " " + } + for i in 0..(BYTES_PER_LINE - self.len) { + if i as usize % BYTES_PER_LINE == BYTES_PER_LINE / 2 { + self.display_buf += " "; + } + self.display_buf += " "; + } + } else { + self.display_buf += &format!("{:49}", ""); + } + if self.chars { + self.display_buf += &format!("{LINE_SEP_VERT} "); + if self.len != 0 { + self.display_buf += CHAR_BORDER; + for i in 0..self.len { + self.display_buf += + &format!("{}", mask_chars(self.buf[self.alt_buf][i] as char)); + } + self.display_buf += CHAR_BORDER; + } else { + self.display_buf += &format!("{:^8}", ""); + } + } + self.display(); + } + + fn skip_lines(&mut self, data: &mut dyn DataSource) -> Result<()> { + trace!(buf = format!("{:?}", self.buf), "found a duplicating line"); + let start_line = self.data_idx; + while self.buf[0] == self.buf[1] && self.len == BYTES_PER_LINE { + self.rd_data(data)?; + } + self.display_buf += &format!( + "******** {LINE_SEP_VERT} {:<49}", + format!( + "(repeats {} lines)", + self.data_idx - start_line / (BYTES_PER_LINE) + 1 + ) + ); + if self.chars { + self.display_buf += &format!("{LINE_SEP_VERT}"); + } + trace!( + buf = format!("{:X?}", self.buf), + "dumping buf after line skip" + ); + self.alt_buf ^= 1; // read into the other buf, so we can check for sameness + self.display(); + Ok(()) + } + pub fn dump(&mut self, data: &mut dyn DataSource) -> Result<()> { + // skip a given number of bytes + if self.skip > 0 { + data.skip(self.skip)?; + self.rd_counter += self.skip; + debug!( + data_idx = self.data_idx, + "Skipped {}", + humanbytes(self.skip) + ); + } + + // print the head + self.display_buf += &format!("DATA IDX {LINE_SEP_VERT} DATA AS HEX"); + if self.chars { + self.display_buf += &format!("{:width$} {LINE_SEP_VERT} DATA AS CHAR", "", width = 37); + } + self.display(); + self.sep(); + + // data dump loop + self.rd_data(data)?; + self.data_idx = 0; + while self.len > 0 || self.first_iter { + self.first_iter = false; + + self.dump_a_line(); + + // loop breaker logic + if self.stop || self.len < BYTES_PER_LINE { + break; + } + self.rd_data(data)?; + + // after line logic + if self.buf[0] == self.buf[1] && self.len == BYTES_PER_LINE && !self.show_identical { + self.skip_lines(data)?; + } + } + self.data_idx += BYTES_PER_LINE; + + self.sep(); + self.display_buf += &format!( + "{:08X} {LINE_SEP_VERT} read total:\t\t {:<8} {:<15}", + self.rd_counter, + humanbytes(self.rd_counter), + format!("({} B)", self.rd_counter) + ); + if self.chars { + self.display_buf += &format!("{LINE_SEP_VERT}"); + } + self.display(); + Ok(()) + } + #[inline] + fn adjust_counters(&mut self) { + self.rd_counter += self.len; + self.data_idx += self.len; + } + + fn rd_data(&mut self, data: &mut dyn DataSource) -> Result<()> { + match data.read(&mut self.buf[self.alt_buf]) { + Ok(mut len) => { + if self.limit != 0 && self.rd_counter + (BYTES_PER_LINE - 1) >= self.limit { + len = self.limit % BYTES_PER_LINE; + self.stop = true; + } + self.len = len; + self.adjust_counters(); + return Ok(()); + } + Err(err) => { + error!("error while reading data: {err}"); + bail!(err) + } } } } @@ -62,88 +224,6 @@ impl DataSource for std::fs::File { } } -pub fn dump(data: &mut dyn DataSource, mut config: HeduConfig) -> Result<()> { - // prepare some variables - - // skip a given number of bytes - if config.skip > 0 { - data.skip(config.skip)?; - config.data_idx += config.skip; - config.data_idx += BYTES_PER_LINE - config.data_idx % BYTES_PER_LINE; - debug!("Skipped {}", humanbytes(config.skip)); - } - - // print the head - print!("DATA IDX {LINE_SEP_VERT} DATA AS HEX"); - if config.chars { - print!("{:width$} {LINE_SEP_VERT} FOO", "", width = 37); - } - println!(); - if config.chars { - println!("{}", format!("{LINE_SEP_HORIZ}").repeat(80)); - } else { - println!("{}", format!("{LINE_SEP_HORIZ}").repeat(59)); - } - - // data dump loop - rd_data(data, &mut config)?; - while config.len > 0 { - print!("{:08X} {LINE_SEP_VERT} ", config.data_idx); - for i in 0..config.len { - if i as usize % BYTES_PER_LINE == BYTES_PER_LINE / 2 { - print!(" "); - } - print!("{:02X} ", config.buf[config.alt_buf][i]); - } - if config.len == BYTES_PER_LINE / 2 { - print!(" ") - } - for i in 0..(BYTES_PER_LINE - config.len) { - if i as usize % BYTES_PER_LINE == BYTES_PER_LINE / 2 { - print!(" "); - } - print!(" "); - } - if config.chars { - print!("{LINE_SEP_VERT} |"); - for i in 0..config.len { - print!("{}", mask_chars(config.buf[config.alt_buf][i] as char)); - } - print!("|"); - } - println!(); - - // loop breaker logic - if config.stop { - break; - } - - // after line logic - rd_data(data, &mut config)?; - config.alt_buf ^= 1; // toggle the alt buf - if config.buf[0] == config.buf[1] && config.len == BYTES_PER_LINE && !config.show_identical - { - trace!( - buf = format!("{:?}", config.buf), - "found a duplicating line" - ); - let start_line = config.data_idx; - while config.buf[0] == config.buf[1] && config.len == BYTES_PER_LINE { - rd_data(data, &mut config)?; - config.data_idx += BYTES_PER_LINE; - } - println!( - "^^^^^^^^ {LINE_SEP_VERT} (repeats {} lines)", - config.data_idx - start_line - ); - } - // switch to the second half of the buf, the original half is stored the old buffer - // We detect duplicate lines with this - config.alt_buf ^= 1; // toggle the alt buf - } - Ok(()) -} - fn mask_chars(c: char) -> char { if c.is_ascii_graphic() { return c; @@ -157,32 +237,3 @@ fn mask_chars(c: char) -> char { return '�'; } } - -fn rd_data(data: &mut dyn DataSource, config: &mut HeduConfig) -> Result<()> { - match data.read(&mut config.buf[config.alt_buf]) { - Ok(mut len) => { - trace!( - conf = format!("{:?}", config), - eval = config.limit != 0 && config.rd_counter >= config.limit, - "reached limit?" - ); - if config.limit != 0 && config.rd_counter + (BYTES_PER_LINE - 1) >= config.limit { - trace!( - conf = format!("{:?}", config), - nlen = (config.limit % BYTES_PER_LINE), - "byte counter is farther than limit" - ); - len = config.limit % BYTES_PER_LINE; - config.stop = true; - } - config.len = len; - config.rd_counter += config.len; - config.data_idx += config.len; - return Ok(()); - } - Err(err) => { - error!("error while reading data: {err}"); - bail!(err) - } - } -} diff --git a/scripts/publish.sh b/scripts/publish.sh index 84036cb..db3c89a 100755 --- a/scripts/publish.sh +++ b/scripts/publish.sh @@ -6,5 +6,5 @@ NEW_VERSION=$(cat Cargo.toml | rg '^\s*version\s*=\s*"([^"]*)"\s*$' -or '$1') bash scripts/set_all_versions.sh $NEW_VERSION git add -A git commit -m "Release v$NEW_VERSION" -cargo ws publish --registry cscherr --amend --publish-as-is || cargo publish --registry cscherr -p libpt +cargo ws publish --registry cscherr --no-git-push --publish-as-is || cargo publish --registry cscherr -p libpt git push diff --git a/src/hedu/mod.rs b/src/hedu/mod.rs index 4e72881..83d5309 100644 --- a/src/hedu/mod.rs +++ b/src/hedu/mod.rs @@ -18,7 +18,7 @@ use libpt::{bintols::hedu::*, log::*}; use clap::Parser; use clap_verbosity_flag::{InfoLevel, Verbosity}; -use std::{fs::File, io::IsTerminal}; +use std::{fs::File, io::IsTerminal, path::PathBuf}; //// TYPES ///////////////////////////////////////////////////////////////////////////////////////// @@ -86,7 +86,8 @@ pub struct Cli { /// a data source, probably a file. /// /// If left empty or set as "-", the program will read from stdin. - pub data_source: Option, + // TODO: take many sources #60 + pub data_source: Vec, } //// IMPLEMENTATION //////////////////////////////////////////////////////////////////////////////// @@ -95,36 +96,58 @@ pub struct Cli { //// PRIVATE FUNCTIONS ///////////////////////////////////////////////////////////////////////////// fn main() { - let cli = cli_parse(); - let mut source: Box; - if cli.data_source.is_some() && cli.data_source.clone().is_some_and(|val| val != "-") { - let data_source = cli.data_source.unwrap(); - trace!("Trying to open '{}'", data_source); - source = match File::open(&data_source) { - Ok(file) => Box::new(file), - Err(err) => { - error!("Could not open file '{}': {err}", data_source); - std::process::exit(1); + let mut cli = cli_parse(); + let mut sources: Vec> = Vec::new(); + if cli.data_source.len() > 0 && cli.data_source[0] != "-" { + for data_source in &cli.data_source { + let data_source: PathBuf = PathBuf::from(data_source); + if data_source.is_dir() { + warn!("Not a file {:?}, skipping", data_source); + // std::process::exit(1); + continue; } - }; + trace!("Trying to open '{:?}'", data_source); + match File::open(&data_source) { + Ok(file) => sources.push(Box::new(file)), + Err(err) => { + error!("Could not open '{:?}': {err}", data_source); + std::process::exit(1); + } + }; + } } else { - trace!("Trying to open stdout"); + trace!("Trying to open stdin"); let stdin = std::io::stdin(); if stdin.is_terminal() { warn!("Refusing to dump from interactive terminal"); std::process::exit(2) } - source = Box::new(stdin); + // just for the little header + cli.data_source = Vec::new(); + cli.data_source.push(format!("stdin")); + sources.push(Box::new(stdin)); } - - match dump( - &mut *source, - HeduConfig::new(cli.chars, cli.skip, cli.show_identical, cli.limit), - ) { - Ok(_) => (), - Err(err) => { - error!("Could not dump data of file: {err}"); - std::process::exit(3); + for (i, source) in sources.iter_mut().enumerate() { + let mut config = Hedu::new(cli.chars, cli.skip, cli.show_identical, cli.limit); + // FIXME: find a better way to get the file name + // Currently, skipped sources make an extra newline here. + match config.chars { + false => { + println!("{:─^59}", format!(" {} ", cli.data_source[i])); + } + true => { + println!("{:─^80}", format!(" {} ", cli.data_source[i])); + } + } + match config.dump(&mut **source) { + Ok(_) => (), + Err(err) => { + error!("Could not dump data of file: {err}"); + std::process::exit(3); + } + } + if i < cli.data_source.len() - 1 { + config.newline(); } } }