Compare commits

...

26 Commits

Author SHA1 Message Date
Christoph J. Scherr b6ea9e142a chore: fix release scripts
cargo devel CI / cargo CI (push) Successful in 1m12s Details
2024-05-24 09:32:24 +02:00
Christoph J. Scherr d331c89a79 Merge pull request 'v0.2.0' (#7) from devel into master
cargo devel CI / cargo CI (push) Has been cancelled Details
Reviewed-on: #7
2024-05-24 09:31:16 +02:00
PlexSheep d51c4dde90 automatic cargo CI changes 2024-05-24 07:30:51 +00:00
Christoph J. Scherr 3d9ddac237 chore: remove doctest from CI (this is a binary)
cargo devel CI / cargo CI (push) Successful in 1m22s Details
2024-05-24 09:29:32 +02:00
Christoph J. Scherr 3cb2f2a778 chore: bump to v0.2.0
cargo devel CI / cargo CI (push) Failing after 1m13s Details
2024-05-24 09:25:25 +02:00
Christoph J. Scherr 256b362f39 fix(cli): accept "-" for stdin marker at any position #2
cargo devel CI / cargo CI (push) Has been cancelled Details
2024-05-24 09:25:08 +02:00
Christoph J. Scherr 18008e9c41 chore: add log to libpt features 2024-05-24 09:25:08 +02:00
Christoph J. Scherr 7ee73c4f64 chore: fix regression from #5, don't show time if not in meta logging mode 2024-05-24 09:25:08 +02:00
Christoph J. Scherr 7929b0af2f docs(help): dirs cannot be dumped #4 2024-05-24 09:25:08 +02:00
Christoph J. Scherr fb145a7ff4 fix!: less dependencies and simpler cli #6 2024-05-24 09:25:08 +02:00
Christoph J. Scherr f6c9c3c820 chore: update libpt and adjust to breaking changes #5
cargo devel CI / cargo CI (push) Failing after 1m21s Details
2024-05-23 14:41:35 +02:00
Christoph J. Scherr 03188c8416 Merge branch 'devel' of https://git.cscherr.de/PlexSheep/hedu into devel
cargo devel CI / cargo CI (push) Failing after 1m49s Details
2024-05-12 17:28:05 +02:00
Christoph J. Scherr 21731687f4 ci add cargo test of docs 2024-05-12 17:27:31 +02:00
PlexSheep 8091e9ce1e automatic cargo CI changes 2024-02-16 15:39:04 +00:00
Christoph J. Scherr 04f683807f
ci add clippy rustup
cargo devel CI / cargo CI (push) Successful in 4m1s Details
2024-02-16 16:35:09 +01:00
Christoph J. Scherr 138ad5d969
ci stuff
cargo devel CI / cargo CI (push) Failing after 24s Details
2024-02-16 16:11:46 +01:00
Christoph J. Scherr 9b4ef82b10
fix ci
cargo devel CI / cargo CI (push) Successful in 3m55s Details
2024-02-09 18:38:12 +01:00
Christoph J. Scherr 8ac55cb6e4
print spaces instead of tabs for higher compatability
cargo devel CI / cargo CI (push) Successful in 1m37s Details
2024-02-02 00:00:50 +01:00
Christoph J. Scherr c9782ef6dc
fix name in help
cargo devel CI / cargo CI (push) Successful in 2m2s Details
2024-02-01 23:29:50 +01:00
Christoph J. Scherr 1cb492d90a
fix line skip counter #1
cargo devel CI / cargo CI (push) Successful in 1m23s Details
2024-02-01 23:27:28 +01:00
Christoph J. Scherr 7bea6ede4c
update some documentation and move code around
cargo devel CI / cargo CI (push) Successful in 1m28s Details
2024-02-01 23:21:01 +01:00
Christoph J. Scherr e829027eb5
fix #1 last line all zero
cargo devel CI / cargo CI (push) Successful in 1m27s Details
we toggle the buffer one time to much after the duplicated lines are
processed. This commit just removes that one toggle too much.
2024-02-01 23:07:16 +01:00
Christoph J. Scherr f5ec931b33
import libpt's remaining hedu parts to here
cargo devel CI / cargo CI (push) Successful in 2m9s Details
2024-02-01 22:54:07 +01:00
Christoph J. Scherr 470b0c9121
remove a done todo 2024-02-01 22:38:26 +01:00
PlexSheep 83a56fdb48 automatic cargo CI changes 2024-02-01 21:24:46 +00:00
Christoph J. Scherr 8f050b2078
publish true
cargo devel CI / cargo CI (push) Successful in 1m32s Details
2024-02-01 22:23:14 +01:00
6 changed files with 306 additions and 91 deletions

View File

@ -16,9 +16,11 @@ jobs:
- name: get repo
uses: actions/checkout@v4
- name: install rust
uses: dtolnay/rust-toolchain@stable
uses: https://github.com/dtolnay/rust-toolchain@stable
- name: install additional rust things
run: rustup component add rustfmt
run: |
rustup component add rustfmt
rustup component add clippy
- name: config custom registry
run: |
mkdir -p ~/.cargo/
@ -28,16 +30,16 @@ jobs:
echo '[registries.cscherr]' >> ~/.cargo/config.toml
echo 'index = "https://git.cscherr.de/PlexSheep/_cargo-index.git"' >> ~/.cargo/config.toml
cat ~/.cargo/config.toml
- name: cargo check
run: cargo check --all-features --all-targets
- name: cargo fix
run: cargo fix --all-features --all-targets
- name: cargo clippy check
run: cargo clippy --all-features --all-targets --workspace
- name: cargo clippy fix
run: cargo clippy --fix --all-features --all-targets --workspace
- name: cargo fmt
run: cargo fmt --all
- name: cargo test
run: cargo test --all-features --all-targets
run: cargo test --all-features --all-targets --workspace
- name: commit back to repository
uses: stefanzweifel/git-auto-commit-action@v5
uses: https://github.com/stefanzweifel/git-auto-commit-action@v5
with:
# Optional. Commit message for the created commit.
# Defaults to "Apply automatic changes"

View File

@ -19,7 +19,9 @@ jobs:
- name: install rust
uses: dtolnay/rust-toolchain@stable
- name: install additional rust things
run: rustup component add rustfmt
run: |
rustup component add rustfmt
rustup component add clippy
- name: config custom registry
run: |
mkdir -p ~/.cargo/
@ -29,14 +31,14 @@ jobs:
echo '[registries.cscherr]' >> ~/.cargo/config.toml
echo 'index = "https://git.cscherr.de/PlexSheep/_cargo-index.git"' >> ~/.cargo/config.toml
cat ~/.cargo/config.toml
- name: cargo check
run: cargo check --all-features --all-targets
- name: cargo fix
run: cargo fix --all-features --all-targets
- name: cargo clippy check
run: cargo clippy --all-features --all-targets --workspace
- name: cargo clippy fix
run: cargo clippy --fix --all-features --all-targets --workspace
- name: cargo fmt
run: cargo fmt --all
- name: cargo test
run: cargo test --all-features --all-targets
run: cargo test --all-features --all-targets --workspace
- name: commit back to repository
uses: stefanzweifel/git-auto-commit-action@v5
with:

View File

@ -1,8 +1,8 @@
[package]
name = "hedu"
version = "0.1.0"
version = "0.2.0"
edition = "2021"
publish = false
publish = true
authors = ["Christoph J. Scherr <software@cscherr.de>"]
license = "GPL-3.0-or-later"
description = "Hex dumping tool written in Rust"
@ -12,7 +12,6 @@ repository = "https://git.cscherr.de/PlexSheep/hedu"
keywords = ["hexdumper"]
[dependencies]
libpt = { version = "0.3.11", features = ["log", "bintols"] }
libpt = { version = "0.5.1", features = ["bintols", "log"], default-features = false }
clap = { version = "4.4.4", features = ["derive", "help"] }
clap-num = { version = "1.0.2" }
clap-verbosity-flag = { version = "2.0.1" }
anyhow = "1.0.79"

View File

@ -4,10 +4,10 @@ NEW_VERSION=$(cat Cargo.toml | rg '^\s*version\s*=\s*"([^"]*)"\s*$' -or '$1')
GIT_COMMIT_SHA=$(git rev-parse HEAD)
REPO=${PWD##*/} # name of cwd
BODY="
$(git log $(git describe --tags --abbrev=0)..HEAD --pretty="- %s" --oneline --decorate)
$(git log $(git describe --tags --abbrev=0)..HEAD --pretty="- %s" --oneline --no-decorate)
"
USER=PlexSheep
git tag "v$NEW_VERSION-test" || echo "could not tag"
git tag "v$NEW_VERSION" || echo "could not tag"
curl -X 'POST' \
'https://git.cscherr.de/api/v1/repos/PlexSheep/'$REPO'/releases' \
-H 'accept: application/json' \

250
src/dumper.rs Normal file
View File

@ -0,0 +1,250 @@
//! # Underlying mechanisms of how hedu works with data
use std::io::{prelude::*, Read, SeekFrom};
use anyhow::Result;
use libpt::bintols::display::humanbytes;
use libpt::log::{debug, error, trace, warn};
pub const BYTES_PER_LINE: usize = 16;
pub const LINE_SEP_HORIZ: char = '─';
pub const LINE_SEP_VERT: char = '│';
pub const CHAR_BORDER: &str = "|";
pub trait DataSource: Read {
fn skip(&mut self, length: usize) -> std::io::Result<()>;
}
impl DataSource for std::io::Stdin {
fn skip(&mut self, _length: usize) -> std::io::Result<()> {
warn!("can't skip bytes for the stdin!");
Ok(())
}
}
impl DataSource for std::fs::File {
fn skip(&mut self, length: usize) -> std::io::Result<()> {
self.seek(SeekFrom::Current(length as i64))?;
// returns the new position from the start, we don't need it here.
Ok(())
}
}
#[derive(Debug)]
pub struct Hedu {
pub chars: bool,
pub skip: usize,
pub show_identical: bool,
pub limit: usize,
stop: bool,
len: usize,
data_idx: usize,
rd_counter: usize,
buf: [[u8; BYTES_PER_LINE]; 2],
alt_buf: usize,
pub display_buf: String,
first_iter: bool,
}
impl Hedu {
/// create a new hedu struct, initializing all kinds of configs and values needed while running
pub fn new(chars: bool, skip: usize, show_identical: bool, limit: usize) -> Self {
Hedu {
chars,
skip,
show_identical,
limit,
stop: false,
len: 0,
data_idx: 0,
rd_counter: 0,
buf: [[0; BYTES_PER_LINE]; 2],
alt_buf: 0,
display_buf: String::new(),
first_iter: true,
}
}
/// print the display buffer to the stdout
#[inline]
pub fn display(&mut self) {
println!("{}", self.display_buf);
self.display_buf = String::new();
}
/// display a separator line
#[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();
}
/// display a newline
#[inline]
pub fn newline(&mut self) {
self.display_buf += "\n";
self.display();
}
/// display the dump of a single line of data
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 % 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 % 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();
}
/// skip duplicate lines and display skip info
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) / BYTES_PER_LINE
)
);
if self.chars {
self.display_buf += &format!("{LINE_SEP_VERT}");
}
trace!(
buf = format!("{:X?}", self.buf),
"dumping buf after line skip"
);
self.display();
Ok(())
}
/// dump a data sources contents according to the set configs
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: {:<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(())
}
/// adjust the counters of the hedu struct, adding self.len
#[inline]
fn adjust_counters(&mut self) {
self.rd_counter += self.len;
self.data_idx += self.len;
}
/// read data from the data source
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();
Ok(())
}
Err(err) => {
error!("error while reading data: {err}");
Err(err.into())
}
}
}
}
/// interpret characters for the --chars option
fn mask_chars(c: char) -> char {
if c.is_ascii_graphic() {
c
} else if c == '\n' {
return '↩';
} else if c == ' ' {
return '␣';
} else if c == '\t' {
return '⭾';
} else {
return '<27>';
}
}

