80 Commits
Author SHA1 Message Date
sharkdp c75a882081 Update dependencies 2019-10-08 20:32:56 +02:00
sharkdp 81787d0858 Bump version 2019-10-08 20:31:56 +02:00
sharkdp 8e77507686 Enable unit tests on Travis CI 2019-10-08 20:29:52 +02:00
sharkdp 9c46ced572 Enable LTO 2019-10-08 20:09:10 +02:00
sharkdp 161ddd7ea1 Only print to buffer if line is not squeezed 2019-10-08 20:09:10 +02:00
David Peter 53110bac6f Add sponsor button as an experiment :-) 2019-10-08 18:35:09 +02:00
Tom Milligan d64053771c hexyl: add display offset option 2019-10-07 23:36:53 +02:00
Noah AL-Shihabi 23f199ffbd Remove additional space found on short input 2019-10-01 20:23:57 +02:00
Tom Milligan 175847787d fix crate import for travis tests 2019-07-17 19:31:37 +02:00
Tom Milligan 622a084ea6 run: factor out mainloop and add tests 2019-07-17 19:31:37 +02:00
Tom Milligan 4c48eedb18 printer: accept any Write as output 2019-07-17 19:31:37 +02:00
Tom Milligan ca8970b5cc lib: main and run in bin, everything else in lib 2019-07-17 19:31:37 +02:00
Tom Milligan fb49b633b2 lib: created, moved bin 2019-07-17 19:31:37 +02:00
sharkdp 82c34fe39b Bump version 2019-05-30 15:35:21 +02:00
Armin Widegreen c4028bb213 Consider color when printing squeeze indicator
When color option has been set to never, the squeeze indicator
character asterisk '*' should not be printed in color.

Fixes #63
2019-05-30 15:29:37 +02:00
Armin Widegreen def066ff92 Fixes squeeze state-machine for first/last char
Consider equality for first and last character of a line.

If the first or last character of a line is not equal to previous character,
the state machine should be set back to probe as the line should not be
squeezed.

The unit tests has been expanded for testing the use case described in #62.
Further the body of all unit tests has been simplified.

Fixes #62
2019-05-30 15:29:37 +02:00
sharkdp 17c2c61bad Update dependencies, closes #61 2019-05-28 21:37:33 +02:00
sharkdp 2ad15413b9 Bump version 2019-04-13 18:39:34 +02:00
sharkdp 710c86f6a1 Update dependencies 2019-04-13 18:38:50 +02:00
sharkdp a2ea80e960 Use u8/usize directly without references 2019-04-13 18:32:59 +02:00
sharkdp 3390bedee9 Update to Rust 2018 2019-04-13 18:27:21 +02:00
Armin Widegreen 6978f7a277 Adds squeeze support
The squeeze feature is implement as for hexdump. Reoccurring lines will
be squeezed together and visualized with an asterisk. Further hexyl
received an additional command line argument '-v|--no-squeezing' to
disable squeezing. Squeezing is enabled by default.

If the squeezing goes until the end of the input data, an empty line
with address next to last line will be printed to indicate the highest
address (again similar to hexdump).
2019-04-13 18:19:15 +02:00
sharkdp 2c7c857ccc Fix left border 2019-04-08 22:52:06 +02:00
sharkdp 55adb2db2c Make sure to always print the header 2019-04-08 21:34:32 +02:00
sharkdp 3a62809c8c Use separate struct for border elements 2019-04-08 21:23:09 +02:00
sharkdp 7cc0b51537 cargo fmt 2019-04-08 21:20:45 +02:00
sharkdp 8363e3f37e Fix typo 2019-04-08 21:16:06 +02:00
Dominik Menke 07c222947d Implement various --border styles 2019-04-08 20:26:30 +02:00
David Peter 2566d7b9f9 Updated Arch linux instructions
closes #56
2019-03-16 20:42:38 +01:00
Mikaela Szekely 8bd7677c9b Bump version number to Rust 1.30.0 2019-03-09 07:41:52 +01:00
Mikaela Szekely 6a848b14f8 Allow passing length in hex with 0x notation 2019-03-09 07:41:52 +01:00
selfup c6c4dd416e unhide --bytes/-c - describe alias to --length/-n 2019-02-06 07:35:12 +01:00
Matthew Ziter f918bfb911 Set printed to true after printing header 2019-02-06 07:31:33 +01:00
Matthew Ziter 10e3535999 Fixes #47 with header printing before data in some scenarios 2019-02-06 07:31:33 +01:00
selfup 8725f8cd25 Refactor Some(length) and change --c/-c to --bytes/-c 2019-02-05 23:15:43 +01:00
selfup c8e65d7528 Add c long/c short and hide values 2019-02-05 23:15:43 +01:00
selfup 170573fad0 Add --c alias to length cmd 2019-02-05 23:15:43 +01:00
Lily Ballard 880807619f Add Nix install instructions to README.md 2019-01-14 22:38:48 +01:00
sharkdp 7e46a5d788 Bump version 2019-01-14 10:51:32 +01:00
sharkdp 19030470f9 Add proper Ctrl-C handling 2019-01-14 10:40:04 +01:00
sharkdp b27ab61608 Fix behavior on broken pipe 2019-01-14 10:17:14 +01:00
sharkdp 39a83021dc Use 16 colors instead of 256 2019-01-14 09:53:33 +01:00
sharkdp f6f41e76aa Use --color=always/auto/never instead of --no-color 2019-01-14 09:14:47 +01:00
Bennett Hardwick 74861353fa Add --no-color flag to disable ANSI sequences 2019-01-14 09:14:47 +01:00
Arnav Borborah 78d8cc516f Change exit code for help and version messages to 0 2019-01-13 18:35:15 +01:00
arnavb11 faebee7b8c Move duplicate code out of conditional 2019-01-13 18:03:07 +01:00
arnavb11 92c974d066 Handle clap errors and other errors separately 2019-01-13 18:03:07 +01:00
arnavb11 866feb2966 Refactor error handling into a if-let 2019-01-13 18:03:07 +01:00
arnavb11 952fff0ee4 Use instead of for consistency 2019-01-13 18:03:07 +01:00
Lily Ballard 1619cd5afe Bump the buffer size to 256 bytes
On my machine this produces an 11% speedup (benchmarked reading a 1.3MB
file from disk). Higher buffer sizes don't seem to make a difference.
2019-01-13 17:35:51 +01:00
Lily Ballard 20694bb628 Write lines into an in-memory buffer before printing to stdout
Running them through an in-memory buffer first produces a 25% speedup
(when tested against a 1.3MB file).
2019-01-13 17:35:51 +01:00
sharkdp 98311282e5 Fix #25 2019-01-10 09:03:39 +01:00
Lily Ballard afb6669477 Replace all uses of format!()
Previously we were allocating 2 strings per byte, plus another for the
offset. Instead we can just write all this directly to stdout.