View File

@ -1,67 +1,36 @@
//! # Executable for the hedu submodule
//! # A simple hexdumper with a somewhat fancy format
//!
//! Dump data to a fancy format.
//// ATTRIBUTES ////////////////////////////////////////////////////////////////////////////////////
// we want docs
#![warn(missing_docs)]
#![warn(rustdoc::missing_crate_level_docs)]
// we want Debug everywhere.
#![warn(missing_debug_implementations)]
// enable clippy's extra lints, the pedantic version
//! Dump data from any readable source, such as the stdin or a file
#![warn(clippy::pedantic)]
//// IMPORTS ///////////////////////////////////////////////////////////////////////////////////////
use libpt::{bintols::hedu::*, log::*};
use clap::Parser;
use clap_verbosity_flag::{InfoLevel, Verbosity};
use std::{fs::File, io::IsTerminal, path::PathBuf};
//// TYPES /////////////////////////////////////////////////////////////////////////////////////////
use libpt::log::{error, trace, warn, Level, Logger};
//// CONSTANTS /////////////////////////////////////////////////////////////////////////////////////
/// short about section displayed in help
const ABOUT_ROOT: &'static str = r##"
Dumps data in fancy formats.
"##;
/// longer about section displayed in help, is combined with [the short help](ABOUT_ROOT)
static LONG_ABOUT_ROOT: &'static str = r##"
use clap::Parser;
libpt is a personal general purpose library, offering this executable, a python module and a
dynamic library.
"##;
mod dumper;
use dumper::{DataSource, Hedu};
//// STATICS ///////////////////////////////////////////////////////////////////////////////////////
//// MACROS ////////////////////////////////////////////////////////////////////////////////////////
//// ENUMS /////////////////////////////////////////////////////////////////////////////////////////
//// STRUCTS ///////////////////////////////////////////////////////////////////////////////////////
/// defines CLI interface
#[derive(Debug, Clone, Parser)]
#[command(
author,
version,
about = ABOUT_ROOT,
long_about = format!("{}{}", ABOUT_ROOT ,LONG_ABOUT_ROOT),
help_template =
r#"{about-section}
about,
long_about,
help_template = r#"{about-section}
{usage-heading} {usage}
{all-args}{tab}
libpt: {version}
{name}: {version}
Author: {author-with-newline}
"#
)]
)]
/// Hexdumper written in Rust
pub struct Cli {
// clap_verbosity_flag seems to make this a global option implicitly
/// set a verbosity, multiple allowed (f.e. -vvv)
#[command(flatten)]
pub verbose: Verbosity<InfoLevel>,
/// show more details
#[arg(short, long)]
pub verbose: bool,
/// show additional logging meta data
#[arg(long)]
@ -86,23 +55,22 @@ pub struct Cli {
/// a data source, probably a file.
///
/// If left empty or set as "-", the program will read from stdin.
// TODO: take many sources #60
///
/// Directories cannot be dumped.
pub data_source: Vec<String>,
}
//// IMPLEMENTATION ////////////////////////////////////////////////////////////////////////////////
//// PUBLIC FUNCTIONS //////////////////////////////////////////////////////////////////////////////
//// PRIVATE FUNCTIONS /////////////////////////////////////////////////////////////////////////////
fn main() {
let mut cli = cli_parse();
let mut sources: Vec<Box<dyn DataSource>> = Vec::new();
if cli.data_source.len() > 0 && cli.data_source[0] != "-" {
if !cli.data_source.is_empty() && !cli.data_source.contains(&"-".to_string()) {
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);
warn!(
"'{:?}' is a directory and cannot be dumped, skipping",
data_source
);
// std::process::exit(1);
continue;
}
@ -124,7 +92,7 @@ fn main() {
}
// just for the little header
cli.data_source = Vec::new();
cli.data_source.push(format!("stdin"));
cli.data_source.push("stdin".to_string());
sources.push(Box::new(stdin));
}
for (i, source) in sources.iter_mut().enumerate() {
@ -140,7 +108,7 @@ fn main() {
}
}
match config.dump(&mut **source) {
Ok(_) => (),
Ok(()) => (),
Err(err) => {
error!("Could not dump data of file: {err}");
std::process::exit(3);
@ -151,25 +119,19 @@ fn main() {
}
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
fn cli_parse() -> Cli {
let cli = Cli::parse();
let ll: Level = match cli.verbose.log_level().unwrap().as_str() {
"TRACE" => Level::TRACE,
"DEBUG" => Level::DEBUG,
"INFO" => Level::INFO,
"WARN" => Level::WARN,
"ERROR" => Level::ERROR,
_ => {
unreachable!();
}
let ll: Level = if cli.verbose {
Level::INFO
} else {
Level::DEBUG
};
if cli.meta {
Logger::init(None, Some(ll), false).expect("could not initialize Logger");
let _ = Logger::builder().max_level(ll).build();
} else {
// less verbose version
Logger::init_mini(Some(ll)).expect("could not initialize Logger");
let _ = Logger::builder().show_time(false).build();
}
return cli;
cli
}