Also replace the calculation of the ANSI escape code for every single
byte. This produces a significant speedup.
2019-01-10 09:00:44 +01:00
sharkdp 0dad8bf481 Bump version 2019-01-10 08:36:45 +01:00
sharkdp 612a227a81 Update dependencies 2019-01-10 08:34:28 +01:00
sharkdp 351fcf0aea Add note about Windows
closes #22
2019-01-10 08:32:46 +01:00
sharkdp 3c3d91cd51 Use 8-bit colors instead of 24-bit truecolor 2019-01-10 08:32:27 +01:00
Lily Ballard 7b7a0ff32d Get rid of the use of str.repeat()
This allocates a string. Instead we can (ab)use format specifiers to do
the repetitions for us.
2019-01-10 07:56:42 +01:00
Sebastian Thiel 2e403a2d11 Make clear we didn't accidentally forget to handle Results 2019-01-09 23:41:29 +01:00
Tobias Kortkamp 155b14e865 Add FreeBSD install instructions to README.md
I've just packaged hexyl for FreeBSD. It should be available in a couple of days from the `latest` package repository.

https://svnweb.freebsd.org/changeset/ports/489825
2019-01-09 23:07:50 +01:00
sharkdp 8429b87594 Re-enable Windows build 2019-01-09 20:20:37 +01:00
Thomas Schneider 1cddf395a8 Update README.md
changed yaourt to 
could be pakku, pikaur, aurman or something 
But yaourt is out of date, insecure and not recommended anymore
2019-01-09 19:55:14 +01:00
Yasuaki Uechi 2a1ae1d295 Add macOS installation guide 2019-01-07 10:09:37 +01:00
sharkdp 57e1c67bfb Disable Windows build 2018-12-30 13:27:21 +01:00
sharkdp a109ce7aba Bump version 2018-12-30 12:56:20 +01:00
David Peter b5704f660c Change variable name 2018-12-02 20:41:11 +01:00
PewZ 648e96dbaf add COLOR_OFFSET 2018-12-02 20:41:11 +01:00
PewZ 91a119f453 display input offset 2018-12-02 20:41:11 +01:00
sharkdp 22b544393b Enable ANSI support on Windows, closes #2 2018-11-21 19:36:20 +01:00
sharkdp 269246097c Enable builds for Windows 2018-11-21 19:36:20 +01:00
Erjan Kalybek eb7a5b677f Fix typo 2018-11-12 09:00:59 +01:00
sharkdp 374b04dd43 Update logo 2018-11-08 20:03:11 +01:00
David Peter 3ac0f9752b Update README.md 2018-11-08 19:58:51 +01:00
Attenuation 499bd4aa54 fixed 2018-11-08 19:58:51 +01:00
Attenuation 1b460c0cef package hexyl to archlinux 2018-11-08 19:58:51 +01:00
David Peter e54d7286b8 Update README.md 2018-11-06 22:26:26 +01:00
David Peter 0e0e1626ab Update README.md 2018-11-06 22:20:52 +01:00
David Peter 300dc73605 Update README.md 2018-11-06 22:19:48 +01:00
sharkdp cd989ba5c4 Add install instructions 2018-11-06 21:43:50 +01:00
David Peter 4046ea4163 Added description. 2018-11-06 21:34:46 +01:00
10 changed files with 1290 additions and 327 deletions

No files matched your search

+1
View File
@@ -0,0 +1 @@
github: sharkdp
+7 -3
View File
@@ -12,9 +12,13 @@ matrix:
- os: osx
rust: stable
env: TARGET=x86_64-apple-darwin
- os: windows
rust: stable
env: TARGET=x86_64-pc-windows-msvc
# Minimum Rust supported channel.
- os: linux
rust: 1.29.0
rust: 1.31.0
env: TARGET=x86_64-unknown-linux-gnu
@@ -39,7 +43,7 @@ install:
script:
# Incorporate TARGET env var to the build and test process
- cargo build --target $TARGET --verbose
#- cargo test --target $TARGET --verbose
- cargo test --target $TARGET --verbose
before_deploy:
- bash ci/before_deploy.bash
@@ -68,7 +72,7 @@ deploy:
# deploy only if we push a tag
tags: true
# deploy only on stable channel that has TARGET env variable sets
condition: $TRAVIS_RUST_VERSION = stable && $TARGET != ""
condition: $TRAVIS_RUST_VERSION = stable && $TARGET != "x86_64-pc-windows-msvc"
notifications:
email:
Generated
+79 -52
View File
@@ -1,47 +1,77 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
[[package]]
name = "ansi_term"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "ansi_term"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "atty"
version = "0.2.11"
version = "0.2.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
"termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "bitflags"
version = "1.0.4"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "cc"
version = "1.0.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "cfg-if"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "clap"
version = "2.32.0"
version = "2.33.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
"atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
"bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
"strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)",
"bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
"term_size 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"textwrap 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
"textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-width 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
"vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "hexyl"
version = "0.2.0"
name = "ctrlc"
version = "3.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
"clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)",
"nix 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "hexyl"
version = "0.6.0"
dependencies = [
"ansi_term 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)",
"atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)",
"clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
"ctrlc 3.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -55,25 +85,24 @@ dependencies = [
[[package]]
name = "libc"
version = "0.2.43"
version = "0.2.62"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "redox_syscall"
version = "0.1.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "redox_termios"
version = "0.1.1"
name = "nix"
version = "0.14.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
"bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)",
"cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
"void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "strsim"
version = "0.7.0"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
@@ -82,32 +111,22 @@ version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "termion"
version = "1.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)",
"redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
"redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "textwrap"
version = "0.10.0"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"term_size 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-width 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "unicode-width"
version = "0.1.5"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
@@ -115,6 +134,11 @@ name = "vec_map"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "void"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "winapi"
version = "0.2.8"
@@ -122,7 +146,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "winapi"
version = "0.3.6"
version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -146,21 +170,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[metadata]
"checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
"checksum atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9a7d5b8723950951411ee34d271d99dddcc2035a16ab25310ea2c8cfd4369652"
"checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12"
"checksum clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b957d88f4b6a63b9d70d5f454ac8011819c6efa7727858f458ab71c756ce2d3e"
"checksum ansi_term 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
"checksum atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)" = "1803c647a3ec87095e7ae7acfca019e98de5ec9a7d01343f611cf3152ed71a90"
"checksum bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8a606a02debe2813760609f57a64a2ffd27d9fdf5b2f133eaca0b248dd92cdd2"
"checksum cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)" = "4fc9a35e1f4290eb9e5fc54ba6cf40671ed2a2514c3eeb2b2a908dda2ea5a1be"
"checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
"checksum clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9"
"checksum ctrlc 3.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c7dfd2d8b4c82121dfdff120f818e09fc4380b0b7e17a742081a89b94853e87f"
"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
"checksum libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)" = "76e3a3ef172f1a0b9a9ff0dd1491ae5e6c948b94479a3021819ba7d860c8645d"
"checksum redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "c214e91d3ecf43e9a4e41e578973adeb14b474f2bee858742d127af75a0112b1"
"checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76"
"checksum strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4f380125926a99e52bc279241539c018323fab05ad6368b56f93d9369ff550"
"checksum libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)" = "34fcd2c08d2f832f376f4173a231990fa5aef4e99fb569867318a227ef4c06ba"
"checksum nix 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6c722bee1037d430d0f8e687bbdbf222f27cc6e4e68d5caf630857bb2b6dbdce"
"checksum strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
"checksum term_size 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9e5b9a66db815dcfd2da92db471106457082577c3c278d4138ab3e3b4e189327"
"checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096"
"checksum textwrap 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "307686869c93e71f94da64286f9a9524c0f308a9e1c87a583de8e9c9039ad3f6"
"checksum unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "882386231c45df4700b275c7ff55b6f3698780a650026380e72dabe76fa46526"
"checksum textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
"checksum unicode-width 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "7007dbd421b92cc6e28410fe7362e2e0a2503394908f417b68ec8d1c364c4e20"
"checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a"
"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
"checksum winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "92c1eb33641e276cfa214a0522acad57be5c56b10cb348b3c5117db75f3ac4b0"
"checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6"
"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"
"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+9 -2
View File
@@ -7,11 +7,18 @@ license = "MIT/Apache-2.0"
name = "hexyl"
readme = "README.md"
repository = "https://github.com/sharkdp/hexyl"
version = "0.2.0"
version = "0.6.0"
edition = "2018"
[dependencies]
ansi_term = "0.11"
ansi_term = "0.12"
atty = "0.2"
ctrlc = "3.1"
[dependencies.clap]
version = "2"
features = ["suggestions", "color", "wrap_help"]
[profile.release]
lto = true
codegen-units = 1
+65 -2
View File
@@ -1,8 +1,11 @@
![](doc/logo.svg)
*A command-line hex viewer.*
[![Build Status](https://travis-ci.org/sharkdp/hexyl.svg?branch=master)](https://travis-ci.org/sharkdp/hexyl)
[![](https://img.shields.io/crates/l/hexyl.svg?colorB=22ba4c)](https://crates.io/crates/hexyl)
![](https://img.shields.io/crates/v/hexyl.svg?colorB=00aa88)
`hexyl` is a simple hex viewer for the terminal. It uses a colored output to distinguish different categories
of bytes (NULL bytes, printable ASCII characters, ASCII whitespace characters, other ASCII characters and non-ASCII).
## Preview
@@ -13,3 +16,63 @@
![](https://i.imgur.com/ln3TniI.png)
![](https://i.imgur.com/f8nm8g6.png)
## Installation
### On Debian-based systems
``` bash
wget "https://github.com/sharkdp/hexyl/releases/download/v0.6.0/hexyl_0.6.0_amd64.deb"
sudo dpkg -i hexyl_0.6.0_amd64.deb
```
### On Arch Linux
You can install `hexyl` from [the official package repository](https://www.archlinux.org/packages/community/x86_64/hexyl/):
```
pacman -S hexyl
```
### On macOS
```
brew install hexyl
```
### On FreeBSD
```
pkg install hexyl
```
### Via Nix
```
nix-env -i hexyl
```
### On other distributions
Check out the [release page](https://github.com/sharkdp/hexyl/releases) for binary builds.
### On Windows
For now, you will have to install from source via `cargo` (see below). Make sure that
you use a terminal that supports ANSI escape sequences (like PowerShell on Windows 10).
### Via cargo
If you have Rust 1.31 or higher, you can install `hexyl` from source via `cargo`:
```
cargo install hexyl
```
## License
Licensed under either of
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
* MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
at your option.
+27 -15
View File
@@ -52,10 +52,10 @@
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="1"
inkscape:cx="334.3399"
inkscape:cy="-64.873189"
inkscape:cx="237.55539"
inkscape:cy="-142.8615"
inkscape:document-units="px"
inkscape:current-layer="g4837"
inkscape:current-layer="g1397"
showgrid="false"
inkscape:window-width="1920"
inkscape:window-height="1175"
@@ -162,19 +162,31 @@
style="opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
inkscape:connector-curvature="0" />
</g>
<text
xml:space="preserve"
<g
aria-label="hexyl"
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:53.38896561px;line-height:111.22701263px;font-family:'Bitstream Vera Sans';-inkscape-font-specification:'Bitstream Vera Sans';letter-spacing:0px;word-spacing:0px;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4.44908094px;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
x="101.36624"
y="16.43"
id="text995"><tspan
sodipodi:role="line"
id="tspan993"
x="101.36624"
y="16.43"
style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-family:Jura;-inkscape-font-specification:'Jura Light';stroke-width:4.44908094px"><tspan
style="fill:#00aa88;stroke-width:4.44908094px"
id="tspan4735">hex</tspan>yl</tspan></text>
id="text995">
<path
d="m 119.35832,-8.2357018 q 4.91179,0 7.10074,2.2423366 2.24233,2.1889475 2.24233,7.1007324 V 15.308832 q 0,1.121168 -1.12117,1.121168 -1.12116,0 -1.12116,-1.121168 V 1.1073672 q 0,-3.9507835 -1.60167,-5.4990635 -1.54828,-1.6016689 -5.49907,-1.6016689 h -1.49489 q -8.7024,0 -9.39646,5.8193972 l -0.0534,0.3203338 V 15.308832 q 0,1.121168 -1.12116,1.121168 -1.12117,0 -1.12117,-1.121168 v -34.59605 q 0,-1.121168 1.12117,-1.121168 1.12116,0 1.12116,1.121168 v 15.2692445 q 2.4559,-4.2177283 9.44985,-4.2177283 z"
style="fill:#00aa88;stroke-width:4.44908094px"
id="path854" />
<path
d="m 139.57939,4.9513727 v 2.1889476 q 0,3.8973947 1.60167,5.4990637 1.60167,1.54828 5.49907,1.54828 h 4.37789 q 3.63045,0 5.23212,-0.961002 1.60167,-1.01439 1.81522,-3.4168934 0.10678,-1.1211683 1.17456,-1.1211683 0.53389,0 0.80084,0.3203338 0.32033,0.3203338 0.26694,0.8542234 -0.26694,3.4168935 -2.56267,5.0185625 -2.24234,1.54828 -6.72701,1.54828 h -4.37789 q -4.8584,0 -7.10074,-2.188947 -2.24233,-2.242337 -2.24233,-7.1007327 V 1.1607561 q 0,-4.9117848 2.24233,-7.1541213 2.24234,-2.2423366 7.10074,-2.1889476 h 4.37789 q 4.91179,0 7.10073,2.2423365 2.24234,2.1889476 2.24234,7.1007324 v 2.6694483 q 0,1.1211683 -1.12117,1.1211683 z m 0,-2.2423366 h 18.57936 v -1.54828 q 0,-3.9507834 -1.60167,-5.4990634 -1.54828,-1.601669 -5.49906,-1.601669 h -4.37789 q -3.8974,-0.053389 -5.49907,1.54828 -1.60167,1.601669 -1.60167,5.5524524 z"
style="fill:#00aa88;stroke-width:4.44908094px"
id="path856" />
<path
d="m 185.94771,-7.861979 q 0.4805,0.3737227 0.4805,0.8542234 0,0.3737228 -0.32033,0.6940566 l -8.7024,10.5176262 8.06173,10.3574598 q 0.32033,0.373722 0.32033,0.747445 0,0.480501 -0.4805,0.800835 Q 184.93332,16.43 184.5596,16.43 q -0.42711,0 -0.85423,-0.4805 L 175.80381,5.8589851 167.52852,15.9495 q -0.37373,0.4805 -0.85423,0.4805 -0.37372,0 -0.69405,-0.320333 -0.4805,-0.427112 -0.4805,-0.854224 0,-0.373723 0.32033,-0.747445 L 174.41569,4.0437603 166.30057,-6.313699 q -0.32033,-0.4271118 -0.32033,-0.7474456 0,-0.4805007 0.4805,-0.8542234 0.42711,-0.2669448 0.74744,-0.2669448 0.4805,0 0.85423,0.4805007 l 7.95495,10.1439034 8.32868,-10.1439034 q 0.42711,-0.4805007 0.85422,-0.4805007 0.42712,0 0.74745,0.3203338 z"
style="fill:#00aa88;stroke-width:4.44908094px"
id="path858" />
<path
d="m 199.74459,27.107793 q -3.41689,0 -5.01856,-1.601669 -1.54828,-1.601669 -1.54828,-5.018562 0,-1.121169 1.12117,-1.121169 1.12116,0 1.12116,1.121169 0,2.509281 0.90762,3.416893 0.961,0.961002 3.41689,0.961002 h 8.86257 q 2.45589,0 3.3635,-0.961002 0.961,-0.907612 0.961,-3.416893 v -7.527844 q -1.4415,1.815224 -3.79061,2.669448 -2.34912,0.854223 -5.65923,0.854223 h -1.12117 q -4.80501,0 -6.99396,-2.188947 -2.18894,-2.188948 -2.18894,-6.9405658 V -7.1145335 q 0,-1.1211683 1.12117,-1.1211683 1.12116,0 1.12116,1.1211683 V 7.3538762 q 0,3.8440058 1.49489,5.3922858 1.54828,1.494891 5.44568,1.494891 h 1.12117 q 9.44984,0 9.44984,-6.8871768 V -7.1145335 q 0,-1.1211683 1.12117,-1.1211683 1.12117,0 1.12117,1.1211683 V 20.487562 q 0,3.416893 -1.60167,5.018562 -1.54828,1.601669 -4.96517,1.601669 z"
style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-family:Jura;-inkscape-font-specification:'Jura Light';stroke-width:4.44908094px"
id="path860" />
<path
d="m 231.93729,16.43 q -3.68383,0 -5.39228,-1.655058 -1.70845,-1.708447 -1.70845,-5.3388962 V -19.287218 q 0,-1.121168 1.12117,-1.121168 1.12117,0 1.12117,1.121168 V 9.4360458 q 0,2.7228372 1.01439,3.7372272 1.06778,1.014391 3.79062,1.014391 1.17455,0 1.17455,1.121168 0,1.121168 -1.12117,1.121168 z"
style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-family:Jura;-inkscape-font-specification:'Jura Light';stroke-width:4.44908094px"
id="path862" />
</g>
</g>
</g>
</g>

Before

Width:  |  Height:  |  Size: 8.0 KiB

After

Width:  |  Height:  |  Size: 11 KiB

+166
View File
@@ -0,0 +1,166 @@
#[macro_use]
extern crate clap;
use atty;
use ctrlc;
use std::fs::File;
use std::io::{self, prelude::*};
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use clap::{App, AppSettings, Arg};
use atty::Stream;
use hexyl::{BorderStyle, Printer};
fn run() -> Result<(), Box<dyn std::error::Error>> {
let app = App::new(crate_name!())
.setting(AppSettings::ColorAuto)
.setting(AppSettings::ColoredHelp)
.setting(AppSettings::DeriveDisplayOrder)
.setting(AppSettings::UnifiedHelpMessage)
.version(crate_version!())
.about(crate_description!())
.arg(Arg::with_name("file").help("File to display"))
.arg(
Arg::with_name("length")
.short("n")
.long("length")
.takes_value(true)
.value_name("N")
.help("Read only N bytes from the input"),
)
.arg(
Arg::with_name("bytes")
.short("c")
.long("bytes")
.takes_value(true)
.value_name("N")
.help("An alias for -n/--length"),
)
.arg(
Arg::with_name("nosqueezing")
.short("v")
.long("no-squeezing")
.help(
"Displays all input data. Otherwise any number of groups of output \
lines which would be identical to the preceding group of lines, are \
replaced with a line comprised of a single asterisk.",
),
)
.arg(
Arg::with_name("color")
.long("color")
.takes_value(true)
.value_name("when")
.possible_values(&["always", "auto", "never"])
.default_value("always")
.help(
"When to use colors. The auto-mode only displays colors if the output \
goes to an interactive terminal",
),
)
.arg(
Arg::with_name("border")
.long("border")
.takes_value(true)
.possible_values(&["unicode", "ascii", "none"])
.default_value("unicode")
.help("Whether to draw a border with unicode or ASCII characters, or none at all"),
)
.arg(
Arg::with_name("display_offset")
.short("o")
.long("display-offset")
.takes_value(true)
.value_name("OFFSET")
.help("Add OFFSET to the displayed file position."),
);
let matches = app.get_matches_safe()?;
let stdin = io::stdin();
let mut reader: Box<dyn Read> = match matches.value_of("file") {
Some(filename) => Box::new(File::open(filename)?),
None => Box::new(stdin.lock()),
};
let length_arg = matches.value_of("length").or(matches.value_of("bytes"));
if let Some(length) = length_arg.and_then(parse_hex_or_int) {
reader = Box::new(reader.take(length));
}
let show_color = match matches.value_of("color") {
Some("never") => false,
Some("auto") => atty::is(Stream::Stdout),
_ => true,
};
let border_style = match matches.value_of("border") {
Some("unicode") => BorderStyle::Unicode,
Some("ascii") => BorderStyle::Ascii,
_ => BorderStyle::None,
};
let squeeze = !matches.is_present("nosqueezing");
let display_offset = matches
.value_of("display_offset")
.and_then(parse_hex_or_int)
.unwrap_or(0);
// Set up Ctrl-C handler
let cancelled = Arc::new(AtomicBool::new(false));
let c = cancelled.clone();
ctrlc::set_handler(move || {
c.store(true, Ordering::SeqCst);
})
.expect("Error setting Ctrl-C handler");
let stdout = io::stdout();
let mut stdout_lock = stdout.lock();
let mut printer = Printer::new(&mut stdout_lock, show_color, border_style, squeeze);
printer.display_offset(display_offset as usize);
printer.print_all(&mut reader, Some(cancelled))?;
Ok(())
}
fn main() {
// Enable ANSI support for Windows
#[cfg(windows)]
let _ = ansi_term::enable_ansi_support();
let result = run();
if let Err(err) = result {
if let Some(clap_err) = err.downcast_ref::<clap::Error>() {
eprint!("{}", clap_err); // Clap errors already have newlines
match clap_err.kind {
// The exit code should not indicate an error for --help / --version
clap::ErrorKind::HelpDisplayed | clap::ErrorKind::VersionDisplayed => {
std::process::exit(0)
}
_ => (),
}
} else {
eprintln!("Error: {}", err);
}
std::process::exit(1);
}
}
fn parse_hex_or_int(n: &str) -> Option<u64> {
if n.starts_with("0x") {
u64::from_str_radix(n.trim_start_matches("0x"), 16).ok()
} else {
n.parse::<u64>().ok()
}
}
+513
View File
@@ -0,0 +1,513 @@
pub mod squeezer;
use std::io::{self, Read, Write};
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use ansi_term::Color;
use ansi_term::Color::Fixed;
use crate::squeezer::{SqueezeAction, Squeezer};
type Cancelled = Arc<AtomicBool>;
const BUFFER_SIZE: usize = 256;
const COLOR_NULL: Color = Fixed(242); // grey
const COLOR_OFFSET: Color = Fixed(242); // grey
const COLOR_ASCII_PRINTABLE: Color = Color::Cyan;
const COLOR_ASCII_WHITESPACE: Color = Color::Green;
const COLOR_ASCII_OTHER: Color = Color::Purple;
const COLOR_NONASCII: Color = Color::Yellow;
pub enum ByteCategory {
Null,
AsciiPrintable,
AsciiWhitespace,
AsciiOther,
NonAscii,
}
#[derive(Copy, Clone)]
struct Byte(u8);
impl Byte {
fn category(self) -> ByteCategory {
if self.0 == 0x00 {
ByteCategory::Null
} else if self.0.is_ascii_alphanumeric()
|| self.0.is_ascii_punctuation()
|| self.0.is_ascii_graphic()
{
ByteCategory::AsciiPrintable
} else if self.0.is_ascii_whitespace() {
ByteCategory::AsciiWhitespace
} else if self.0.is_ascii() {
ByteCategory::AsciiOther
} else {
ByteCategory::NonAscii
}
}
fn color(self) -> &'static Color {
use crate::ByteCategory::*;
match self.category() {
Null => &COLOR_NULL,
AsciiPrintable => &COLOR_ASCII_PRINTABLE,
AsciiWhitespace => &COLOR_ASCII_WHITESPACE,
AsciiOther => &COLOR_ASCII_OTHER,
NonAscii => &COLOR_NONASCII,
}
}
fn as_char(self) -> char {
use crate::ByteCategory::*;
match self.category() {
Null => '0',
AsciiPrintable => self.0 as char,
AsciiWhitespace if self.0 == 0x20 => ' ',
AsciiWhitespace => '_',
AsciiOther => '•',
NonAscii => '×',
}
}
}
struct BorderElements {
left_corner: char,
horizontal_line: char,
column_separator: char,
right_corner: char,
}
pub enum BorderStyle {
Unicode,
Ascii,
None,
}
impl BorderStyle {
fn header_elems(&self) -> Option<BorderElements> {
match self {
BorderStyle::Unicode => Some(BorderElements {
left_corner: '┌',
horizontal_line: '─',
column_separator: '┬',
right_corner: '┐',
}),
BorderStyle::Ascii => Some(BorderElements {
left_corner: '+',
horizontal_line: '-',
column_separator: '+',
right_corner: '+',
}),
BorderStyle::None => None,
}
}
fn footer_elems(&self) -> Option<BorderElements> {
match self {
BorderStyle::Unicode => Some(BorderElements {
left_corner: '└',
horizontal_line: '─',
column_separator: '┴',
right_corner: '┘',
}),
BorderStyle::Ascii => Some(BorderElements {
left_corner: '+',
horizontal_line: '-',
column_separator: '+',
right_corner: '+',
}),
BorderStyle::None => None,
}
}
fn outer_sep(&self) -> char {
match self {
BorderStyle::Unicode => '│',
BorderStyle::Ascii => '|',
BorderStyle::None => ' ',
}
}
fn inner_sep(&self) -> char {
match self {
BorderStyle::Unicode => '┊',
BorderStyle::Ascii => '|',
BorderStyle::None => ' ',
}
}
}
pub struct Printer<'a, Writer: Write> {
idx: usize,
/// The raw bytes used as input for the current line.
raw_line: Vec<u8>,
/// The buffered line built with each byte, ready to print to writer.
buffer_line: Vec<u8>,
writer: &'a mut Writer,
show_color: bool,
border_style: BorderStyle,
header_was_printed: bool,
byte_hex_table: Vec<String>,
byte_char_table: Vec<String>,
squeezer: Squeezer,
display_offset: usize,
}
impl<'a, Writer: Write> Printer<'a, Writer> {
pub fn new(
writer: &'a mut Writer,
show_color: bool,
border_style: BorderStyle,
use_squeeze: bool,
) -> Printer<'a, Writer> {
Printer {
idx: 1,
raw_line: vec![],
buffer_line: vec![],
writer,
show_color,
border_style,
header_was_printed: false,
byte_hex_table: (0u8..=u8::max_value())
.map(|i| {
let byte_hex = format!("{:02x} ", i);
if show_color {
Byte(i).color().paint(byte_hex).to_string()
} else {
byte_hex
}
})
.collect(),
byte_char_table: (0u8..=u8::max_value())
.map(|i| {
let byte_char = format!("{}", Byte(i).as_char());
if show_color {
Byte(i).color().paint(byte_char).to_string()
} else {
byte_char
}
})
.collect(),
squeezer: Squeezer::new(use_squeeze),
display_offset: 0,
}
}
pub fn display_offset(&mut self, display_offset: usize) -> &mut Self {
self.display_offset = display_offset;
self
}
pub fn header(&mut self) {
if let Some(border_elements) = self.border_style.header_elems() {
let h = border_elements.horizontal_line;
let h8 = h.to_string().repeat(8);
let h25 = h.to_string().repeat(25);
writeln!(
self.writer,
"{l}{h8}{c}{h25}{c}{h25}{c}{h8}{c}{h8}{r}",
l = border_elements.left_corner,
c = border_elements.column_separator,
r = border_elements.right_corner,
h8 = h8,
h25 = h25
)
.ok();
}
}
pub fn footer(&mut self) {
if let Some(border_elements) = self.border_style.footer_elems() {
let h = border_elements.horizontal_line;
let h8 = h.to_string().repeat(8);
let h25 = h.to_string().repeat(25);
writeln!(
self.writer,
"{l}{h8}{c}{h25}{c}{h25}{c}{h8}{c}{h8}{r}",
l = border_elements.left_corner,
c = border_elements.column_separator,
r = border_elements.right_corner,
h8 = h8,
h25 = h25
)
.ok();
}
}
fn print_position_indicator(&mut self) {
if !self.header_was_printed {
self.header();
self.header_was_printed = true;
}
let style = COLOR_OFFSET.normal();
let byte_index = format!("{:08x}", (self.idx - 1) + self.display_offset);
let formatted_string = if self.show_color {
format!("{}", style.paint(byte_index))
} else {
byte_index
};
let _ = write!(
&mut self.buffer_line,
"{}{}{} ",
self.border_style.outer_sep(),
formatted_string,
self.border_style.outer_sep()
);
}
pub fn print_byte(&mut self, b: u8) -> io::Result<()> {
if self.idx % 16 == 1 {
self.print_position_indicator();
}
write!(&mut self.buffer_line, "{}", self.byte_hex_table[b as usize])?;
self.raw_line.push(b);
self.squeezer.process(b, self.idx);
match self.idx % 16 {
8 => {
let _ = write!(&mut self.buffer_line, "{} ", self.border_style.inner_sep());
}
0 => {
self.print_textline()?;
}
_ => {}
}
self.idx += 1;
Ok(())
}
pub fn print_textline(&mut self) -> io::Result<()> {
let len = self.raw_line.len();
if len == 0 {
if self.squeezer.active() {
self.print_position_indicator();
let _ = writeln!(
&mut self.buffer_line,
"{0:1$}{4}{0:2$}{5}{0:3$}{4}{0:3$}{5}",
"",
24,
25,
8,
self.border_style.inner_sep(),
self.border_style.outer_sep(),
);
self.writer.write_all(&self.buffer_line)?;
}
return Ok(());
}
let squeeze_action = self.squeezer.action();
if squeeze_action != SqueezeAction::Delete {
if len < 8 {
let _ = write!(
&mut self.buffer_line,
"{0:1$}{3}{0:2$}{4}",
"",
3 * (8 - len),
1 + 3 * 8,
self.border_style.inner_sep(),
self.border_style.outer_sep(),
);
} else {
let _ = write!(
&mut self.buffer_line,
"{0:1$}{2}",
"",
3 * (16 - len),
self.border_style.outer_sep()
);
}
let mut idx = 1;
for &b in self.raw_line.iter() {
let _ = write!(
&mut self.buffer_line,
"{}",
self.byte_char_table[b as usize]
);
if idx == 8 {
let _ = write!(&mut self.buffer_line, "{}", self.border_style.inner_sep());
}
idx += 1;
}
if len < 8 {
let _ = writeln!(
&mut self.buffer_line,
"{0:1$}{3}{0:2$}{4}",
"",
8 - len,
8,
self.border_style.inner_sep(),
self.border_style.outer_sep(),
);
} else {
let _ = writeln!(
&mut self.buffer_line,
"{0:1$}{2}",
"",
16 - len,
self.border_style.outer_sep()
);
}
}
match squeeze_action {
SqueezeAction::Print => {
self.buffer_line.clear();
let style = COLOR_OFFSET.normal();
let asterisk = if self.show_color {
format!("{}", style.paint("*"))
} else {
String::from("*")
};
let _ = writeln!(
&mut self.buffer_line,
"{5}{0}{1:2$}{5}{1:3$}{6}{1:3$}{5}{1:4$}{6}{1:4$}{5}",
asterisk,
"",
7,
25,
8,
self.border_style.outer_sep(),
self.border_style.inner_sep(),
);
}
SqueezeAction::Delete => self.buffer_line.clear(),
SqueezeAction::Ignore => (),
}
self.writer.write_all(&self.buffer_line)?;
self.raw_line.clear();
self.buffer_line.clear();
self.squeezer.advance();
Ok(())
}
pub fn header_was_printed(&self) -> bool {
self.header_was_printed
}
/// Loop through the given `Reader`, printing until the `Reader` buffer
/// is exhausted, or the optional `cancelled` bool is set to true.
pub fn print_all<Reader: Read>(
&mut self,
mut reader: Reader,
canceller: Option<Cancelled>,
) -> Result<(), Box<dyn std::error::Error>> {
let mut buffer = [0; BUFFER_SIZE];
'mainloop: loop {
let size = reader.read(&mut buffer)?;
if size == 0 {
break;
}
if let Some(cancelled) = &canceller {
if cancelled.load(Ordering::SeqCst) {
eprintln!("hexyl has been cancelled.");
std::process::exit(130); // Set exit code to 128 + SIGINT
}
}
for b in &buffer[..size] {
let res = self.print_byte(*b);
if res.is_err() {
// Broken pipe
break 'mainloop;
}
}
}
// Finish last line
self.print_textline().ok();
if !self.header_was_printed() {
self.header();
}
self.footer();
Ok(())
}
}
#[cfg(test)]
mod tests {
use std::io;
use std::str;
use super::*;
fn assert_print_all_output<Reader: Read>(input: Reader, expected_string: String) -> () {
let mut output = vec![];
let mut printer = Printer::new(&mut output, false, BorderStyle::Unicode, true);
printer.print_all(input, None).unwrap();
let actual_string: &str = str::from_utf8(&output).unwrap();
assert_eq!(actual_string, expected_string)
}
#[test]
fn empty_file_passes() {
let input = io::empty();
let expected_string =
"┌────────┬─────────────────────────┬─────────────────────────┬────────┬────────┐
└────────┴─────────────────────────┴─────────────────────────┴────────┴────────┘
"
.to_owned();
assert_print_all_output(input, expected_string);
}
#[test]
fn short_input_passes() {
let input = io::Cursor::new(b"spam");
let expected_string =
"┌────────┬─────────────────────────┬─────────────────────────┬────────┬────────┐
│00000000│ 73 70 61 6d ┊ │spam ┊ │
└────────┴─────────────────────────┴─────────────────────────┴────────┴────────┘
"
.to_owned();
assert_print_all_output(input, expected_string);
}
#[test]
fn display_offset() {
let input = io::Cursor::new(b"spamspamspamspamspam");
let expected_string =
"┌────────┬─────────────────────────┬─────────────────────────┬────────┬────────┐
│deadbeef│ 73 70 61 6d 73 70 61 6d ┊ 73 70 61 6d 73 70 61 6d │spamspam┊spamspam│
│deadbeff│ 73 70 61 6d ┊ │spam ┊ │
└────────┴─────────────────────────┴─────────────────────────┴────────┴────────┘
"
.to_owned();
let mut output = vec![];
let mut printer: Printer<Vec<u8>> =
Printer::new(&mut output, false, BorderStyle::Unicode, true);
printer.display_offset(0xdeadbeef);
printer.print_all(input, None).unwrap();
let actual_string: &str = str::from_utf8(&output).unwrap();
assert_eq!(actual_string, expected_string)
}
}
-253
View File
@@ -1,253 +0,0 @@
#[macro_use]
extern crate clap;
extern crate ansi_term;
use std::fs::File;
use std::io::{self, prelude::*, StdoutLock};
use clap::{App, AppSettings, Arg};
use ansi_term::Colour;
use ansi_term::Colour::RGB;
const BUFFER_SIZE: usize = 64;
const COLOR_NULL: Colour = RGB(117, 113, 94); // grey
const COLOR_ASCII_PRINTABLE: Colour = RGB(102, 217, 239); // cyan
const COLOR_ASCII_WHITESPACE: Colour = RGB(166, 226, 46); // green
const COLOR_ASCII_OTHER: Colour = RGB(249, 38, 114); // magenta
const COLOR_NONASCII: Colour = RGB(253, 151, 31); // orange
enum ByteCategory {
Null,
AsciiPrintable,
AsciiWhitespace,
AsciiOther,
NonAscii,
}
#[derive(Copy, Clone)]
struct Byte(u8);
impl Byte {
fn category(self) -> ByteCategory {
if self.0 == 0x00 {
ByteCategory::Null
} else if self.0.is_ascii_alphanumeric()
|| self.0.is_ascii_punctuation()
|| self.0.is_ascii_graphic()
{
ByteCategory::AsciiPrintable
} else if self.0.is_ascii_whitespace() {
ByteCategory::AsciiWhitespace
} else if self.0.is_ascii() {
ByteCategory::AsciiOther
} else {
ByteCategory::NonAscii
}
}
fn color(self) -> &'static Colour {
use ByteCategory::*;
match self.category() {
Null => &COLOR_NULL,
AsciiPrintable => &COLOR_ASCII_PRINTABLE,
AsciiWhitespace => &COLOR_ASCII_WHITESPACE,
AsciiOther => &COLOR_ASCII_OTHER,
NonAscii => &COLOR_NONASCII,
}
}
fn as_char(self) -> char {
use ByteCategory::*;
match self.category() {
Null => '0',
AsciiPrintable => self.0 as char,
AsciiWhitespace if self.0 == 0x20 => ' ',
AsciiWhitespace => '_',
AsciiOther => '•',
NonAscii => '×',
}
}
}
struct Printer<'a> {
idx: usize,
line: Vec<u8>,
stdout: StdoutLock<'a>,
}
impl<'a> Printer<'a> {
fn new(stdout: StdoutLock) -> Printer {
Printer {
idx: 1,
line: vec![],
stdout,
}
}
fn header(&mut self) {
writeln!(
self.stdout,
"┌{}┬{}┬{}┬{}┐",
"─".repeat(3 * 8 + 1),
"─".repeat(3 * 8 + 1),
"─".repeat(8),
"─".repeat(8)
);
}
fn footer(&mut self) {
writeln!(
self.stdout,
"└{}┴{}┴{}┴{}┘",
"─".repeat(3 * 8 + 1),
"─".repeat(3 * 8 + 1),
"─".repeat(8),
"─".repeat(8)
);
}
fn print_byte(&mut self, b: u8) -> io::Result<()> {
if self.idx % 16 == 1 {
write!(self.stdout, "│ ");
}
let byte_str = format!("{:02x} ", b);
write!(self.stdout, "{}", Byte(b).color().paint(byte_str))?;
self.line.push(b);
match self.idx % 16 {
8 => write!(self.stdout, "┊ ")?,
0 => {
self.print_textline()?;
}
_ => {}
}
self.idx += 1;
Ok(())
}
fn print_textline(&mut self) -> io::Result<()> {
let len = self.line.len();
if len == 0 {
return Ok(());
}
if len < 8 {
write!(
self.stdout,
"{}┊{}│",
" ".repeat(3 * (8 - len)),
" ".repeat(1 + 3 * 8)
)?;
} else {
write!(self.stdout, "{}│", " ".repeat(3 * (16 - len)))?;
}
let mut idx = 1;
for b in self.line.iter().map(|b| Byte(*b)) {
let chr = format!("{}", b.as_char());
write!(self.stdout, "{}", b.color().paint(chr)).ok();
if idx == 8 {
write!(self.stdout, "┊");
}
idx += 1;
}
if len < 8 {
writeln!(
self.stdout,
"{}┊{}│ ",
" ".repeat(8 - len),
" ".repeat(8)
)?;
} else {
writeln!(self.stdout, "{}│", " ".repeat(16 - len))?;
}
self.line.clear();
Ok(())
}
}
fn run() -> io::Result<()> {
let app = App::new(crate_name!())
.setting(AppSettings::ColorAuto)
.setting(AppSettings::ColoredHelp)
.setting(AppSettings::DeriveDisplayOrder)
.setting(AppSettings::UnifiedHelpMessage)
.version(crate_version!())
.about(crate_description!())
.arg(Arg::with_name("file").help("File to display"))
.arg(
Arg::with_name("length")
.short("n")
.long("length")
.takes_value(true)
.value_name("N")
.help("read only N bytes from the input"),
);
let matches = app.get_matches();
let stdin = io::stdin();
let mut reader: Box<dyn Read> = match matches.value_of("file") {
Some(filename) => Box::new(File::open(filename)?),
None => Box::new(stdin.lock()),
};
if let Some(length) = matches
.value_of("length")
.and_then(|n| n.parse::<u64>().ok())
{
reader = Box::new(reader.take(length));
}
let stdout = io::stdout();
let mut printer = Printer::new(stdout.lock());
printer.header();
let mut buffer = [0; BUFFER_SIZE];
loop {
let size = reader.read(&mut buffer)?;
if size == 0 {
break;
}
for b in &buffer[..size] {
let res = printer.print_byte(*b);
if res.is_err() {
// Broken pipe
break;
}
}
}
// Finish last line
printer.print_textline().ok();
printer.footer();
Ok(())
}
fn main() {
let result = run();
match result {
Err(err) => {
eprintln!("Error: {}", err);
std::process::exit(1);
}
Ok(()) => {}
}
}
+423
View File
@@ -0,0 +1,423 @@
#[derive(Debug, PartialEq)]
enum SqueezeState {
/// not enabled
Disabled,
/// Will be set from all states if equal condition can't be hold up.
/// Set if previous byte is not equal the current processed byte.
NoSqueeze,
/// Valid for a whole line to identify if it is candidate for squeezing
Probe,
/// Squeeze line parsing is active, but EOL is not reached yet
SqueezeActive,
/// Squeeze line, EOL is reached, will influence the action
Squeeze,
/// same as Squeeze, however this is only for the first line after
/// the squeeze candidate has been set.
SqueezeFirstLine,
/// same as SqueezeActive, however this is only for the first line after
/// the squeeze candidate has been set.
SqueezeActiveFirstLine,
}
pub struct Squeezer {
state: SqueezeState,
byte: u8,
}
#[derive(Debug, PartialEq)]
pub enum SqueezeAction {
Ignore,
Print,
Delete,
}
/// line size
const LSIZE: usize = 16;
impl Squeezer {
pub fn new(enabled: bool) -> Squeezer {
Squeezer {
state: if enabled {
SqueezeState::Probe
} else {
SqueezeState::Disabled
},
byte: 0,
}
}
pub fn process(&mut self, b: u8, i: usize) {
use self::SqueezeState::*;
if self.state == Disabled {
return;
}
let eq = b == self.byte;
if i % LSIZE == 0 {
if !eq {
self.state = Probe;
} else {
self.state = match self.state {
NoSqueeze => Probe,
Probe => SqueezeActiveFirstLine,
SqueezeActiveFirstLine => SqueezeFirstLine,
SqueezeFirstLine => SqueezeActive,
SqueezeActive => Squeeze,
Squeeze => SqueezeActive,
Disabled => Disabled,
};
}
} else if !eq {
if i % LSIZE == 1 {
self.state = Probe;
} else if i % LSIZE != 1 {
self.state = NoSqueeze;
}
}
self.byte = b;
}
pub fn active(&self) -> bool {
use self::SqueezeState::*;
match self.state {
Squeeze | SqueezeActive | SqueezeFirstLine | SqueezeActiveFirstLine => true,
_ => false,
}
}
pub fn action(&self) -> SqueezeAction {
match self.state {
SqueezeState::SqueezeFirstLine => SqueezeAction::Print,
SqueezeState::Squeeze => SqueezeAction::Delete,
_ => SqueezeAction::Ignore,
}
}
pub fn advance(&mut self) {
match self.state {
SqueezeState::SqueezeFirstLine => {
self.state = SqueezeState::SqueezeActive;
}
SqueezeState::Squeeze => {
self.state = SqueezeState::SqueezeActive;
}
_ => {}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn three_same_lines() {
const LINES: usize = 3;
let v = vec![0u8; LINES * LSIZE];
let mut s = Squeezer::new(true);
// just initialized
assert_eq!(s.action(), SqueezeAction::Ignore);
s.advance();
let exp = vec![
SqueezeAction::Ignore, // first line, print as is
SqueezeAction::Print, // print squeeze symbol
SqueezeAction::Delete, // delete reoccurring line
];
let mut line = 0;
let mut idx = 1;
for z in v.chunks(LSIZE) {
for i in z {
s.process(*i, idx);
idx += 1;
}
let action = s.action();
s.advance();
assert_eq!(action, exp[line]);
line += 1;
}
}
#[test]
fn incomplete_while_squeeze() {
// fourth line only has 12 bytes and should be printed
let v = vec![0u8; 3 * LSIZE + 12];
let mut s = Squeezer::new(true);
// just initialized
assert_eq!(s.action(), SqueezeAction::Ignore);
s.advance();
let exp = vec![
SqueezeAction::Ignore, // first line, print as is
SqueezeAction::Print, // print squeeze symbol
SqueezeAction::Delete, // delete reoccurring line
SqueezeAction::Ignore, // last line only 12 bytes, print it
];
let mut line = 0;
let mut idx = 1;
for z in v.chunks(LSIZE) {
for i in z {
s.process(*i, idx);
idx += 1;
}
assert_eq!(s.action(), exp[line]);
s.advance();
line += 1;
}
}
#[test]
/// all three lines are different, print all
fn three_different_lines() {
let mut v: Vec<u8> = vec![];
v.extend(vec![0u8; 16]);
v.extend(vec![1u8; 16]);
v.extend(vec![2u8; 16]);
let mut s = Squeezer::new(true);
// just initialized
assert_eq!(s.action(), SqueezeAction::Ignore);
s.advance();
let exp = vec![
SqueezeAction::Ignore, // first line, print as is
SqueezeAction::Ignore, // different
SqueezeAction::Ignore, // different
];
let mut line = 0;
let mut idx = 1;
for z in v.chunks(LSIZE) {
for i in z {
s.process(*i, idx);
idx += 1;
}
let action = s.action();
assert_eq!(action, exp[line]);
s.advance();
line += 1;
}
}
#[test]
/// first two lines same, hence squeeze symbol, third line diff, hence
/// print
fn one_squeeze_no_delete() {
const LINES: usize = 3;
let mut v = vec![0u8; (LINES - 1) * LSIZE];
v.extend(vec![1u8; 16]);
let mut s = Squeezer::new(true);
// just initialized
assert_eq!(s.action(), SqueezeAction::Ignore);
s.advance();
let exp = vec![
SqueezeAction::Ignore, // first line, print as is
SqueezeAction::Print, // print squeeze symbol
SqueezeAction::Ignore, // different lines, print again
];
let mut line = 0;
let mut idx = 1;
for z in v.chunks(LSIZE) {
for i in z {
s.process(*i, idx);
idx += 1;
}
let action = s.action();
s.advance();
assert_eq!(action, exp[line]);
line += 1;
}
}
#[test]
/// First line all eq, 2nd half eq with first line, then change
fn second_line_different() {
const LINES: usize = 2;
let mut v = vec![0u8; (LINES - 1) * LSIZE];
v.extend(vec![0u8; 8]);
v.extend(vec![1u8; 8]);
let mut s = Squeezer::new(true);
// just initialized
assert_eq!(s.action(), SqueezeAction::Ignore);
s.advance();
let exp = vec![
SqueezeAction::Ignore, // first line, print as is
SqueezeAction::Ignore, // print squeeze symbol
];
let mut line = 0;
let mut idx = 1;
for z in v.chunks(LSIZE) {
for i in z {
s.process(*i, idx);
idx += 1;
}
let action = s.action();
s.advance();
assert_eq!(action, exp[line]);
line += 1;
}
}
#[test]
/// all three lines never become squeeze candidate (diff within line)
fn never_squeeze_candidate() {
let mut v = vec![];
v.extend(vec![0u8; 8]);
v.extend(vec![1u8; 8]);
v.extend(vec![0u8; 8]);
v.extend(vec![1u8; 8]);
v.extend(vec![0u8; 8]);
v.extend(vec![1u8; 8]);
let mut s = Squeezer::new(true);
// just initialized
assert_eq!(s.action(), SqueezeAction::Ignore);
s.advance();
let exp = vec![
SqueezeAction::Ignore, // first line, print as is
SqueezeAction::Ignore, // print squeeze symbol
SqueezeAction::Ignore, // print squeeze symbol
];
let mut line = 0;
let mut idx = 1;
for z in v.chunks(LSIZE) {
for i in z {
s.process(*i, idx);
idx += 1;
}
let action = s.action();
s.advance();
assert_eq!(action, exp[line]);
line += 1;
}
}
#[test]
fn mix_everything() {
let mut v = vec![];
v.extend(vec![10u8; 16]); // print
v.extend(vec![20u8; 16]); // print
v.extend(vec![0u8; 16]); // print
v.extend(vec![0u8; 16]); // *
v.extend(vec![10u8; 16]); // print
v.extend(vec![20u8; 16]); // print
v.extend(vec![0u8; 16]); // print
v.extend(vec![0u8; 16]); // *
v.extend(vec![0u8; 16]); // delete
v.extend(vec![0u8; 16]); // delete*
v.extend(vec![20u8; 16]); // print
v.extend(vec![0u8; 12]); // print, only 12 bytes
let mut s = Squeezer::new(true);
// just initialized
assert_eq!(s.action(), SqueezeAction::Ignore);
s.advance();
let exp = vec![
SqueezeAction::Ignore,
SqueezeAction::Ignore,
SqueezeAction::Ignore,
SqueezeAction::Print,
SqueezeAction::Ignore,
SqueezeAction::Ignore,
SqueezeAction::Ignore,
SqueezeAction::Print,
SqueezeAction::Delete,
SqueezeAction::Delete,
SqueezeAction::Ignore,
SqueezeAction::Ignore,
];
let mut line = 0;
let mut idx = 1;
for z in v.chunks(LSIZE) {
for i in z {
s.process(*i, idx);
idx += 1;
}
let action = s.action();
s.advance();
assert_eq!(action, exp[line]);
line += 1;
}
}
#[test]
fn last_char_diff() {
// see issue #62
let mut v = vec![];
v.extend(vec![20u8; 16]);
v.extend(vec![20u8; 15]);
v.push(61);
v.extend(vec![20u8; 16]);
v.extend(vec![20u8; 16]);
let mut s = Squeezer::new(true);
// just initialized
assert_eq!(s.action(), SqueezeAction::Ignore);
s.advance();
let exp = vec![
SqueezeAction::Ignore, // print as is
SqueezeAction::Ignore, // print as is
SqueezeAction::Ignore, // print as is
SqueezeAction::Print, // print '*' char
];
let mut line = 0;
let mut idx = 1;
for z in v.chunks(LSIZE) {
for i in z {
s.process(*i, idx);
idx += 1;
}
assert_eq!(s.action(), exp[line]);
s.advance();
line += 1;
}
}
#[test]
fn first_char_diff() {
// see issue #62
let mut v = vec![];
v.extend(vec![20u8; 16]);
v.push(61);
v.extend(vec![20u8; 15]);
v.extend(vec![20u8; 16]);
let mut s = Squeezer::new(true);
// just initialized
assert_eq!(s.action(), SqueezeAction::Ignore);
s.advance();
let exp = vec![
SqueezeAction::Ignore, // print as is
SqueezeAction::Ignore, // print as is
SqueezeAction::Ignore, // print as is
];
let mut line = 0;
let mut idx = 1;
for z in v.chunks(LSIZE) {
for i in z {
s.process(*i, idx);
idx += 1;
}
assert_eq!(s.action(), exp[line]);
s.advance();
line += 1;
}
}
}