261 Commits
Author SHA1 Message Date
David Peter 6ecc29b9c8 Remove sponsors 2026-04-30 10:24:05 +02:00
David Peter 8eb6d4771c Bump version to v0.17.0 2026-02-14 13:53:54 +01:00
David Peter 5d05382df8 Update README 2026-02-14 13:50:33 +01:00
David Peter 626f03bfbd Fix #254 2026-02-14 13:49:10 +01:00
n4n5 04d74bae47 feat: handle standard input "-" (#257) 2026-02-14 13:33:23 +01:00
n4n5 96e427cb4a Improve crate && fix CI (#256)
* feat: improve crate

* fix: CI
2026-02-14 13:09:13 +01:00
Filip Czaplicki 7f8db4f113 Add colors to --help (#253)
Thank you!
2026-02-14 13:08:00 +01:00
王品呈 2e2643782d Add option to output result in C include file style (#242) (#246)
* feat: Implement "-i/-include" option

* make clippy happy

Suppress clippy warning for too many arguments in Printer struct

* add tests & use buffered reading for include mode to handle large files

* Update CHANGELOG
2025-10-19 21:38:21 +02:00
David Peter 269860ba7e Update README 2025-09-23 19:38:25 +02:00
David Peter 2c8be77181 Update README 2025-09-04 09:00:22 +02:00
David Peter f16e2c96ea Fix CHANGELOG formatting 2025-08-31 21:40:15 +02:00
Friedrich 'friedɀ' SchwedlerandDavid Peter 585d06293a Add shell completion (#251)
* add commandline argument to generate shell completion

* update changelog

* fixup! add commandline argument to generate shell completion

* Update option name and description

---------

Co-authored-by: David Peter <mail@david-peter.de>
2025-08-31 21:39:17 +02:00
David Peter 909dd7d947 Update Windows runners 2025-08-31 21:22:59 +02:00
David Peter 5a295296fe Fix formatting 2025-08-31 21:21:22 +02:00
David Peter 38301f3759 Fix clippy, CI 2025-08-31 21:17:54 +02:00
Niclas SchwarzloseandDavid Peter f2547aed69 Add new braille character table and color schemes (#247)
* Add new braille character table and color schemes

* Add character for tab and change color for 0x7f

---------

Co-authored-by: David Peter <mail@david-peter.de>
2025-08-31 21:13:57 +02:00
Dan Hipschman 9ef7c346dd Enable custom colors with environment variables (#248)
* Enable custom colors with environment variables

* Rename HEXYL_XYZ env vars to HEXYL_COLOR_XYZ
2025-06-16 22:41:37 +02:00
Regis Boudinot 6220cae644 Check if terminal_width is less than offset and return 1 (see #244) (#245) 2025-06-08 21:35:42 +02:00
David Peter ad553e9e33 Merge pull request #249 from dan-hipschman/fix-ci
Fix CI
2025-06-08 21:11:18 +02:00
Dan Hipschman fbbd5e3733 Fix CI 2025-05-13 15:18:02 -07:00
David Peter 742c2fcbfd Update README 2025-03-09 19:29:26 +01:00
David Peter 79f7076b02 Merge pull request #239 from ilmanzo/issue238
Fix issue #238
2025-03-02 19:28:12 +01:00
David Peter cba09bf7b7 Merge pull request #241 from saulvaldelvira/master
Fix some clippy warnings
2025-02-19 21:39:49 +01:00
Saúl Valdelvira 06ff4b750e Fix some clippy warnings
Changes:
- Elid lifetimes when possible
- Use a slice instead of a Vec as function argument
- Rewrite &[b'*''] to b"*"
2025-02-19 12:44:08 +01:00
Andrea Manzini 630d92fb8f Fix issue #238 2025-01-26 18:54:13 +01:00
David Peter abc20a380c Bump version 2024-12-27 14:43:18 +01:00
David Peter dd89c22327 Remove color reference from README 2024-12-27 14:42:27 +01:00
David Peter 45a557e0df Merge pull request #235 from sahinfalcon/print-colour-table
PR #229 • Add option to print color table
2024-12-27 14:41:12 +01:00
David Peter 73cfd05a10 Minor updates 2024-12-27 14:37:04 +01:00
Taylan Sahin 37e9ef6a64 cargo fmt 2024-12-27 14:35:04 +01:00
Taylan Sahin 568f38ae77 PR #229 • Add option to print color table 2024-12-27 14:35:03 +01:00
David Peter 6e0bff2cc5 Merge pull request #234 from Integral-Tech/error-on-dir
fix: throw an error when try to view a directory
2024-12-27 14:29:34 +01:00
David Peter 1783fe6a9b Merge pull request #233 from lunrenyi/patch-1
docs(install): add x-cmd method to install hexyl
2024-12-27 14:26:02 +01:00
David Peter 733b9e12e4 Fix CI 2024-12-27 14:25:01 +01:00
David Peter 19198c2aac Minor updates 2024-12-27 14:23:02 +01:00
Integral c65b298e60 fix: throw an error when try to view a directory 2024-11-21 00:55:37 +08:00
lunrenyi f986c196d2 docs(install): add x-cmd method to install hexyl 2024-11-12 14:30:33 +08:00
David Peter dbed12e423 Update MSRV 2024-10-29 22:44:53 +01:00
David Peter 1927610953 Update MSRV 2024-10-29 22:42:12 +01:00
David Peter f8531b3e44 Updates for v0.15.0 2024-10-29 22:17:12 +01:00
David Peter 427a5524e1 Merge pull request #226 from v1gnesh/master
Add codepage 1047 for EBCDIC
2024-09-08 13:09:17 +02:00
v1gnesh 120cf29adf Add codepage 1047 for EBCDIC 2024-08-04 15:18:38 +05:30
David Peter d2cc025c6f Merge pull request #225 from sorairolake/rewrite-in-derive-api
Rewrite CLI using the derive API
2024-07-16 21:29:59 +02:00
Shun Sakai f8b87cc5e2 Change help output
Even if `-h` is specified, other than the first sentence will also be
printed.
2024-07-02 18:23:30 +09:00
Shun Sakai 252a6dbef4 Rewrite CLI using derive API 2024-07-01 22:39:28 +09:00
David Peter 16cc5d9757 Merge pull request #222 from wxpppp/master
deps: bump libc from 0.2.147 to 0.2.155
2024-06-22 21:35:19 +02:00
wangweijie d51086e0da deps: bump libc from 0.2.147 to 0.2.155
Signed-off-by: wangweijie <wangweijie@loongson.cn>
2024-06-06 10:17:30 +08:00
David Peter d8a26da964 Merge pull request #216 from FilipRazek/fix-upload-artifact-job
ci: Fix Debian artifact names
2024-01-25 09:15:36 +01:00
Filip Razek dcce9e1011 ci: Fix Debian artifact names 2024-01-24 22:38:28 +01:00
David Peter 651087ca9d Remove 'simple' adjective 2023-12-11 23:42:43 +01:00
David Peter 3bd84fd5cb Adapt version number 2023-12-11 23:42:22 +01:00
David Peter 0634a06470 Update CHANGELOG 2023-12-11 23:38:51 +01:00
David Peter 7a9234c182 Merge pull request #212 from sharkdp/add-ascii-character-table
Add pure ASCII character table
2023-12-11 23:31:17 +01:00
David Peter 45fa72ec3b Add ASCII character table
closes #36
2023-12-11 23:22:36 +01:00
David Peter 711627bfe8 Rename ascii-only to 'default', as it is not only ASCII 2023-12-11 23:00:49 +01:00
David Peter ba89e88dfd Update dependencies 2023-12-11 22:58:42 +01:00
David Peter 1792090799 Merge pull request #211 from sharifhsn/devzero
`/dev/zero` fix (final)
2023-12-11 22:55:38 +01:00
Sharif Haason 33d5b4dbda Flush output on first print for /dev/zero fix 2023-12-11 16:41:26 -05:00
David Peter 921327f2ae Merge pull request #210 from sharkdp/fix-no-color
Fix NO_COLOR behavior
2023-12-11 20:11:38 +01:00
David Peter 4bf875095c Fix NO_COLOR behavior 2023-12-11 20:06:10 +01:00
David Peter a78c00b10d Remove unused character_table field 2023-12-11 19:47:59 +01:00
David Peter 2bab0c769e Merge pull request #195 from sharifhsn/cp437
Implement character table control and codepage 437 option
2023-12-11 19:42:12 +01:00
Sharif Haason 1310b1d19e Adjust color function to not take character_table as arg 2023-12-11 13:33:44 -05:00
Sharif Haason a56b3b0c46 Adjust color function to not include character_table 2023-12-11 13:32:46 -05:00
Sharif Haason 7ca00279ed Correct docs 2023-12-10 13:21:23 -05:00
Sharif Haason dfb5d57ee7 Remove block coloring option for refactor 2023-12-10 13:21:23 -05:00
Sharif Haason a0a7d6761f Refactor color components into new module 2023-12-10 13:21:23 -05:00
Sharif Haason 7ef8e5aae4 Add test for code page 437 2023-12-10 13:21:23 -05:00
Sharif Haason 8d5b355b76 Correct asterisk colorization 2023-12-10 13:21:23 -05:00
Sharif Haason 19f325a691 Implement block character table 2023-12-10 13:21:23 -05:00
Sharif Haason 10ddbbad7d Use U+FB00 LATIN SMALL LIGATURE FF (ff) for FFh 2023-12-10 13:21:23 -05:00
Sharif Haason 58c8c06317 Clarify use of ␀ 2023-12-10 13:21:23 -05:00
sharifandDavid Peter 78477a90c9 Close quotation in help text
Co-authored-by: David Peter <sharkdp@users.noreply.github.com>
2023-12-10 13:21:23 -05:00
Sharif Haason 9362c2313a Attribute original author of CP437 2023-12-10 13:21:23 -05:00
Sharif Haason e363f2fdc4 Remove old as_char implementation 2023-12-10 13:21:23 -05:00
Sharif Haason 29e3350e69 Rename CharTable to CharacterTable 2023-12-10 13:21:23 -05:00
Sharif Haason 0c192e979b Make CharTable non-exhaustive 2023-12-10 13:21:23 -05:00
Sharif Haason c4f91959ef Add default value for character table 2023-12-10 13:21:23 -05:00
Sharif Haason 4e66357618 Use hyphens instead of underscores 2023-12-10 13:21:23 -05:00
Sharif Haason 6a230d34a4 Implement character table control and codepage 437 option 2023-12-10 13:21:23 -05:00
David Peter 663e4c19ff Merge pull request #208 from 0323pin/master
fix for ...
2023-09-23 21:49:32 +02:00
pin 894ca71b40 fix for ...
https://github.com/sharkdp/hexyl/pull/206#issuecomment-1722295392
2023-09-23 20:01:55 +02:00
David Peter e85953eeb5 Merge pull request #206 from 0323pin/master
Add NetBSD
2023-09-11 08:43:18 +02:00
pin f5ce9946f6 Add NetBSD 2023-09-11 08:35:32 +02:00
David Peter 668054fe50 Merge pull request #205 from cdump/master
fix arch linux repo link in readme
2023-08-27 22:17:32 +02:00
Maxim Andreev d6cb7af643 fix arch linux repo link in readme 2023-08-27 22:11:29 +03:00
David Peter 7b59a3f374 Merge pull request #203 from VlkrS/OpenBSD-Instruction-Fix
Correct OpenBSD instructions
2023-08-21 19:16:46 +02:00
VlkrS 3c69797f23 Correct OpenBSD instructions
I'm terribly sorry, but I just noticed that a copy&paste mistake slipped though in my initial PR.
2023-08-21 17:27:30 +02:00
David Peter 7e9bc5138d Merge pull request #202 from GrigorenkoPV/deps
Vet dependencies
2023-08-21 16:39:54 +02:00
David Peter ce70246f80 Merge pull request #201 from GrigorenkoPV/tests
Move tests to a separate `#[cfg(test)]` module
2023-08-21 16:39:22 +02:00
David Peter a306c82024 Merge pull request #199 from VlkrS/openbsd-doc
Add instructions for OpenBSD
2023-08-21 16:37:49 +02:00
Pavel Grigorenko e1e53335d3 Move tests to a separate #[cfg(test)] module 2023-08-21 16:01:22 +03:00
Pavel Grigorenko c9f113c8c4 cargo update 2023-08-21 15:42:40 +03:00
Pavel Grigorenko 59557bd433 Bump predicates to not depend on both 2.x & 3.x 2023-08-21 15:42:02 +03:00
VlkrS 39a94379c6 Add instructions for OpenBSD
Mention OpenBSD in README.md
2023-08-06 20:39:31 +02:00
David Peter 5981fc8725 Bump MSRV to 1.66 2023-07-26 08:54:15 +02:00
David Peter f4515b0f14 Update CHANGELOG 2023-07-26 08:51:26 +02:00
David Peter e3d406dd95 Update dependencies 2023-07-26 08:47:03 +02:00
David Peter b6b7e2c79a Merge pull request #197 from sharifhsn/stdin-fix
Correctly handle discontinuous input i.e. stdin
2023-07-23 13:07:31 +02:00
Sharif Haason dfad7b3faf Refactor to avoid code duplication 2023-06-20 10:55:07 -04:00
Sharif Haason ef9b530d6a Finish comment 2023-06-20 10:46:48 -04:00
Sharif Haason a3268b8791 Correctly handle discontinuous input i.e. stdin 2023-06-20 10:45:13 -04:00
David Peter 2a534483c8 Update README.md 2023-05-25 15:27:04 +02:00
David Peter e6ba196bcb Merge pull request #193 from v0idx/master
Add color reference table to README.md
2023-05-25 15:26:11 +02:00
David Peter d0bd971382 Merge pull request #192 from LemmingAvalanche/master
Bump version in readme (0.13.0)
2023-05-25 15:24:41 +02:00
v0idx 8eed3cadbb Add color reference table to README.md 2023-05-22 02:33:41 +01:00
Kristoffer Haugsbakk 38a9662ea5 Bump version in readme (0.13.0) 2023-05-18 17:31:31 +02:00
David Peter e10136d203 Updates for 0.13 2023-05-10 22:19:07 +02:00
David Peter 5f0373184f Add --characters, change meaning of -C
This changes the meaning of `-C` to "show the character panel". The long
version `--no-characters` can still be used to hide the character panel.

closes #187
2023-05-10 22:09:19 +02:00
David Peter 57d18482ba Add Copy and Clone for Endianness 2023-04-26 14:53:37 +02:00
David Peter 816779e968 Merge pull request #189 from RinHizakura/master
Support both little and big endian dump
2023-04-25 09:20:23 +02:00
David Peter 9be018fbb9 Add -e alias for --endianness=little 2023-04-25 09:12:26 +02:00
David Peter cba717b783 Minor doc comment adjustment 2023-04-25 08:51:31 +02:00
David Peter 580f865c4a Rename function 2023-04-25 08:50:55 +02:00
David Peter 20450310c9 Help text adjustments, do not require group-size to be present 2023-04-25 08:48:39 +02:00
RinHizakura b251575d94 Change design for endianness command line option 2023-04-24 23:44:49 +08:00
RinHizakura 58b038af27 Change le to non-abbreviated name little endian 2023-04-24 23:13:28 +08:00
RinHizakura d703cdfd78 Fix comment error and grammarly 2023-04-24 23:10:25 +08:00
RinHizakura 43f8b92502 Fix test for endianness 2023-04-24 22:59:23 +08:00
RinHizakura a966896d3f Fix integration test module name 2023-04-24 22:59:23 +08:00
RinHizakura 08b903270f Change endianness from boolean type to enum 2023-04-24 22:59:19 +08:00
David Peter dd601e4792 Run cargo fmt 2023-04-21 21:28:52 +02:00
RinHizakura 564cf64b1b Support both little and big endian dump 2023-04-21 21:26:43 +02:00
David Peter c24280620a Merge pull request #191 from sharkdp/ci-sync
CI update
2023-04-21 21:25:27 +02:00
David Peter bfa2726e05 CI update 2023-04-21 21:02:49 +02:00
David Peter d6af232691 Merge pull request #188 from sharkdp/ci-update
CI: macos 12
2023-02-26 19:53:56 +01:00
David Peter cc6987e270 CI: macos 12 2023-02-26 13:44:08 +01:00
David Peter ee56a3396d Bump version 2022-12-07 23:48:29 +01:00
David Peter 4833c2afe4 Merge pull request #183 from sharifhsn/owo
Use `owo-colors` instead of `anstyle`
2022-12-07 23:42:54 +01:00
sharifandDavid Peter dfd7bdef49 Update src/bin/hexyl.rs
Co-authored-by: David Peter <sharkdp@users.noreply.github.com>
2022-12-07 17:33:35 -05:00
sharif fd4aee2b85 Merge branch 'master' into owo 2022-12-07 16:51:30 -05:00
Sharif Haason cf8e7ec079 Revert default color option to always 2022-12-07 16:48:20 -05:00
Sharif Haason faa9866d11 Change group_bytes to satisfy merge conflict 2022-12-07 16:44:11 -05:00
David Peter 1df35eeb63 Renme --group-bytes to --group-size
For `xxd`-compatibility reasons

see #121
2022-12-07 21:50:12 +01:00
David Peter 437a15f3d6 One panel by default is terminal is not wide enough
closes #182
2022-12-07 21:35:34 +01:00
David Peter 70fa092df1 Do not fail if output is not a TTY for --panels=auto
closes #184
2022-12-07 21:33:20 +01:00
Sharif Haason b2f0e75ed2 Satisfy clippy 2022-12-06 13:22:34 -05:00
Sharif Haason 4fc0c1f1b0 Replace anstyle with owo-colors 2022-12-06 11:27:16 -05:00
Sharif Haason 4ce6e9d799 Remove now unnecessary byte_char_panel_g 2022-12-06 11:26:19 -05:00
Sharif Haason 5bd76fd11b Replace is-terminal with supports-color and make auto default for color 2022-12-06 11:24:53 -05:00
David Peter c7e2235293 Update CHANGELOG 2022-12-05 10:01:03 +01:00
David Peter 421bd73ec1 Revert "Replace libc with rustix"
This reverts commit 571312dd6e.
2022-12-05 09:52:27 +01:00
David Peter e888021a48 Update version in README, man page 2022-12-05 09:48:10 +01:00
David Peter 7d38f70918 Show 0x00-bytes as '⋄' instead of '0', closes #166 2022-12-05 09:45:38 +01:00
David Peter c6af2edda8 Bump version 2022-12-05 09:32:07 +01:00
David Peter a7fadfaaf2 Update dependencies 2022-12-05 09:31:43 +01:00
David Peter 8577a17b14 Ignore EPIPE silently
fixes a regression introduced in a6aeabfde4

closes #181
2022-12-05 09:30:13 +01:00
David Peter 2d8b600c48 Update CHANGELOG 2022-12-05 09:11:47 +01:00
David Peter f8601e5bc9 Merge pull request #180 from sharifhsn/altdeps
Replace outdated dependencies
2022-12-05 09:03:40 +01:00
David Peter 9770b89cf4 Merge pull request #178 from sharifhsn/base
[feature] Implement alternate bases
2022-12-05 09:03:03 +01:00
Sharif Haason 571312dd6e Replace libc with rustix 2022-12-04 21:48:10 -05:00
Sharif Haason fcd09fffac Relax anstyle version 2022-12-04 21:43:03 -05:00
Sharif Haason 188e753675 Replace atty with is-terminal 2022-12-04 21:42:44 -05:00
Sharif Haason 030235afb7 Improve help text for --base 2022-12-04 21:36:42 -05:00
Sharif Haason 7847e8cd33 Remove debug statement 2022-11-27 19:45:33 -05:00
Sharif Haason 32f4ce9987 cargo fmt 2022-11-27 17:52:29 -05:00
Sharif Haason 2a4b17a162 Add tests for alternate base 2022-11-27 17:32:18 -05:00
Sharif Haason 5e3c49f933 cargo fmt 2022-11-27 17:19:48 -05:00
Sharif Haason fc794b290c Account for new panel arguments 2022-11-27 17:19:33 -05:00
Sharif Haason e6000a56f4 Parse -b argument 2022-11-27 17:18:38 -05:00
Sharif Haason 2861cd51ac Adjust panel calculation to account for base and group 2022-11-27 17:17:52 -05:00
Sharif Haason a8cffbb6df Move group_bytes so it can be used for panels 2022-11-27 17:17:25 -05:00
Sharif Haason 7bb6343d2a Add --base/-b argument 2022-11-27 17:16:48 -05:00
Sharif Haason 7de17c4f35 Adjust spaces on empty lines 2022-11-27 17:15:26 -05:00
Sharif Haason ec61c2a789 Print header only if non-empty 2022-11-27 17:14:30 -05:00
Sharif Haason 7f7b88a914 Adjust tests 2022-11-27 17:13:36 -05:00
Sharif Haason 85fa75ba67 Differentiate format on base 2022-11-27 17:11:55 -05:00
Sharif Haason d5d645fc48 Add Base to Printer 2022-11-27 17:11:21 -05:00
David Peter 8d3aa65134 Merge pull request #176 from sharifhsn/anstyle
Change `ansi_term` to `anstyle`
2022-11-27 21:02:26 +01:00
Sharif Haason 252b4e84c3 Merge branch 'master' into anstyle 2022-11-27 10:03:53 -05:00
Sharif Haason 3dc118d0ad cargo update 2022-11-27 09:47:38 -05:00
Mikhail Katychev b1ff8b6ce4 added squeeze tests 2022-11-26 20:18:21 +01:00
David Peter fe65792b96 Add CHANGELOG entry 2022-11-26 13:07:06 +01:00
David Peter 31c0bcfeae Improve error messages 2022-11-26 13:07:06 +01:00
RinHizakura c0f94ad6aa Refine the support of byte grouping 2022-11-26 13:07:06 +01:00
RinHizakura 55fed5e3c0 Fix for clap4 2022-11-26 13:07:06 +01:00
RinHizakura aeee0a1033 Add new integration tests for group bytes feature 2022-11-26 13:07:06 +01:00
RinHizakura 179c8f40a2 Support byte grouping 2022-11-26 13:07:06 +01:00
Sharif Haason fb2500d7d6 Include Cargo.lock 2022-11-19 11:40:03 -05:00
Sharif Haason ad9ecda952 Remove once_cell dependency 2022-11-19 11:04:59 -05:00
Sharif Haason c43effefd6 cargo fmt 2022-11-19 11:04:13 -05:00
Sharif Haason 0b6454ed9d Use Vec of enums for color instead of HashMap 2022-11-19 10:50:31 -05:00
Sharif Haason 557ae66ed5 Update Cargo.lock 2022-11-18 16:15:49 -05:00
Sharif Haason df1afdc73f Merge branch 'anstyle' of https://github.com/sharifhsn/hexyl into anstyle 2022-11-18 16:15:12 -05:00
Sharif Haason 9106b899b2 Bump MSRV 2022-11-18 19:22:24 +01:00
Sharif Haason df1c36e5a7 Satisfy clippy 2022-11-18 19:22:24 +01:00
Sharif Haason ac610f7bad Add get_one and fix ordering 2022-11-18 19:22:24 +01:00
Sharif Haason 767740865f Update predicate for default_value_if 2022-11-18 19:22:24 +01:00
Sharif Haason 1fb324f411 Use ArgAction for flags 2022-11-18 19:22:24 +01:00
Sharif Haason 3d0bc3d0eb Replace 'value_of with get_one::<String>` 2022-11-18 19:22:24 +01:00
Sharif Haason 8a52261a50 Replace possible_values with value_parser 2022-11-18 19:22:24 +01:00
Sharif Haason ccc4fb9ba9 Replace takes_value with num_args 2022-11-18 19:22:24 +01:00
Sharif Haason 0baa22f152 DeriveDisplayOrder now default 2022-11-18 19:22:24 +01:00
Sharif Haason 165ced9307 Update to clap 4.0 2022-11-18 19:22:24 +01:00
Sharif Haason 3888a20dfe Bump MSRV 2022-11-18 11:06:18 -05:00
Sharif Haason 75b37fa125 Use compatible lockfile 2022-11-18 11:01:39 -05:00
Sharif Haason 794e1935e3 Prerender color strings for better performance 2022-11-18 10:39:54 -05:00
Sharif Haason c49aa079b4 Add color only when needed 2022-11-18 08:19:13 -05:00
Sharif Haason 675ef6d4a8 Remove calls to ansi_term 2022-11-17 22:33:49 -05:00
Sharif Haason eade38c94a Explicitly paint with style formatting 2022-11-17 22:33:03 -05:00
Sharif Haason 98ceec18cd Replace colors with ANSI styles 2022-11-17 22:15:53 -05:00
Sharif Haason a49f295acc Replace deprecated ansi_term with anstyle 2022-11-17 22:12:00 -05:00
Sharif Haason 0caab15255 Document the main loop 2022-11-13 22:15:41 +01:00
Sharif Haason 828fb210ce Use repeat byte when discovering squeeze 2022-11-13 22:15:41 +01:00
Sharif Haason 1e43783616 Clean up flags for ergonomics 2022-11-13 22:15:41 +01:00
Sharif Haason 6b7cfefec6 Use repeated byte optimization on nonzero bytes 2022-11-13 22:15:41 +01:00
Sharif Haason 5f82942f98 Add second check if read fails unexpectedly 2022-11-13 22:15:41 +01:00
Sharif Haason f6a1c4e8e3 Add unit tests for new implementation of squeeze 2022-11-13 22:15:41 +01:00
Sharif Haason b4fb1b079c Print last line only when squeezed 2022-11-13 22:15:41 +01:00
Sharif Haason f9f59d653d Print asterisk when no position panel 2022-11-13 22:15:41 +01:00
Sharif Haason c82695f0a9 Fix incomplete line ending when squeezed 2022-11-13 22:15:41 +01:00
Sharif Haason 9d938b11ed Remove unnecessary ending case (covered by next) 2022-11-13 22:15:41 +01:00
Sharif Haason d5982c0eb0 Optimize line check when squeezing zero bytes 2022-11-13 22:15:41 +01:00
Sharif Haason 1502419ad4 Document byte_hex_panel_g 2022-11-13 22:15:41 +01:00
Sharif Haason f5be845d70 Satisfy clippy 2022-11-13 22:15:41 +01:00
Sharif Haason 664025477b Alter when position panel is called 2022-11-13 22:15:41 +01:00
Sharif Haason 074bc791c2 Fix incomplete line ending 2022-11-13 22:15:41 +01:00
Sharif Haason 77a4ad25f1 Remove unnecessary import 2022-11-13 22:15:41 +01:00
Sharif Haason ca31d466ba Keep newline consistent 2022-11-13 22:15:41 +01:00
Sharif Haason 945e7a3472 Remove unnecessary squeezer module 2022-11-13 22:15:41 +01:00
Sharif Haason 75d612ff11 Alter newline usage 2022-11-13 22:15:41 +01:00
Sharif Haason 15e8abfc9a Complete architectural rewrite (intentionally fails) 2022-11-13 22:15:41 +01:00
Sharif Haason b36d74ed9d Track squeeze_byte correctly 2022-11-13 22:15:41 +01:00
Sharif Haason 094bf63f02 Eliminate unnecessary call to active 2022-11-13 22:15:41 +01:00
Sharif Haason e0e870de27 Store squeezed byte instead of pushing to vector 2022-11-13 22:15:41 +01:00
Sharif Haason 966421c522 Undo inlining to satisfy MSRV 2022-11-13 22:15:41 +01:00
Sharif Haason 166c2cc46b Inline StdoutLock variable 2022-11-13 22:15:41 +01:00
Sharif Haason 74a5e4bc84 Print first line always 2022-11-13 22:15:41 +01:00
Sharif Haason 363ea0a586 Undo perf regression on squeezer 2022-11-13 22:15:41 +01:00
Sharif Haason 1c4c7ed9a5 Satisfy clippy 2022-11-13 22:15:41 +01:00
Sharif Haason b5c224c119 Fix for no position panel 2022-11-13 22:15:41 +01:00
Sharif Haason d6e34d9dd2 Fix issues with hello_world_elf64 2022-11-13 22:15:41 +01:00
Sharif Haason 1cb346bd13 Fixes --no-characters causes output artifacts when squeezing lines #171 2022-11-13 22:15:41 +01:00
Sharif Haason daa40900bd Fix char panel showing even if disabled 2022-11-13 22:15:41 +01:00
Sharif Haason d98c6edb55 Fix overflow when printing without char panel 2022-11-13 22:15:41 +01:00
Sharif Haason 73bfc68cd0 cargo fmt 2022-11-13 22:15:41 +01:00
Sharif Haason 5d3f36d066 Remove write! formatting from hot loop 2022-11-13 22:15:41 +01:00
Sharif Haason d214bc63f1 Precalculate position formatting in Printer state 2022-11-13 22:15:41 +01:00
Sharif Haason a6aeabfde4 Refactor code to replace manual buffering with BufReader and BufWriter 2022-11-13 22:15:41 +01:00
Sharif Haason 9602667991 Rename with_panels to num_panels 2022-10-04 18:09:47 +02:00
Sharif Haason 9ccf18f4c0 Update CHANGELOG.md with PrinterBuilder 2022-10-04 18:09:47 +02:00
Sharif Haason aa921ca184 Rename with_squeeze to enable_squeezing 2022-10-04 18:09:47 +02:00
Sharif Haason 4823523b7d Remove superfluous with_writer function 2022-10-04 18:09:47 +02:00
Sharif Haason 4569c8f871 Self-document simple.rs example with builder API 2022-10-04 18:09:47 +02:00
Sharif Haason 80163764c3 Implement builder pattern 2022-10-04 18:09:47 +02:00
Sharif Haason 9bc81820e7 Update CHANGELOG 2022-09-11 21:45:06 +02:00
Sharif Haason 273ef0c045 Remove short options for panels and terminal-width 2022-09-11 21:45:06 +02:00
Sharif Haason ac1a67e90c Fix abbreviation clash 2022-09-11 21:45:06 +02:00
Sharif Haason 12324d615a Rename --columns to --panels 2022-09-11 21:45:06 +02:00
Sharif Haason 6b062ba127 Fix typo 2022-09-11 21:45:06 +02:00
Sharif Haason 8c7c3a073c Use --columns=auto instead of --auto-width 2022-09-11 21:45:06 +02:00
Sharif Haason 0565901fad Return a proper error if terminal size fails 2022-09-11 21:45:06 +02:00
Sharif Haason 41b3241718 Change columns type to u16 2022-09-11 21:45:06 +02:00
Sharif Haason 975a406d20 Remove debugging line 2022-09-11 21:45:06 +02:00
Sharif Haason aa43c20f39 Bump MSRV 2022-09-11 21:45:06 +02:00
Sharif Haason 1fdd770093 Implement -a and -t for terminal width setting 2022-09-11 21:45:06 +02:00
Sharif Haason a3fde45c18 Added test for multiple columns 2022-09-11 21:45:06 +02:00
Sharif Haason 85659a9cd6 Prevent overflow when columns >= 32 2022-09-11 21:45:06 +02:00
sharifandDavid Peter 2a4a22c9cb Update src/squeezer.rs
Co-authored-by: David Peter <sharkdp@users.noreply.github.com>
2022-09-11 21:45:06 +02:00
Sharif Haason b820bdfeca cargo fmt 2022-09-11 21:45:06 +02:00
Sharif Haason b9348ffb1d Adds --columns for Print using custom width #13 2022-09-11 21:45:06 +02:00
15 changed files with 3328 additions and 1971 deletions

No files matched your search

+116 -122
View File
@@ -1,8 +1,8 @@
name: CICD
env:
MIN_SUPPORTED_RUST_VERSION: "1.56.1"
CICD_INTERMEDIATES_DIR: "_cicd-intermediates"
MSRV_FEATURES: ""
on:
workflow_dispatch:
@@ -14,84 +14,104 @@ on:
- '*'
jobs:
crate_metadata:
name: Extract crate metadata
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Extract crate information
id: crate_metadata
run: |
cargo metadata --no-deps --format-version 1 | jq -r '"name=" + .packages[0].name' | tee -a $GITHUB_OUTPUT
cargo metadata --no-deps --format-version 1 | jq -r '"version=" + .packages[0].version' | tee -a $GITHUB_OUTPUT
cargo metadata --no-deps --format-version 1 | jq -r '"maintainer=" + .packages[0].authors[0]' | tee -a $GITHUB_OUTPUT
cargo metadata --no-deps --format-version 1 | jq -r '"homepage=" + .packages[0].homepage' | tee -a $GITHUB_OUTPUT
cargo metadata --no-deps --format-version 1 | jq -r '"msrv=" + .packages[0].rust_version' | tee -a $GITHUB_OUTPUT
outputs:
name: ${{ steps.crate_metadata.outputs.name }}
version: ${{ steps.crate_metadata.outputs.version }}
maintainer: ${{ steps.crate_metadata.outputs.maintainer }}
homepage: ${{ steps.crate_metadata.outputs.homepage }}
msrv: ${{ steps.crate_metadata.outputs.msrv }}
ensure_cargo_fmt:
name: Ensure 'cargo fmt' has been run
runs-on: ubuntu-24.04
steps:
- uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt
- uses: actions/checkout@v3
- run: cargo fmt -- --check
min_version:
name: Minimum supported rust version
runs-on: ubuntu-18.04
runs-on: ubuntu-24.04
needs: crate_metadata
steps:
- name: Checkout source code
uses: actions/checkout@v2
uses: actions/checkout@v3
- name: Install rust toolchain (v${{ env.MIN_SUPPORTED_RUST_VERSION }})
uses: actions-rs/toolchain@v1
- name: Install rust toolchain (v${{ needs.crate_metadata.outputs.msrv }})
uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ env.MIN_SUPPORTED_RUST_VERSION }}
default: true
profile: minimal # minimal component installation (ie, no documentation)
components: clippy, rustfmt
- name: Ensure `cargo fmt` has been run
uses: actions-rs/cargo@v1
with:
command: fmt
args: -- --check
toolchain: ${{ needs.crate_metadata.outputs.msrv }}
components: clippy
- name: Run clippy (on minimum supported rust version to prevent warnings we can't fix)
uses: actions-rs/cargo@v1
with:
command: clippy
args: --locked --all-targets
run: cargo clippy --locked --all-targets ${{ env.MSRV_FEATURES }}
- name: Run tests
uses: actions-rs/cargo@v1
with:
command: test
args: --locked
run: cargo test --locked ${{ env.MSRV_FEATURES }}
build:
name: ${{ matrix.job.os }} (${{ matrix.job.target }})
name: ${{ matrix.job.target }} (${{ matrix.job.os }})
runs-on: ${{ matrix.job.os }}
needs: crate_metadata
strategy:
fail-fast: false
matrix:
job:
- { os: ubuntu-18.04, target: arm-unknown-linux-gnueabihf , use-cross: true }
- { os: ubuntu-18.04, target: aarch64-unknown-linux-gnu , use-cross: true }
- { os: ubuntu-18.04, target: i686-unknown-linux-gnu , use-cross: true }
- { os: ubuntu-18.04, target: i686-unknown-linux-musl , use-cross: true }
- { os: ubuntu-18.04, target: x86_64-unknown-linux-gnu }
- { os: ubuntu-18.04, target: x86_64-unknown-linux-musl , use-cross: true }
- { os: macos-10.15 , target: x86_64-apple-darwin }
# - { os: windows-2019, target: i686-pc-windows-gnu } ## disabled; error: linker `i686-w64-mingw32-gcc` not found
- { os: windows-2019, target: i686-pc-windows-msvc }
- { os: windows-2019, target: x86_64-pc-windows-gnu }
- { os: windows-2019, target: x86_64-pc-windows-msvc }
- { target: aarch64-unknown-linux-gnu , os: ubuntu-24.04, use-cross: true }
- { target: arm-unknown-linux-gnueabihf , os: ubuntu-24.04, use-cross: true }
- { target: arm-unknown-linux-musleabihf, os: ubuntu-24.04, use-cross: true }
- { target: i686-pc-windows-msvc , os: windows-2025 }
- { target: i686-unknown-linux-gnu , os: ubuntu-24.04, use-cross: true }
- { target: i686-unknown-linux-musl , os: ubuntu-24.04, use-cross: true }
- { target: x86_64-apple-darwin , os: macos-15-intel }
- { target: aarch64-apple-darwin , os: macos-15 }
# Was causing CI failures unrelated to app logic
# - { target: x86_64-pc-windows-gnu , os: windows-2019 }
- { target: x86_64-pc-windows-msvc , os: windows-2025 }
- { target: x86_64-unknown-linux-gnu , os: ubuntu-24.04, use-cross: true }
- { target: x86_64-unknown-linux-musl , os: ubuntu-24.04, use-cross: true }
env:
BUILD_CMD: cargo
steps:
- name: Checkout source code
uses: actions/checkout@v2
uses: actions/checkout@v3
- name: Install prerequisites
shell: bash
run: |
case ${{ matrix.job.target }} in
arm-unknown-linux-gnueabihf) sudo apt-get -y update ; sudo apt-get -y install gcc-arm-linux-gnueabihf ;;
arm-unknown-linux-*) sudo apt-get -y update ; sudo apt-get -y install gcc-arm-linux-gnueabihf ;;
aarch64-unknown-linux-gnu) sudo apt-get -y update ; sudo apt-get -y install gcc-aarch64-linux-gnu ;;
esac
- name: Extract crate information
shell: bash
run: |
echo "PROJECT_NAME=$(sed -n 's/^name = "\(.*\)"/\1/p' Cargo.toml | head -n1)" >> $GITHUB_ENV
echo "PROJECT_VERSION=$(sed -n 's/^version = "\(.*\)"/\1/p' Cargo.toml | head -n1)" >> $GITHUB_ENV
echo "PROJECT_MAINTAINER=$(sed -n 's/^authors = \["\(.*\)"\]/\1/p' Cargo.toml)" >> $GITHUB_ENV
echo "PROJECT_HOMEPAGE=$(sed -n 's/^homepage = "\(.*\)"/\1/p' Cargo.toml)" >> $GITHUB_ENV
- name: Install Rust toolchain
uses: actions-rs/toolchain@v1
uses: dtolnay/rust-toolchain@stable
with:
toolchain: stable
target: ${{ matrix.job.target }}
override: true
profile: minimal # minimal component installation (ie, no documentation)
targets: ${{ matrix.job.target }}
- name: Install cross
if: matrix.job.use-cross
uses: taiki-e/install-action@v2
with:
tool: cross
- name: Overwrite build command env variable
if: matrix.job.use-cross
shell: bash
run: echo "BUILD_CMD=cross" >> $GITHUB_ENV
- name: Show version information (Rust, cargo, GCC)
shell: bash
@@ -104,21 +124,16 @@ jobs:
rustc -V
- name: Build
uses: actions-rs/cargo@v1
with:
use-cross: ${{ matrix.job.use-cross }}
command: build
args: --release --target=${{ matrix.job.target }}
shell: bash
run: $BUILD_CMD build --locked --release --target=${{ matrix.job.target }}
- name: Run example
uses: actions-rs/cargo@v1
with:
use-cross: ${{ matrix.job.use-cross }}
command: run
args: --release --target=${{ matrix.job.target }} --example=simple
if: ${{ !matrix.job.use-cross }}
shell: bash
run: $BUILD_CMD run --release --target=${{ matrix.job.target }} --example=simple
- name: Strip debug information from executable
id: strip
- name: Set binary name & path
id: bin
shell: bash
run: |
# Figure out suffix of binary
@@ -127,31 +142,13 @@ jobs:
*-pc-windows-*) EXE_suffix=".exe" ;;
esac;
# Figure out what strip tool to use if any
STRIP="strip"
case ${{ matrix.job.target }} in
arm-unknown-linux-gnueabihf) STRIP="arm-linux-gnueabihf-strip" ;;
aarch64-unknown-linux-gnu) STRIP="aarch64-linux-gnu-strip" ;;
*-pc-windows-msvc) STRIP="" ;;
esac;
# Setup paths
BIN_DIR="${{ env.CICD_INTERMEDIATES_DIR }}/stripped-release-bin/"
mkdir -p "${BIN_DIR}"
BIN_NAME="${{ env.PROJECT_NAME }}${EXE_suffix}"
BIN_PATH="${BIN_DIR}/${BIN_NAME}"
BIN_NAME="${{ needs.crate_metadata.outputs.name }}${EXE_suffix}"
BIN_PATH="target/${{ matrix.job.target }}/release/${BIN_NAME}"
# Copy the release build binary to the result location
cp "target/${{ matrix.job.target }}/release/${BIN_NAME}" "${BIN_DIR}"
# Also strip if possible
if [ -n "${STRIP}" ]; then
"${STRIP}" "${BIN_PATH}"
fi
# Let subsequent steps know where to find the (stripped) bin
echo ::set-output name=BIN_PATH::${BIN_PATH}
echo ::set-output name=BIN_NAME::${BIN_NAME}
# Let subsequent steps know where to find the binary
echo "BIN_PATH=${BIN_PATH}" >> $GITHUB_OUTPUT
echo "BIN_NAME=${BIN_NAME}" >> $GITHUB_OUTPUT
- name: Set testing options
id: test-options
@@ -159,44 +156,41 @@ jobs:
run: |
# test only library unit tests and binary for arm-type targets
unset CARGO_TEST_OPTIONS
unset CARGO_TEST_OPTIONS ; case ${{ matrix.job.target }} in arm-* | aarch64-*) CARGO_TEST_OPTIONS="--lib --bin ${PROJECT_NAME}" ;; esac;
echo ::set-output name=CARGO_TEST_OPTIONS::${CARGO_TEST_OPTIONS}
case ${{ matrix.job.target }} in arm-* | aarch64-*) CARGO_TEST_OPTIONS="--lib --bin ${{ needs.crate_metadata.outputs.name }}" ;; esac;
echo "CARGO_TEST_OPTIONS=${CARGO_TEST_OPTIONS}" >> $GITHUB_OUTPUT
- name: Run tests
uses: actions-rs/cargo@v1
with:
use-cross: ${{ matrix.job.use-cross }}
command: test
args: --target=${{ matrix.job.target }} ${{ steps.test-options.outputs.CARGO_TEST_OPTIONS}}
shell: bash
run: $BUILD_CMD test --locked --target=${{ matrix.job.target }} ${{ steps.test-options.outputs.CARGO_TEST_OPTIONS}}
- name: Setup Pandoc
uses: r-lib/actions/setup-pandoc@v2
- name: Generate man page
run: pandoc -s -f markdown -t man -o "doc/${{ env.PROJECT_NAME }}.1" "doc/${{ env.PROJECT_NAME }}.1.md"
run: pandoc -s -f markdown -t man -o "doc/${{ needs.crate_metadata.outputs.name }}.1" "doc/${{ needs.crate_metadata.outputs.name }}.1.md"
- name: Create tarball
id: package
shell: bash
run: |
PKG_suffix=".tar.gz" ; case ${{ matrix.job.target }} in *-pc-windows-*) PKG_suffix=".zip" ;; esac;
PKG_BASENAME=${PROJECT_NAME}-v${PROJECT_VERSION}-${{ matrix.job.target }}
PKG_BASENAME=${{ needs.crate_metadata.outputs.name }}-v${{ needs.crate_metadata.outputs.version }}-${{ matrix.job.target }}
PKG_NAME=${PKG_BASENAME}${PKG_suffix}
echo ::set-output name=PKG_NAME::${PKG_NAME}
echo "PKG_NAME=${PKG_NAME}" >> $GITHUB_OUTPUT
PKG_STAGING="${{ env.CICD_INTERMEDIATES_DIR }}/package"
ARCHIVE_DIR="${PKG_STAGING}/${PKG_BASENAME}/"
mkdir -p "${ARCHIVE_DIR}"
# Binary
cp "${{ steps.strip.outputs.BIN_PATH }}" "$ARCHIVE_DIR"
# Man page
cp "doc/${{ env.PROJECT_NAME }}.1" "$ARCHIVE_DIR"
cp "${{ steps.bin.outputs.BIN_PATH }}" "$ARCHIVE_DIR"
# README, LICENSE and CHANGELOG files
cp "README.md" "LICENSE-MIT" "LICENSE-APACHE" "CHANGELOG.md" "$ARCHIVE_DIR"
# Man page
cp "doc/${{ needs.crate_metadata.outputs.name }}.1" "$ARCHIVE_DIR"
# base compressed package
pushd "${PKG_STAGING}/" >/dev/null
case ${{ matrix.job.target }} in
@@ -206,7 +200,7 @@ jobs:
popd >/dev/null
# Let subsequent steps know where to find the compressed package
echo ::set-output name=PKG_PATH::"${PKG_STAGING}/${PKG_NAME}"
echo "PKG_PATH=${PKG_STAGING}/${PKG_NAME}" >> $GITHUB_OUTPUT
- name: Create Debian package
id: debian-package
@@ -218,10 +212,10 @@ jobs:
DPKG_DIR="${DPKG_STAGING}/dpkg"
mkdir -p "${DPKG_DIR}"
DPKG_BASENAME=${PROJECT_NAME}
DPKG_CONFLICTS=${PROJECT_NAME}-musl
case ${{ matrix.job.target }} in *-musl) DPKG_BASENAME=${PROJECT_NAME}-musl ; DPKG_CONFLICTS=${PROJECT_NAME} ;; esac;
DPKG_VERSION=${PROJECT_VERSION}
DPKG_BASENAME=${{ needs.crate_metadata.outputs.name }}
DPKG_CONFLICTS=${{ needs.crate_metadata.outputs.name }}-musl
case ${{ matrix.job.target }} in *-musl*) DPKG_BASENAME=${{ needs.crate_metadata.outputs.name }}-musl ; DPKG_CONFLICTS=${{ needs.crate_metadata.outputs.name }} ;; esac;
DPKG_VERSION=${{ needs.crate_metadata.outputs.version }}
unset DPKG_ARCH
case ${{ matrix.job.target }} in
@@ -233,14 +227,14 @@ jobs:
esac;
DPKG_NAME="${DPKG_BASENAME}_${DPKG_VERSION}_${DPKG_ARCH}.deb"
echo ::set-output name=DPKG_NAME::${DPKG_NAME}
echo "DPKG_NAME=${DPKG_NAME}" >> $GITHUB_OUTPUT
# Binary
install -Dm755 "${{ steps.strip.outputs.BIN_PATH }}" "${DPKG_DIR}/usr/bin/${{ steps.strip.outputs.BIN_NAME }}"
install -Dm755 "${{ steps.bin.outputs.BIN_PATH }}" "${DPKG_DIR}/usr/bin/${{ steps.bin.outputs.BIN_NAME }}"
# Man page
install -Dm644 "doc/${{ env.PROJECT_NAME }}.1" "${DPKG_DIR}/usr/share/man/man1/${{ env.PROJECT_NAME }}.1"
gzip -n --best "${DPKG_DIR}/usr/share/man/man1/${{ env.PROJECT_NAME }}.1"
install -Dm644 'doc/${{ needs.crate_metadata.outputs.name }}.1' "${DPKG_DIR}/usr/share/man/man1/${{ needs.crate_metadata.outputs.name }}.1"
gzip -n --best "${DPKG_DIR}/usr/share/man/man1/${{ needs.crate_metadata.outputs.name }}.1"
# README and LICENSE
install -Dm644 "README.md" "${DPKG_DIR}/usr/share/doc/${DPKG_BASENAME}/README.md"
@@ -251,12 +245,12 @@ jobs:
cat > "${DPKG_DIR}/usr/share/doc/${DPKG_BASENAME}/copyright" <<EOF
Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: ${{ env.PROJECT_NAME }}
Source: ${{ env.PROJECT_HOMEPAGE }}
Upstream-Name: ${{ needs.crate_metadata.outputs.name }}
Source: ${{ needs.crate_metadata.outputs.homepage }}
Files: *
Copyright: ${{ env.PROJECT_MAINTAINER }}
Copyright: $COPYRIGHT_YEARS ${{ env.PROJECT_MAINTAINER }}
Copyright: ${{ needs.crate_metadata.outputs.maintainer }}
Copyright: $COPYRIGHT_YEARS ${{ needs.crate_metadata.outputs.maintainer }}
License: Apache-2.0 or MIT
License: Apache-2.0
@@ -297,28 +291,28 @@ jobs:
Version: ${DPKG_VERSION}
Section: utils
Priority: optional
Maintainer: ${{ env.PROJECT_MAINTAINER }}
Homepage: ${{ env.PROJECT_HOMEPAGE }}
Maintainer: ${{ needs.crate_metadata.outputs.maintainer }}
Homepage: ${{ needs.crate_metadata.outputs.homepage }}
Architecture: ${DPKG_ARCH}
Provides: ${{ env.PROJECT_NAME }}
Provides: ${{ needs.crate_metadata.outputs.name }}
Conflicts: ${DPKG_CONFLICTS}
Description: colorized hex viewer for the terminal.
Description: A command-line benchmarking tool
EOF
DPKG_PATH="${DPKG_STAGING}/${DPKG_NAME}"
echo ::set-output name=DPKG_PATH::${DPKG_PATH}
echo "DPKG_PATH=${DPKG_PATH}" >> $GITHUB_OUTPUT
# build dpkg
fakeroot dpkg-deb --build "${DPKG_DIR}" "${DPKG_PATH}"
- name: "Artifact upload: tarball"
uses: actions/upload-artifact@master
uses: actions/upload-artifact@main
with:
name: ${{ steps.package.outputs.PKG_NAME }}
path: ${{ steps.package.outputs.PKG_PATH }}
- name: "Artifact upload: Debian package"
uses: actions/upload-artifact@master
uses: actions/upload-artifact@main
if: steps.debian-package.outputs.DPKG_NAME
with:
name: ${{ steps.debian-package.outputs.DPKG_NAME }}
@@ -329,7 +323,7 @@ jobs:
shell: bash
run: |
unset IS_RELEASE ; if [[ $GITHUB_REF =~ ^refs/tags/v[0-9].* ]]; then IS_RELEASE='true' ; fi
echo ::set-output name=IS_RELEASE::${IS_RELEASE}
echo "IS_RELEASE=${IS_RELEASE}" >> $GITHUB_OUTPUT
- name: Publish archives and packages
uses: softprops/action-gh-release@v1
+104 -5
View File
@@ -1,19 +1,118 @@
# unreleased
## Changes
# v0.17.0
## Features
- Add option to output result in C include file style, see #242 (@wpcwzy)
- Add `--color-scheme` option, see #247 (@aticu)
- Add `braille` character table, see #247 (@aticu)
- Add command line argument to generate shell completion, see #155 (@friedz)
- Add colors to `--help`/`-h`, see #253 (@starsep)
## Bugfixes
- Fix memory allocation bug when terminal width is less than 10, see #244 (@selfup)
# v0.16.0
## Features
* New `--print-color-table` option, see #229 (@sahinfalcon)
## Bugfixes
- Throw an error when try to view a directory, see #234 (@Integral-Tech)
# v0.15.0
## Features
- Add codepage 1047 for EBCDIC, see #226 (@v1gnesh)
## Other
- Rewrite CLI using the derive API, see #225 (@sorairolake)
## Packaging
# v0.14.0
## Features
* New `--character-table` option, with the ability to use [codepage 437](https://www.azabani.com/2020/11/15/xd.html), see #194 and #195 (@sharifhsn)
* New `--character-table=ascii` option for a ASCII-only character table, see #212 and #36 (@sharkdp)
## Bugfixes
* Show output when doing `hexyl /dev/zero`, see #211 (@sharifhsn)
* Respect NO_COLOR environment variable, see #210 (@sharkdp)
# v0.13.1
## Bugfixes
- Correctly handle discontinuous input (stdin), see #196 and #197 (@sharifhsn)
# v0.13.0
## Features
- Support both little and big Endian dumps using `--endianness={little,big}`, see #189 and #104 (@RinHizakura)
## Changes
- **Breaking**: Changed the meaning of the short flag `-C` to be consistent with `hexdump -C`. Previously, this would *hide* the character panel, but now `-C` *shows* the character panel, in case it has been previously (e.g. in an `alias`) disabled with `--no-characters`, see #187 (@sharkdp)
## `hexyl` as a library
- New `endianness` method for `PrinterBuilder`
# v0.12.0
## Features
- Only show one panel by default if the terminal width is not wide enough for two panels, see #182 (@sharkdp)
- Respect the `NO_COLOR` environment variable, see #179 (@sharifhsn)
## Bugfixes
- Do not fail with an error if `--panels=auto` is used and the output is piped, see #184 (@sharkdp)
## Changes
- Breaking: For `xxd`-compatibility reasons, `--group_bytes` has been renamed to `--group-size` (with an `--groupsize` alias), see #121 (@sharkdp)
## `hexyl` as a library
- Breaking: `num_group_bytes` has been renamed to `group_size`.
# v0.11.0
## Features
- Significantly improved performance, see #173 and #176 (@sharifhsn)
- Added variable panels through the `--panels` and `--terminal-width` flags, see [#13](https://github.com/sharkdp/hexyl/issues/13) and [#164](https://github.com/sharkdp/hexyl/pull/164) (@sharifhsn)
- Added new `--group-bytes`/`-g` option, see #104 and #170 (@RinHizakura)
- Added new `--base B` option (where `B` can be `binary`, `octal`, `decimal` or `hexadecimal`), see #147 and #178 (@sharifhsn)
- Show actual zero bytes as `⋄` in the character panel (previously: `0`), in order not to confuse them with ASCII
`0` bytes if colors are deactivated. Closes #166 (@sharkdp)
## `hexyl` as a library
- Breaking change: `Printer::new` is deprecated as a part of the public API. Alternatively, you can now construct a `Printer` using the `PrinterBuilder` builder API, see [#168](https://github.com/sharkdp/hexyl/pull/168). (@sharifhsn)
## Other
- More tests for the squeezing feature, see #177 (@mkatychev)
## Thank you
Special thanks go to @sharifhsn, not just for the new features,
bugfixes and performance improvements. But also for many internal
improvements of the code base and other maintenance tasks.
# v0.10.0
Generated
+275 -246
View File
@@ -4,33 +4,67 @@ version = 4
[[package]]
name = "aho-corasick"
version = "0.7.18"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f"
checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
dependencies = [
"memchr",
]
[[package]]
name = "ansi_term"
version = "0.12.1"
name = "anstream"
version = "0.6.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
checksum = "23a1e53f0f5d86382dafe1cf314783b2044280f406e7e1506368220ad11b1338"
dependencies = [
"winapi",
"anstyle",
"anstyle-parse",
"anstyle-query",
"anstyle-wincon",
"colorchoice",
"is_terminal_polyfill",
"utf8parse",
]
[[package]]
name = "anstyle"
version = "1.0.13"
version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78"
checksum = "8365de52b16c035ff4fcafe0092ba9390540e3e352870ac09933bebcaa2c8c56"
[[package]]
name = "anstyle-parse"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9"
dependencies = [
"utf8parse",
]
[[package]]
name = "anstyle-query"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c"
dependencies = [
"windows-sys 0.59.0",
]
[[package]]
name = "anstyle-wincon"
version = "3.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125"
dependencies = [
"anstyle",
"windows-sys 0.59.0",
]
[[package]]
name = "anyhow"
version = "1.0.57"
version = "1.0.91"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08f9b8508dccb7687a1d6c4ce66b2b0ecef467c94667de27d8d7fe1f8d2a9cdc"
checksum = "c042108f3ed77fd83760a5fd79b53be043192bb3b9dba91d8c574c0ada7850c8"
[[package]]
name = "assert_cmd"
@@ -41,40 +75,29 @@ dependencies = [
"anstyle",
"bstr",
"libc",
"predicates 3.1.4",
"predicates",
"predicates-core",
"predicates-tree",
"wait-timeout",
]
[[package]]
name = "atty"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
dependencies = [
"hermit-abi",
"libc",
"winapi",
]
[[package]]
name = "autocfg"
version = "1.1.0"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
[[package]]
name = "bitflags"
version = "1.3.2"
version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
[[package]]
name = "bstr"
version = "1.12.1"
version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "63044e1ae8e69f3b5a92c736ca6269b8d12fa7efe39bf34ddb06d102cf0e2cab"
checksum = "40723b8fb387abc38f4f4a37c09073622e41dd12327033091ef8950659e6dc0c"
dependencies = [
"memchr",
"regex-automata",
@@ -82,88 +105,101 @@ dependencies = [
]
[[package]]
name = "cc"
version = "1.0.73"
name = "clap"
version = "4.5.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11"
checksum = "b97f376d85a664d5837dbae44bf546e6477a679ff6610010f17276f686d867e8"
dependencies = [
"clap_builder",
"clap_derive",
]
[[package]]
name = "clap"
version = "3.2.11"
name = "clap_builder"
version = "4.5.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d646c7ade5eb07c4aa20e907a922750df0c448892513714fd3e4acbc7130829f"
checksum = "19bc80abd44e4bed93ca373a0704ccbd1b710dc5749406201bb018272808dc54"
dependencies = [
"atty",
"bitflags",
"anstream",
"anstyle",
"clap_lex",
"indexmap",
"once_cell",
"strsim",
"termcolor",
"terminal_size 0.1.17",
"textwrap",
"terminal_size",
]
[[package]]
name = "clap_complete"
version = "4.5.55"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a5abde44486daf70c5be8b8f8f1b66c49f86236edf6fa2abadb4d961c4c6229a"
dependencies = [
"clap",
]
[[package]]
name = "clap_derive"
version = "4.5.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab"
dependencies = [
"heck",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "clap_lex"
version = "0.2.4"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5"
dependencies = [
"os_str_bytes",
]
checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97"
[[package]]
name = "colorchoice"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990"
[[package]]
name = "const_format"
version = "0.2.23"
version = "0.2.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0936ffe6d0c8d6a51b3b0a73b2acbe925d786f346cf45bfddc8341d79fb7dc8a"
checksum = "50c655d81ff1114fb0dcdea9225ea9f0cc712a6f8d189378e82bdf62a473a64b"
dependencies = [
"const_format_proc_macros",
]
[[package]]
name = "const_format_proc_macros"
version = "0.2.22"
version = "0.2.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef196d5d972878a48da7decb7686eded338b4858fbabeed513d63a7c98b2b82d"
checksum = "eff1a44b93f47b1bac19a27932f5c591e43d1ba357ee4f61526c8a25603f0eb1"
dependencies = [
"proc-macro2",
"quote",
"unicode-xid",
]
[[package]]
name = "diff"
version = "0.1.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8"
[[package]]
name = "difflib"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8"
[[package]]
name = "either"
version = "1.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
[[package]]
name = "errno"
version = "0.2.8"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1"
checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba"
dependencies = [
"errno-dragonfly",
"libc",
"winapi",
]
[[package]]
name = "errno-dragonfly"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf"
dependencies = [
"cc",
"libc",
"windows-sys 0.52.0",
]
[[package]]
@@ -176,78 +212,58 @@ dependencies = [
]
[[package]]
name = "hashbrown"
version = "0.11.2"
name = "heck"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
[[package]]
name = "hermit-abi"
version = "0.1.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
dependencies = [
"libc",
]
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
[[package]]
name = "hexyl"
version = "0.10.0"
version = "0.17.0"
dependencies = [
"ansi_term",
"anyhow",
"assert_cmd",
"atty",
"clap",
"clap_complete",
"const_format",
"libc",
"predicates 2.1.1",
"terminal_size 0.2.1",
"owo-colors",
"predicates",
"pretty_assertions",
"supports-color",
"terminal_size",
"thiserror",
]
[[package]]
name = "indexmap"
version = "1.8.1"
name = "is_ci"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0f647032dfaa1f8b6dc29bd3edb7bbef4861b8b8007ebb118d6db284fd59f6ee"
dependencies = [
"autocfg",
"hashbrown",
]
checksum = "7655c9839580ee829dfacba1d1278c2b7883e50a277ff7541299489d6bdfdc45"
[[package]]
name = "io-lifetimes"
version = "0.7.2"
name = "is_terminal_polyfill"
version = "1.70.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24c3f4eff5495aee4c0399d7b6a0dc2b6e81be84242ffbfcf253ebacccc1d0cb"
[[package]]
name = "itertools"
version = "0.10.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3"
dependencies = [
"either",
]
checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
[[package]]
name = "libc"
version = "0.2.182"
version = "0.2.161"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112"
checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1"
[[package]]
name = "linux-raw-sys"
version = "0.0.46"
version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4d2456c373231a208ad294c33dc5bff30051eafd954cd4caae83a712b12854d"
checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
[[package]]
name = "memchr"
version = "2.8.0"
version = "2.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
[[package]]
name = "normalize-line-endings"
@@ -257,202 +273,195 @@ checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be"
[[package]]
name = "num-traits"
version = "0.2.15"
version = "0.2.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
dependencies = [
"autocfg",
]
[[package]]
name = "once_cell"
version = "1.13.0"
name = "owo-colors"
version = "4.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "18a6dbe30758c9f83eb00cbea4ac95966305f5a7772f3f42ebfc7fc7eddbd8e1"
[[package]]
name = "os_str_bytes"
version = "6.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "029d8d0b2f198229de29dca79676f2738ff952edf3fde542eb8bf94d8c21b435"
checksum = "fb37767f6569cd834a413442455e0f066d0d522de8630436e2a1761d9726ba56"
[[package]]
name = "predicates"
version = "2.1.1"
version = "3.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a5aab5be6e4732b473071984b3164dbbfb7a3674d30ea5ff44410b6bcd960c3c"
checksum = "7e9086cc7640c29a356d1a29fd134380bee9d8f79a17410aa76e7ad295f42c97"
dependencies = [
"anstyle",
"difflib",
"float-cmp",
"itertools",
"normalize-line-endings",
"predicates-core",
"regex",
]
[[package]]
name = "predicates"
version = "3.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ada8f2932f28a27ee7b70dd6c1c39ea0675c55a36879ab92f3a715eaa1e63cfe"
dependencies = [
"anstyle",
"difflib",
"predicates-core",
]
[[package]]
name = "predicates-core"
version = "1.0.10"
version = "1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cad38746f3166b4031b1a0d39ad9f954dd291e7854fcc0eed52ee41a0b50d144"
checksum = "ae8177bee8e75d6846599c6b9ff679ed51e882816914eec639944d7c9aa11931"
[[package]]
name = "predicates-tree"
version = "1.0.5"
version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4d86de6de25020a36c6d3643a86d9a6a9f552107c0559c60ea03551b5e16c032"
checksum = "41b740d195ed3166cd147c8047ec98db0e22ec019eb8eeb76d343b795304fb13"
dependencies = [
"predicates-core",
"termtree",
]
[[package]]
name = "proc-macro2"
version = "1.0.39"
name = "pretty_assertions"
version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c54b25569025b7fc9651de43004ae593a75ad88543b17178aa5e1b9c4f15f56f"
checksum = "3ae130e2f271fbc2ac3a40fb1d07180839cdbbe443c7a27e1e3c13c5cac0116d"
dependencies = [
"diff",
"yansi",
]
[[package]]
name = "proc-macro2"
version = "1.0.89"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.18"
version = "1.0.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1"
checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
dependencies = [
"proc-macro2",
]
[[package]]
name = "regex"
version = "1.5.6"
version = "1.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d83f127d94bdbcda4c8cc2e50f6f84f4b611f69c902699ca385a39c3a75f9ff1"
checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
dependencies = [
"aho-corasick",
"memchr",
"regex-automata",
"regex-syntax",
]
[[package]]
name = "regex-automata"
version = "0.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
]
[[package]]
name = "regex-automata"
version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f"
[[package]]
name = "regex-syntax"
version = "0.6.26"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49b3de9ec5dc0a3417da371aab17d729997c15010e7fd24ff707773a33bddb64"
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
[[package]]
name = "rustix"
version = "0.35.7"
version = "0.38.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d51cc38aa10f6bbb377ed28197aa052aa4e2b762c22be9d3153d01822587e787"
checksum = "aa260229e6538e52293eeb577aabd09945a09d6d9cc0fc550ed7529056c2e32a"
dependencies = [
"bitflags",
"errno",
"io-lifetimes",
"libc",
"linux-raw-sys",
"windows-sys",
"windows-sys 0.52.0",
]
[[package]]
name = "serde"
version = "1.0.185"
version = "1.0.214"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be9b6f69f1dfd54c3b568ffa45c310d6973a5e5148fd40cf515acaf38cf5bc31"
checksum = "f55c3193aca71c12ad7890f1785d2b73e1b9f63a0bbc353c08ef26fe03fc56b5"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.214"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "de523f781f095e28fa605cdce0f8307e451cc0fd14e2eb4cd2e98a355b147766"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "strsim"
version = "0.10.0"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
[[package]]
name = "supports-color"
version = "3.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8775305acf21c96926c900ad056abeef436701108518cf890020387236ac5a77"
dependencies = [
"is_ci",
]
[[package]]
name = "syn"
version = "1.0.95"
version = "2.0.85"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fbaf6116ab8924f39d52792136fb74fd60a80194cf1b1c6ffa6453eef1c3f942"
checksum = "5023162dfcd14ef8f32034d8bcd4cc5ddc61ef7a247c024a33e24e1f24d21b56"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "termcolor"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755"
dependencies = [
"winapi-util",
]
[[package]]
name = "terminal_size"
version = "0.1.17"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "633c1a546cee861a1a6d0dc69ebeca693bf4296661ba7852b9d21d159e0506df"
dependencies = [
"libc",
"winapi",
]
[[package]]
name = "terminal_size"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8440c860cf79def6164e4a0a983bcc2305d82419177a0e0c71930d049e3ac5a1"
checksum = "4f599bd7ca042cfdf8f4512b277c02ba102247820f9d9d4a9f521f496751a6ef"
dependencies = [
"rustix",
"windows-sys",
"windows-sys 0.59.0",
]
[[package]]
name = "termtree"
version = "0.2.4"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "507e9898683b6c43a9aa55b64259b721b52ba226e0f3779137e50ad114a4c90b"
[[package]]
name = "textwrap"
version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb"
dependencies = [
"terminal_size 0.1.17",
]
checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76"
[[package]]
name = "thiserror"
version = "1.0.31"
version = "1.0.65"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a"
checksum = "5d11abd9594d9b38965ef50805c5e469ca9cc6f197f883f717e0269a3057b3d5"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.31"
version = "1.0.65"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a"
checksum = "ae71770322cbd277e69d762a16c444af02aa0575ac0d174f0b9562d3b37f8602"
dependencies = [
"proc-macro2",
"quote",
@@ -461,15 +470,21 @@ dependencies = [
[[package]]
name = "unicode-ident"
version = "1.0.0"
version = "1.0.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d22af068fba1eb5edcb4aea19d382b2a3deb4c8f9d475c589b6ada9e0fd493ee"
checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe"
[[package]]
name = "unicode-xid"
version = "0.2.3"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "957e51f3646910546462e67d5f7599b9e4fb8acdd304b087a6494730f9eebf04"
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
[[package]]
name = "utf8parse"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
[[package]]
name = "wait-timeout"
@@ -481,75 +496,89 @@ dependencies = [
]
[[package]]
name = "winapi"
version = "0.3.9"
name = "windows-sys"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
"windows-targets",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-util"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
dependencies = [
"winapi",
]
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-sys"
version = "0.36.1"
version = "0.59.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2"
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-targets"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_gnullvm",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_msvc"
version = "0.36.1"
name = "windows_aarch64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47"
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
[[package]]
name = "windows_i686_gnu"
version = "0.36.1"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6"
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
[[package]]
name = "windows_i686_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
[[package]]
name = "windows_i686_msvc"
version = "0.36.1"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
[[package]]
name = "windows_x86_64_gnu"
version = "0.36.1"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1"
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
[[package]]
name = "windows_x86_64_msvc"
version = "0.36.1"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[[package]]
name = "yansi"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049"
+11 -8
View File
@@ -1,32 +1,35 @@
[package]
authors = ["David Peter <mail@david-peter.de>"]
categories = ["command-line-utilities"]
keywords = ["hex", "viewer"]
description = "A command-line hex viewer"
homepage = "https://github.com/sharkdp/hexyl"
license = "MIT/Apache-2.0"
name = "hexyl"
readme = "README.md"
repository = "https://github.com/sharkdp/hexyl"
version = "0.10.0"
version = "0.17.0"
edition = "2021"
rust-version = "1.88"
[dependencies]
ansi_term = "0.12"
anyhow = "1.0"
atty = "0.2"
const_format = "0.2"
libc = "0.2"
owo-colors = "4"
supports-color = "3"
thiserror = "1.0"
terminal_size = "0.2"
terminal_size = "0.4"
clap_complete = "4"
[dependencies.clap]
version = "3"
default-features = false
features = ["std", "suggestions", "color", "wrap_help", "cargo"]
version = "4"
features = ["derive", "wrap_help"]
[dev-dependencies]
assert_cmd = "2.1"
predicates = "2.0"
predicates = "3.0"
pretty_assertions = "1.4.0"
[profile.release]
lto = true
+36 -3
View File
@@ -4,7 +4,7 @@
[![](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
`hexyl` is a 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
@@ -17,6 +17,7 @@ of bytes (NULL bytes, printable ASCII characters, ASCII whitespace characters, o
![](https://i.imgur.com/f8nm8g6.png)
## Installation
### On Ubuntu
@@ -31,7 +32,7 @@ If you use an older version of Ubuntu, you can download
the latest `.deb` package from the release page and install it via:
``` bash
sudo dpkg -i hexyl_0.10.0_amd64.deb # adapt version number and architecture
sudo dpkg -i hexyl_0.15.0_amd64.deb # adapt version number and architecture
```
### On Debian
@@ -54,7 +55,7 @@ sudo dnf install hexyl
### On Arch Linux
You can install `hexyl` from [the official package repository](https://www.archlinux.org/packages/community/x86_64/hexyl/):
You can install `hexyl` from [the official package repository](https://archlinux.org/packages/extra/x86_64/hexyl/):
```
pacman -S hexyl
@@ -96,6 +97,18 @@ sudo port install hexyl
pkg install hexyl
```
### On NetBSD
```
pkgin install hexyl
```
### On OpenBSD
```
doas pkg_add hexyl
```
### on Termux
```
pkg install hexyl
@@ -163,6 +176,26 @@ sudo snap install hexyl
scoop install hexyl
```
### Via [X-CMD](https://x-cmd.com)
```
x env use hexyl
```
## Configuration
`hexyl` colors can be configured via environment variables. The variables used are as follows:
* `HEXYL_COLOR_ASCII_PRINTABLE`: Any non-whitespace printable ASCII character
* `HEXYL_COLOR_ASCII_WHITESPACE`: Whitespace such as space or newline (only visible in middle panel with byte values)
* `HEXYL_COLOR_ASCII_OTHER`: Any other ASCII character (< `0x80`) besides null
* `HEXYL_COLOR_NULL`: The null byte (`0x00`)
* `HEXYL_COLOR_NONASCII`: Any non-ASCII byte (> `0x7F`)
* `HEXYL_COLOR_OFFSET`: The lefthand file offset
The colors can be any of the 8 standard terminal colors: `black`, `blue`, `cyan`, `green`, `magenta`, `red`,
`yellow` and `white`. The "bright" variants are also supported (e.g., `bright blue`). Additionally, you can use
the RGB hex format, `#abcdef`. For example, `HEXYL_COLOR_ASCII_PRINTABLE=blue HEXYL_COLOR_ASCII_WHITESPACE="bright green"
HEXYL_COLOR_ASCII_OTHER="#ff7f99"`.
## License
+18 -7
View File
@@ -1,11 +1,6 @@
% HEXYL(1) hexyl 0.10.0 | General Commands Manual
% HEXYL(1) hexyl 0.12.0 | General Commands Manual
%
% 2022-03-23
<!--
This man page is written in Pandoc's Markdown.
See: https://pandoc.org/MANUAL.html#pandocs-markdown
-->
% 2022-12-05
# NAME
@@ -111,6 +106,22 @@ _FILE_
**-V**, **\--version**
: Prints version information.
# ENVIRONMENT VARIABLES
**hexyl** colors can be configured via environment variables. The variables used are as follows:
: - **HEXYL_COLOR_ASCII_PRINTABLE**: Any non-whitespace printable ASCII character
- **HEXYL_COLOR_ASCII_WHITESPACE**: Whitespace such as space or newline (only visible in middle panel with byte values)
- **HEXYL_COLOR_ASCII_OTHER**: Any other ASCII character (< **0x80**) besides null
- **HEXYL_COLOR_NULL**: The null byte (**0x00**)
- **HEXYL_COLOR_NONASCII**: Any non-ASCII byte (> **0x7F**)
- **HEXYL_COLOR_OFFSET**: The lefthand file offset
The colors can be any of the 8 standard terminal colors: **black**, **blue**, **cyan**, **green**, **magenta**, **red**,
**yellow** and **white**. The "bright" variants are also supported (e.g., **bright blue**). Additionally, you can use
the RGB hex format, **#abcdef**. For example, **HEXYL_COLOR_ASCII_PRINTABLE=blue HEXYL_COLOR_ASCII_WHITESPACE="bright green"
HEXYL_COLOR_ASCII_OTHER="#ff7f99"**.
# NOTES
Source repository:
+11 -18
View File
@@ -1,9 +1,9 @@
use std::io;
use hexyl::{BorderStyle, Printer};
use hexyl::{BorderStyle, PrinterBuilder};
fn main() {
let input = vec![
let input = [
0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44,
0x52, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x44, 0x08, 0x02, 0x00, 0x00, 0x00,
];
@@ -11,21 +11,14 @@ fn main() {
let stdout = io::stdout();
let mut handle = stdout.lock();
let show_color = true;
let show_char_panel = true;
let show_position_panel = true;
let use_squeezing = false;
let border_style = BorderStyle::Unicode;
let columns = 2;
let mut printer = Printer::new(
&mut handle,
show_color,
show_char_panel,
show_position_panel,
border_style,
use_squeezing,
columns,
);
let mut printer = PrinterBuilder::new(&mut handle)
.show_color(true)
.show_char_panel(true)
.show_position_panel(true)
.with_border_style(BorderStyle::Unicode)
.enable_squeezing(false)
.num_panels(2)
.group_size(1)
.build();
printer.print_all(&input[..]).unwrap();
}
-800
View File
@@ -1,800 +0,0 @@
#[macro_use]
extern crate clap;
use std::convert::TryFrom;
use std::fs::File;
use std::io::{self, prelude::*, SeekFrom};
use std::num::{NonZeroI64, NonZeroU16};
use clap::{crate_name, crate_version, AppSettings, Arg, ColorChoice, Command};
use atty::Stream;
use anyhow::{anyhow, Context, Error as AnyhowError};
use const_format::formatcp;
use thiserror::Error as ThisError;
use terminal_size::terminal_size;
use hexyl::{BorderStyle, Input, Printer};
const DEFAULT_BLOCK_SIZE: i64 = 512;
fn run() -> Result<(), AnyhowError> {
let command = Command::new(crate_name!())
.setting(AppSettings::DeriveDisplayOrder)
.color(ColorChoice::Auto)
.max_term_width(90)
.version(crate_version!())
.about(crate_description!())
.arg(
Arg::new("FILE")
.help("The file to display. If no FILE argument is given, read from STDIN."),
)
.arg(
Arg::new("length")
.short('n')
.long("length")
.takes_value(true)
.value_name("N")
.help(
"Only read N bytes from the input. The N argument can also include a \
unit with a decimal prefix (kB, MB, ..) or binary prefix (kiB, MiB, ..), \
or can be specified using a hex number. \
The short option '-l' can be used as an alias.\n\
Examples: --length=64, --length=4KiB, --length=0xff",
),
)
.arg(
Arg::new("bytes")
.short('c')
.long("bytes")
.takes_value(true)
.value_name("N")
.conflicts_with("length")
.help("An alias for -n/--length"),
)
.arg(
Arg::new("count")
.short('l')
.takes_value(true)
.value_name("N")
.conflicts_with_all(&["length", "bytes"])
.hide(true)
.help("Yet another alias for -n/--length"),
)
.arg(
Arg::new("skip")
.short('s')
.long("skip")
.takes_value(true)
.value_name("N")
.help(
"Skip the first N bytes of the input. The N argument can also include \
a unit (see `--length` for details)\n\
A negative value is valid and will seek from the end of the file.",
),
)
.arg(
Arg::new("block_size")
.long("block-size")
.takes_value(true)
.value_name("SIZE")
.help(formatcp!(
"Sets the size of the `block` unit to SIZE (default is {}).\n\
Examples: --block-size=1024, --block-size=4kB",
DEFAULT_BLOCK_SIZE
)),
)
.arg(
Arg::new("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::new("color")
.long("color")
.takes_value(true)
.value_name("WHEN")
.possible_values(&["always", "auto", "never"])
.default_value_if("plain", None, Some("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::new("plain").short('p').long("plain").help(
"Display output with --no-characters, --no-position, --border=none, and --color=never.",
))
.arg(
Arg::new("border")
.long("border")
.takes_value(true)
.value_name("STYLE")
.possible_values(&["unicode", "ascii", "none"])
.default_value_if("plain", None, Some("none"))
.default_value("unicode")
.help(
"Whether to draw a border with Unicode characters, ASCII characters, \
or none at all",
),
)
.arg(
Arg::new("no_chars")
.short('C')
.long("no-characters")
.help("Whether to display the character panel on the right."),
)
.arg(
Arg::new("no_position")
.short('P')
.long("no-position")
.help("Whether to display the position panel on the left."),
)
.arg(
Arg::new("display_offset")
.short('o')
.long("display-offset")
.takes_value(true)
.value_name("N")
.help(
"Add N bytes to the displayed file position. The N argument can also \
include a unit (see `--length` for details)\n\
A negative value is valid and calculates an offset relative to the \
end of the file.",
),
)
.arg(
Arg::new("columns")
.short('w')
.long("columns")
.takes_value(true)
.value_name("N")
.help(
"Sets the number of hex data columns to be displayed. \
Cannot be used with other width-setting options.",
),
)
.arg(
Arg::new("terminal_width")
.short('t')
.long("terminal-width")
.takes_value(true)
.value_name("N")
.conflicts_with("columns")
.help(
"Sets the number of terminal columns to be displayed.\nSince the terminal \
width may not be an evenly divisible by the width per hex data column, this \
will use the greatest number of hex data columns that can fit in the requested \
width but still leave some space to the right.\nCannot be used with other \
width-setting options.",
),
)
.arg(
Arg::new("auto_width")
.short('a')
.long("auto-width")
.conflicts_with_all(&["columns", "terminal_width"])
.help(
"Sets the number of hex data columns to be adjusted according to the \
detected terminal width.\nCannot be used with other width-setting options.",
),
);
let matches = command.get_matches();
let stdin = io::stdin();
let mut reader = match matches.value_of("FILE") {
Some(filename) => Input::File(File::open(filename)?),
None => Input::Stdin(stdin.lock()),
};
let block_size = matches
.value_of("block_size")
.map(|bs| {
if let Some(hex_number) = try_parse_as_hex_number(bs) {
return hex_number.map_err(|e| anyhow!(e)).and_then(|x| {
PositiveI64::new(x)
.ok_or_else(|| anyhow!("block size argument must be positive"))
});
}
let (num, unit) = extract_num_and_unit_from(bs)?;
if let Unit::Block { custom_size: _ } = unit {
return Err(anyhow!(
"can not use 'block(s)' as a unit to specify block size"
));
};
num.checked_mul(unit.get_multiplier())
.ok_or_else(|| anyhow!(ByteOffsetParseError::UnitMultiplicationOverflow))
.and_then(|x| {
PositiveI64::new(x)
.ok_or_else(|| anyhow!("block size argument must be positive"))
})
})
.transpose()?
.unwrap_or_else(|| PositiveI64::new(DEFAULT_BLOCK_SIZE).unwrap());
let skip_arg = matches
.value_of("skip")
.map(|s| {
parse_byte_offset(s, block_size).context(anyhow!(
"failed to parse `--skip` arg {:?} as byte count",
s
))
})
.transpose()?;
let skip_offset = if let Some(ByteOffset { kind, value }) = skip_arg {
let value = value.into_inner();
reader
.seek(match kind {
ByteOffsetKind::ForwardFromBeginning | ByteOffsetKind::ForwardFromLastOffset => {
SeekFrom::Current(value)
}
ByteOffsetKind::BackwardFromEnd => SeekFrom::End(value.checked_neg().unwrap()),
})
.map_err(|_| {
anyhow!(
"Failed to jump to the desired input position. \
This could be caused by a negative offset that is too large or by \
an input that is not seek-able (e.g. if the input comes from a pipe)."
)
})?
} else {
0
};
let parse_byte_count = |s| -> Result<u64, AnyhowError> {
Ok(parse_byte_offset(s, block_size)?
.assume_forward_offset_from_start()?
.into())
};
let mut reader = if let Some(length) = matches
.value_of("length")
.or_else(|| matches.value_of("bytes"))
.or_else(|| matches.value_of("count"))
.map(|s| {
parse_byte_count(s).context(anyhow!(
"failed to parse `--length` arg {:?} as byte count",
s
))
})
.transpose()?
{
Box::new(reader.take(length))
} else {
reader.into_inner()
};
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 show_char_panel = !matches.is_present("no_chars") && !matches.is_present("plain");
let show_position_panel = !matches.is_present("no_position") && !matches.is_present("plain");
let display_offset: u64 = matches
.value_of("display_offset")
.map(|s| {
parse_byte_count(s).context(anyhow!(
"failed to parse `--display-offset` arg {:?} as byte count",
s
))
})
.transpose()?
.unwrap_or(0);
let columns = if let Some(columns) = matches
.value_of("columns")
.map(|s| {
s.parse::<NonZeroU16>().map(u16::from).context(anyhow!(
"failed to parse `--columns` arg {:?} as unsigned nonzero integer",
s
))
})
.transpose()?
{
columns
} else {
let terminal_width = if let Some(terminal_width) = matches
.value_of("terminal_width")
.map(|s| {
s.parse::<NonZeroU16>().map(u16::from).context(anyhow!(
"failed to parse `--columns` arg {:?} as unsigned nonzero integer",
s
))
})
.transpose()?
{
Some(terminal_width)
} else if matches.is_present("auto_width") {
Some(terminal_size().ok_or_else(|| anyhow!("not a TTY"))?.0 .0)
} else {
None
};
if let Some(terminal_width) = terminal_width {
let offset = if show_position_panel { 10 } else { 1 };
let col_width = if show_char_panel { 35 } else { 26 };
if (terminal_width - offset) / col_width < 1 {
1
} else {
(terminal_width - offset) / col_width
}
} else {
2
}
};
let stdout = io::stdout();
let mut stdout_lock = stdout.lock();
let mut printer = Printer::new(
&mut stdout_lock,
show_color,
show_char_panel,
show_position_panel,
border_style,
squeeze,
columns,
);
printer.display_offset(skip_offset + display_offset);
printer.print_all(&mut reader).map_err(|e| anyhow!(e))?;
Ok(())
}
fn main() {
// Enable ANSI support for Windows
#[cfg(windows)]
let _ = ansi_term::enable_ansi_support();
let result = run();
if let Err(err) = result {
eprintln!("Error: {:?}", err);
std::process::exit(1);
}
}
#[derive(Clone, Copy, Debug, Default, Hash, Eq, Ord, PartialEq, PartialOrd)]
pub struct NonNegativeI64(i64);
impl NonNegativeI64 {
pub fn new(x: i64) -> Option<Self> {
if x.is_negative() {
None
} else {
Some(Self(x))
}
}
pub fn into_inner(self) -> i64 {
self.0
}
}
impl From<NonNegativeI64> for u64 {
fn from(x: NonNegativeI64) -> u64 {
u64::try_from(x.0)
.expect("invariant broken: NonNegativeI64 should contain a non-negative i64 value")
}
}
#[derive(Clone, Copy, Debug, Default, Hash, Eq, Ord, PartialEq, PartialOrd)]
pub struct PositiveI64(i64);
impl PositiveI64 {
pub fn new(x: i64) -> Option<Self> {
if x < 1 {
None
} else {
Some(Self(x))
}
}
pub fn into_inner(self) -> i64 {
self.0
}
}
impl From<PositiveI64> for u64 {
fn from(x: PositiveI64) -> u64 {
u64::try_from(x.0)
.expect("invariant broken: PositiveI64 should contain a positive i64 value")
}
}
#[derive(Debug, PartialEq)]
enum Unit {
Byte,
Kilobyte,
Megabyte,
Gigabyte,
Terabyte,
Kibibyte,
Mebibyte,
Gibibyte,
Tebibyte,
/// a customizable amount of bytes
Block {
custom_size: Option<NonZeroI64>,
},
}
impl Unit {
const fn get_multiplier(self) -> i64 {
match self {
Self::Byte => 1,
Self::Kilobyte => 1000,
Self::Megabyte => 1_000_000,
Self::Gigabyte => 1_000_000_000,
Self::Terabyte => 1_000_000_000_000,
Self::Kibibyte => 1 << 10,
Self::Mebibyte => 1 << 20,
Self::Gibibyte => 1 << 30,
Self::Tebibyte => 1 << 40,
Self::Block {
custom_size: Some(size),
} => size.get(),
Self::Block { custom_size: None } => DEFAULT_BLOCK_SIZE,
}
}
}
const HEX_PREFIX: &str = "0x";
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
enum ByteOffsetKind {
ForwardFromBeginning,
ForwardFromLastOffset,
BackwardFromEnd,
}
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
struct ByteOffset {
value: NonNegativeI64,
kind: ByteOffsetKind,
}
#[derive(Clone, Debug, ThisError)]
#[error(
"negative offset specified, but only positive offsets (counts) are accepted in this context"
)]
struct NegativeOffsetSpecifiedError;
impl ByteOffset {
fn assume_forward_offset_from_start(
&self,
) -> Result<NonNegativeI64, NegativeOffsetSpecifiedError> {
let &Self { value, kind } = self;
match kind {
ByteOffsetKind::ForwardFromBeginning | ByteOffsetKind::ForwardFromLastOffset => {
Ok(value)
}
ByteOffsetKind::BackwardFromEnd => Err(NegativeOffsetSpecifiedError),
}
}
}
#[derive(Clone, Debug, Eq, PartialEq, ThisError)]
enum ByteOffsetParseError {
#[error("no character data found, did you forget to write it?")]
Empty,
#[error("no digits found after sign, did you forget to write them?")]
EmptyAfterSign,
#[error(
"found {0:?} sign after hex prefix ({:?}); signs should go before it",
HEX_PREFIX
)]
SignFoundAfterHexPrefix(char),
#[error("{0:?} is not of the expected form <pos-integer>[<unit>]")]
InvalidNumAndUnit(String),
#[error("{0:?} is a valid unit, but an integer should come before it")]
EmptyWithUnit(String),
#[error("invalid unit {0:?}")]
InvalidUnit(String),
#[error("failed to parse integer part")]
ParseNum(#[source] std::num::ParseIntError),
#[error("count multiplied by the unit overflowed a signed 64-bit integer; are you sure it should be that big?")]
UnitMultiplicationOverflow,
}
fn parse_byte_offset(n: &str, block_size: PositiveI64) -> Result<ByteOffset, ByteOffsetParseError> {
use ByteOffsetParseError::*;
let (n, kind) = process_sign_of(n)?;
let into_byte_offset = |value| {
Ok(ByteOffset {
value: NonNegativeI64::new(value).unwrap(),
kind,
})
};
if let Some(hex_number) = try_parse_as_hex_number(n) {
return hex_number.map(into_byte_offset)?;
}
let (num, mut unit) = extract_num_and_unit_from(n)?;
if let Unit::Block { custom_size: None } = unit {
unit = Unit::Block {
custom_size: Some(
NonZeroI64::new(block_size.into_inner()).expect("PositiveI64 was zero"),
),
};
}
num.checked_mul(unit.get_multiplier())
.ok_or(UnitMultiplicationOverflow)
.and_then(into_byte_offset)
}
/// Takes a string containing a base-10 number and an optional unit, and returns them with their proper types.
/// The unit must directly follow the number (e.g. no whitespace is allowed between them).
/// When no unit is given, [Unit::Byte] is assumed.
/// When the unit is [Unit::Block], it is returned without custom size.
/// No normalization is performed, that is "1024" is extracted to (1024, Byte), not (1, Kibibyte).
fn extract_num_and_unit_from(n: &str) -> Result<(i64, Unit), ByteOffsetParseError> {
use ByteOffsetParseError::*;
if n.is_empty() {
return Err(Empty);
}
match n.chars().position(|c| !c.is_ascii_digit()) {
Some(unit_begin_idx) => {
let (n, raw_unit) = n.split_at(unit_begin_idx);
let unit = match raw_unit.to_lowercase().as_str() {
"" => Unit::Byte, // no "b" => Byte to allow hex nums with units
"kb" => Unit::Kilobyte,
"mb" => Unit::Megabyte,
"gb" => Unit::Gigabyte,
"tb" => Unit::Terabyte,
"kib" => Unit::Kibibyte,
"mib" => Unit::Mebibyte,
"gib" => Unit::Gibibyte,
"tib" => Unit::Tebibyte,
"block" | "blocks" => Unit::Block { custom_size: None },
_ => {
return if n.is_empty() {
Err(InvalidNumAndUnit(raw_unit.to_string()))
} else {
Err(InvalidUnit(raw_unit.to_string()))
}
}
};
let num = n.parse::<i64>().map_err(|e| {
if n.is_empty() {
EmptyWithUnit(raw_unit.to_owned())
} else {
ParseNum(e)
}
})?;
Ok((num, unit))
}
None => {
// no unit part
let num = n.parse::<i64>().map_err(ParseNum)?;
Ok((num, Unit::Byte))
}
}
}
/// Extracts a [ByteOffsetKind] based on the sign at the beginning of the given string.
/// Returns the input string without the sign (or an equal string if there wasn't any sign).
fn process_sign_of(n: &str) -> Result<(&str, ByteOffsetKind), ByteOffsetParseError> {
use ByteOffsetParseError::*;
let mut chars = n.chars();
let next_char = chars.next();
let check_empty_after_sign = || {
if chars.clone().next().is_none() {
Err(EmptyAfterSign)
} else {
Ok(chars.as_str())
}
};
match next_char {
Some('+') => Ok((
check_empty_after_sign()?,
ByteOffsetKind::ForwardFromLastOffset,
)),
Some('-') => Ok((check_empty_after_sign()?, ByteOffsetKind::BackwardFromEnd)),
None => Err(Empty),
_ => Ok((n, ByteOffsetKind::ForwardFromBeginning)),
}
}
/// If `n` starts with a hex prefix, its remaining part is returned as some number (if possible),
/// otherwise None is returned.
fn try_parse_as_hex_number(n: &str) -> Option<Result<i64, ByteOffsetParseError>> {
use ByteOffsetParseError::*;
n.strip_prefix(HEX_PREFIX).map(|num| {
let mut chars = num.chars();
match chars.next() {
Some(c @ '+') | Some(c @ '-') => {
return if chars.next().is_none() {
Err(EmptyAfterSign)
} else {
Err(SignFoundAfterHexPrefix(c))
}
}
_ => (),
}
i64::from_str_radix(num, 16).map_err(ParseNum)
})
}
#[test]
fn unit_multipliers() {
use Unit::*;
assert_eq!(Kilobyte.get_multiplier(), 1000 * Byte.get_multiplier());
assert_eq!(Megabyte.get_multiplier(), 1000 * Kilobyte.get_multiplier());
assert_eq!(Gigabyte.get_multiplier(), 1000 * Megabyte.get_multiplier());
assert_eq!(Terabyte.get_multiplier(), 1000 * Gigabyte.get_multiplier());
assert_eq!(Kibibyte.get_multiplier(), 1024 * Byte.get_multiplier());
assert_eq!(Mebibyte.get_multiplier(), 1024 * Kibibyte.get_multiplier());
assert_eq!(Gibibyte.get_multiplier(), 1024 * Mebibyte.get_multiplier());
assert_eq!(Tebibyte.get_multiplier(), 1024 * Gibibyte.get_multiplier());
}
#[test]
fn test_process_sign() {
use ByteOffsetKind::*;
use ByteOffsetParseError::*;
assert_eq!(process_sign_of("123"), Ok(("123", ForwardFromBeginning)));
assert_eq!(process_sign_of("+123"), Ok(("123", ForwardFromLastOffset)));
assert_eq!(process_sign_of("-123"), Ok(("123", BackwardFromEnd)));
assert_eq!(process_sign_of("-"), Err(EmptyAfterSign));
assert_eq!(process_sign_of("+"), Err(EmptyAfterSign));
assert_eq!(process_sign_of(""), Err(Empty));
}
#[test]
fn test_parse_as_hex() {
assert_eq!(try_parse_as_hex_number("73"), None);
assert_eq!(try_parse_as_hex_number("0x1337"), Some(Ok(0x1337)));
assert!(matches!(try_parse_as_hex_number("0xnope"), Some(Err(_))));
assert!(matches!(try_parse_as_hex_number("0x-1"), Some(Err(_))));
}
#[test]
fn extract_num_and_unit() {
use ByteOffsetParseError::*;
use Unit::*;
// byte is default unit
assert_eq!(extract_num_and_unit_from("4"), Ok((4, Byte)));
// blocks are returned without customization
assert_eq!(
extract_num_and_unit_from("2blocks"),
Ok((2, Block { custom_size: None }))
);
// no normalization is performed
assert_eq!(extract_num_and_unit_from("1024kb"), Ok((1024, Kilobyte)));
// unit without number results in error
assert_eq!(
extract_num_and_unit_from("gib"),
Err(EmptyWithUnit("gib".to_string()))
);
// empty string results in error
assert_eq!(extract_num_and_unit_from(""), Err(Empty));
// an invalid unit results in an error
assert_eq!(
extract_num_and_unit_from("25litres"),
Err(InvalidUnit("litres".to_string()))
);
}
#[test]
fn test_parse_byte_offset() {
use ByteOffsetParseError::*;
macro_rules! success {
($input: expr, $expected_kind: ident $expected_value: expr) => {
success!($input, $expected_kind $expected_value; block_size: DEFAULT_BLOCK_SIZE)
};
($input: expr, $expected_kind: ident $expected_value: expr; block_size: $block_size: expr) => {
assert_eq!(
parse_byte_offset($input, PositiveI64::new($block_size).unwrap()),
Ok(
ByteOffset {
value: NonNegativeI64::new($expected_value).unwrap(),
kind: ByteOffsetKind::$expected_kind,
}
),
);
};
}
macro_rules! error {
($input: expr, $expected_err: expr) => {
assert_eq!(
parse_byte_offset($input, PositiveI64::new(DEFAULT_BLOCK_SIZE).unwrap()),
Err($expected_err),
);
};
}
success!("0", ForwardFromBeginning 0);
success!("1", ForwardFromBeginning 1);
success!("1", ForwardFromBeginning 1);
success!("100", ForwardFromBeginning 100);
success!("+100", ForwardFromLastOffset 100);
success!("0x0", ForwardFromBeginning 0);
success!("0xf", ForwardFromBeginning 15);
success!("0xdeadbeef", ForwardFromBeginning 3_735_928_559);
success!("1KB", ForwardFromBeginning 1000);
success!("2MB", ForwardFromBeginning 2000000);
success!("3GB", ForwardFromBeginning 3000000000);
success!("4TB", ForwardFromBeginning 4000000000000);
success!("+4TB", ForwardFromLastOffset 4000000000000);
success!("1GiB", ForwardFromBeginning 1073741824);
success!("2TiB", ForwardFromBeginning 2199023255552);
success!("+2TiB", ForwardFromLastOffset 2199023255552);
success!("0xff", ForwardFromBeginning 255);
success!("0xEE", ForwardFromBeginning 238);
success!("+0xFF", ForwardFromLastOffset 255);
success!("1block", ForwardFromBeginning 512; block_size: 512);
success!("2block", ForwardFromBeginning 1024; block_size: 512);
success!("1block", ForwardFromBeginning 4; block_size: 4);
success!("2block", ForwardFromBeginning 8; block_size: 4);
// empty string is invalid
error!("", Empty);
// These are also bad.
error!("+", EmptyAfterSign);
error!("-", EmptyAfterSign);
error!("K", InvalidNumAndUnit("K".to_owned()));
error!("k", InvalidNumAndUnit("k".to_owned()));
error!("m", InvalidNumAndUnit("m".to_owned()));
error!("block", EmptyWithUnit("block".to_owned()));
// leading/trailing space is invalid
error!(" 0", InvalidNumAndUnit(" 0".to_owned()));
error!("0 ", InvalidUnit(" ".to_owned()));
// Signs after the hex prefix make no sense
error!("0x-12", SignFoundAfterHexPrefix('-'));
// This was previously accepted but shouldn't be.
error!("0x+12", SignFoundAfterHexPrefix('+'));
// invalid suffix
error!("1234asdf", InvalidUnit("asdf".to_owned()));
// bad numbers
error!("asdf1234", InvalidNumAndUnit("asdf1234".to_owned()));
error!("a1s2d3f4", InvalidNumAndUnit("a1s2d3f4".to_owned()));
// multiplication overflows u64
error!("20000000TiB", UnitMultiplicationOverflow);
assert!(
match parse_byte_offset("99999999999999999999", PositiveI64::new(512).unwrap()) {
// We can't check against the kind of the `ParseIntError`, so we'll just make sure it's the
// same as trying to do the parse directly.
Err(ParseNum(e)) => e == "99999999999999999999".parse::<i64>().unwrap_err(),
_ => false,
}
);
}
+184
View File
@@ -0,0 +1,184 @@
use owo_colors::{colors, AnsiColors, Color, DynColors, OwoColorize};
use std::str::FromStr;
use std::sync::LazyLock;
pub static COLOR_NULL: LazyLock<String> =
LazyLock::new(|| init_color("NULL", AnsiColors::BrightBlack));
pub static COLOR_OFFSET: LazyLock<String> =
LazyLock::new(|| init_color("OFFSET", AnsiColors::BrightBlack));
pub static COLOR_ASCII_PRINTABLE: LazyLock<String> =
LazyLock::new(|| init_color("ASCII_PRINTABLE", AnsiColors::Cyan));
pub static COLOR_ASCII_WHITESPACE: LazyLock<String> =
LazyLock::new(|| init_color("ASCII_WHITESPACE", AnsiColors::Green));
pub static COLOR_ASCII_OTHER: LazyLock<String> =
LazyLock::new(|| init_color("ASCII_OTHER", AnsiColors::Green));
pub static COLOR_NONASCII: LazyLock<String> =
LazyLock::new(|| init_color("NONASCII", AnsiColors::Yellow));
pub const COLOR_RESET: &str = colors::Default::ANSI_FG;
fn init_color(name: &str, default_ansi: AnsiColors) -> String {
let default = DynColors::Ansi(default_ansi);
let env_var = format!("HEXYL_COLOR_{name}");
let color = match std::env::var(env_var).as_deref() {
Ok(color) => match DynColors::from_str(color) {
Ok(color) => color,
_ => default,
},
_ => default,
};
// owo_colors' API isn't designed to get the terminal codes directly for
// dynamic colors, so we use this hack to get them from the LHS of some text.
format!("{}", "|".color(color))
.split_once("|")
.unwrap()
.0
.to_owned()
}
pub const COLOR_NULL_RGB: &[u8] = &rgb_bytes(100, 100, 100);
pub const COLOR_DEL: &[u8] = &rgb_bytes(64, 128, 0);
pub const COLOR_GRADIENT_NONASCII: [[u8; 19]; 128] =
generate_color_gradient(&[(255, 0, 0, 0.0), (255, 255, 0, 0.66), (255, 255, 255, 1.0)]);
pub const COLOR_GRADIENT_ASCII_NONPRINTABLE: [[u8; 19]; 31] =
generate_color_gradient(&[(255, 0, 255, 0.0), (128, 0, 255, 1.0)]);
pub const COLOR_GRADIENT_ASCII_PRINTABLE: [[u8; 19]; 95] =
generate_color_gradient(&[(0, 128, 255, 0.0), (0, 255, 128, 1.0)]);
const fn as_dec(byte: u8) -> [u8; 3] {
[
b'0' + (byte / 100),
b'0' + ((byte % 100) / 10),
b'0' + (byte % 10),
]
}
const fn rgb_bytes(r: u8, g: u8, b: u8) -> [u8; 19] {
let mut buf = *b"\x1b[38;2;rrr;ggg;bbbm";
// r 7
buf[7] = as_dec(r)[0];
buf[8] = as_dec(r)[1];
buf[9] = as_dec(r)[2];
// g 11
buf[11] = as_dec(g)[0];
buf[12] = as_dec(g)[1];
buf[13] = as_dec(g)[2];
// b 15
buf[15] = as_dec(b)[0];
buf[16] = as_dec(b)[1];
buf[17] = as_dec(b)[2];
buf
}
const fn generate_color_gradient<const N: usize>(stops: &[(u8, u8, u8, f64)]) -> [[u8; 19]; N] {
let mut out = [rgb_bytes(0, 0, 0); N];
assert!(stops.len() >= 2, "need at least two stops for the gradient");
let mut byte = 0;
while byte < N {
let relative_byte = byte as f64 / N as f64;
let mut i = 1;
while i < stops.len() && stops[i].3 < relative_byte {
i += 1;
}
if i >= stops.len() {
i = stops.len() - 1;
}
let prev_stop = stops[i - 1];
let stop = stops[i];
let diff = stop.3 - prev_stop.3;
let t = (relative_byte - prev_stop.3) / diff;
let r = (prev_stop.0 as f64 + (t * (stop.0 as f64 - prev_stop.0 as f64))) as u8;
let g = (prev_stop.1 as f64 + (t * (stop.1 as f64 - prev_stop.1 as f64))) as u8;
let b = (prev_stop.2 as f64 + (t * (stop.2 as f64 - prev_stop.2 as f64))) as u8;
out[byte] = rgb_bytes(r, g, b);
byte += 1;
}
out
}
#[rustfmt::skip]
pub const CP437: [char; 256] = [
// Copyright (c) 2016, Delan Azabani <delan@azabani.com>
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
//
// modified to use the ⋄ character instead of ␀
// use https://en.wikipedia.org/w/index.php?title=Code_page_437&oldid=978947122
// not ftp://ftp.unicode.org/Public/MAPPINGS/VENDORS/MICSFT/PC/CP437.TXT
// because we want the graphic versions of 01h–1Fh + 7Fh
'⋄','☺','☻','♥','♦','♣','♠','•','◘','○','◙','♂','♀','♪','♫','☼',
'►','◄','↕','‼','¶','§','▬','↨','↑','↓','→','←','∟','↔','▲','▼',
' ','!','"','#','$','%','&','\'','(',')','*','+',',','-','.','/',
'0','1','2','3','4','5','6','7','8','9',':',';','<','=','>','?',
'@','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O',
'P','Q','R','S','T','U','V','W','X','Y','Z','[','\\',']','^','_',
'`','a','b','c','d','e','f','g','h','i','j','k','l','m','n','o',
'p','q','r','s','t','u','v','w','x','y','z','{','|','}','~','⌂',
'Ç','ü','é','â','ä','à','å','ç','ê','ë','è','ï','î','ì','Ä','Å',
'É','æ','Æ','ô','ö','ò','û','ù','ÿ','Ö','Ü','¢','£','¥','₧','ƒ',
'á','í','ó','ú','ñ','Ñ','ª','º','¿','⌐','¬','½','¼','¡','«','»',
'░','▒','▓','│','┤','╡','╢','╖','╕','╣','║','╗','╝','╜','╛','┐',
'└','┴','┬','├','─','┼','╞','╟','╚','╔','╩','╦','╠','═','╬','╧',
'╨','╤','╥','╙','╘','╒','╓','╫','╪','┘','┌','█','▄','▌','▐','▀',
'α','ß','Γ','π','Σ','σ','µ','τ','Φ','Θ','Ω','δ','∞','φ','ε','∩',
'≡','±','≥','≤','⌠','⌡','÷','≈','°','∙','·','√','ⁿ','²','■','ff',
];
#[rustfmt::skip]
pub const CP1047: [char; 256] = [
//
// Copyright (c) 2016,2024 IBM Corporation and other Contributors.
//
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// which accompanies this distribution, and is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// Contributors:
// Mark Taylor - Initial Contribution
//
// ref1 https://github.com/ibm-messaging/mq-smf-csv/blob/master/src/smfConv.c
// ref2 https://web.archive.org/web/20150607033635/http://www-01.ibm.com/software/globalization/cp/cp01047.html
'.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.',
'.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.',
'.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.',
'.','.','.','.','.','.','.','.','.','.','.','.','.','.','.','.',
' ','.','.','.','.','.','.','.','.','.','$','.','<','(','+','|',
'&','.','.','.','.','.','.','.','.','.','!','$','*',')',';','.',
'-','/','.','.','.','.','.','.','.','.','.',',','%','_','>','?',
'.','.','.','.','.','.','.','.','.','.',':','#','@','\'','=','.',
'.','a','b','c','d','e','f','g','h','i','.','{','.','(','+','.',
'.','j','k','l','m','n','o','p','q','r','.','}','.',')','.','.',
'.','~','s','t','u','v','w','x','y','z','.','.','.','.','.','.',
'.','.','.','.','.','.','.','.','.','.','[',']','.','.','.','-',
'{','A','B','C','D','E','F','G','H','I','.','.','.','.','.','.',
'}','J','K','L','M','N','O','P','Q','R','.','.','.','.','.','.',
'.','.','S','T','U','V','W','X','Y','Z','.','.','.','.','.','.',
'0','1','2','3','4','5','6','7','8','9','.','.','.','.','.','.'
];
+3 -4
View File
@@ -1,4 +1,3 @@
use std::convert::TryFrom;
use std::fs;
use std::io::{self, copy, sink, Read, Seek, SeekFrom};
@@ -7,7 +6,7 @@ pub enum Input<'a> {
Stdin(io::StdinLock<'a>),
}
impl<'a> Read for Input<'a> {
impl Read for Input<'_> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
match *self {
Input::File(ref mut file) => file.read(buf),
@@ -16,13 +15,13 @@ impl<'a> Read for Input<'a> {
}
}
impl<'a> Seek for Input<'a> {
impl Seek for Input<'_> {
fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
fn try_skip<R>(reader: R, pos: SeekFrom, err_desc: &'static str) -> io::Result<u64>
where
R: Read,
{
let cant_seek_abs_err = || Err(io::Error::new(io::ErrorKind::Other, err_desc));
let cant_seek_abs_err = || Err(io::Error::other(err_desc));
let offset = match pos {
SeekFrom::Current(o) => u64::try_from(o).or_else(|_e| cant_seek_abs_err())?,
+883 -346
View File
File diff suppressed because it is too large. Load diff
+835
View File
@@ -0,0 +1,835 @@
use std::fs::File;
use std::io::{self, prelude::*, BufWriter, SeekFrom};
use std::num::{NonZeroI64, NonZeroU64};
use std::path::PathBuf;
use clap::builder::styling::{AnsiColor, Effects};
use clap::builder::ArgPredicate;
use clap::builder::Styles;
use clap::{ArgAction, CommandFactory, Parser, ValueEnum};
use clap_complete::aot::{generate, Shell};
use anyhow::{anyhow, bail, Context, Result};
use const_format::formatcp;
use thiserror::Error as ThisError;
use terminal_size::terminal_size;
use hexyl::{
Base, BorderStyle, CharacterTable, ColorScheme, Endianness, IncludeMode, Input, PrinterBuilder,
};
use hexyl::{
COLOR_ASCII_OTHER, COLOR_ASCII_PRINTABLE, COLOR_ASCII_WHITESPACE, COLOR_NONASCII, COLOR_NULL,
COLOR_RESET,
};
#[cfg(test)]
mod tests;
const DEFAULT_BLOCK_SIZE: i64 = 512;
const LENGTH_HELP_TEXT: &str = "Only read N bytes from the input. The N argument can also include \
a unit with a decimal prefix (kB, MB, ..) or binary prefix (kiB, \
MiB, ..), or can be specified using a hex number. The short \
option '-l' can be used as an alias.
Examples: --length=64, --length=4KiB, --length=0xff";
const SKIP_HELP_TEXT: &str = "Skip the first N bytes of the input. The N argument can also \
include a unit (see `--length` for details).
A negative value is valid and will seek from the end of the file.";
const BLOCK_SIZE_HELP_TEXT: &str = "Sets the size of the `block` unit to SIZE.
Examples: --block-size=1024, --block-size=4kB";
const DISPLAY_OFFSET_HELP_TEXT: &str = "Add N bytes to the displayed file position. The N \
argument can also include a unit (see `--length` for \
details).
A negative value is valid and calculates an offset relative to the end of the file.";
const TERMINAL_WIDTH_HELP_TEXT: &str = "Sets the number of terminal columns to be displayed.
Since the terminal width may not be an evenly divisible by the width per hex data column, this \
will use the greatest number of hex data panels that can \
fit in the requested width but still leave some space to \
the right.
Cannot be used with other width-setting options.";
const STYLES: Styles = Styles::styled()
.header(AnsiColor::Green.on_default().effects(Effects::BOLD))
.usage(AnsiColor::Green.on_default().effects(Effects::BOLD))
.literal(AnsiColor::Cyan.on_default().effects(Effects::BOLD))
.placeholder(AnsiColor::Cyan.on_default());
#[derive(Debug, Parser)]
#[command(version, about, max_term_width(90), styles = STYLES)]
struct Opt {
/// The file to display. If no FILE argument is given, read from STDIN.
#[arg(value_name("FILE"))]
file: Option<PathBuf>,
#[arg(
help(LENGTH_HELP_TEXT),
short('n'),
long,
visible_short_alias('c'),
visible_alias("bytes"),
short_alias('l'),
value_name("N")
)]
length: Option<String>,
#[arg(help(SKIP_HELP_TEXT), short, long, value_name("N"))]
skip: Option<String>,
#[arg(
help(BLOCK_SIZE_HELP_TEXT),
long,
default_value(formatcp!("{DEFAULT_BLOCK_SIZE}")),
value_name("SIZE")
)]
block_size: String,
/// 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(short('v'), long)]
no_squeezing: bool,
/// When to use colors.
#[arg(
long,
value_enum,
default_value_t,
value_name("WHEN"),
default_value_if("plain", ArgPredicate::IsPresent, Some("never"))
)]
color: ColorWhen,
/// Whether to draw a border.
#[arg(
long,
value_enum,
default_value_t,
value_name("STYLE"),
default_value_if("plain", ArgPredicate::IsPresent, Some("none"))
)]
border: BorderStyle,
/// Display output with --no-characters, --no-position, --border=none, and
/// --color=never.
#[arg(short, long)]
plain: bool,
/// Do not show the character panel on the right.
#[arg(long)]
no_characters: bool,
/// Show the character panel on the right. This is the default, unless
/// --no-characters has been specified.
#[arg(
short('C'),
long,
action(ArgAction::SetTrue),
overrides_with("no_characters")
)]
characters: (),
/// Defines how bytes are mapped to characters.
#[arg(long, value_enum, default_value_t, value_name("FORMAT"))]
character_table: CharacterTable,
/// Defines the color scheme for the characters.
#[arg(long, value_enum, default_value_t, value_name("FORMAT"))]
color_scheme: ColorScheme,
/// Whether to display the position panel on the left.
#[arg(short('P'), long)]
no_position: bool,
#[arg(
help(DISPLAY_OFFSET_HELP_TEXT),
short('o'),
long,
default_value("0"),
value_name("N")
)]
display_offset: String,
/// Sets the number of hex data panels to be displayed. `--panels=auto` will
/// display the maximum number of hex data panels based on the current
/// terminal width. By default, hexyl will show two panels, unless the
/// terminal is not wide enough for that.
#[arg(long, value_name("N"))]
panels: Option<String>,
/// Number of bytes/octets that should be grouped together. You can use the
/// '--endianness' option to control the ordering of the bytes within a
/// group. '--groupsize' can be used as an alias (xxd-compatibility).
#[arg(
short('g'),
long,
value_enum,
default_value_t,
alias("groupsize"),
value_name("N")
)]
group_size: GroupSize,
/// Whether to print out groups in little-endian or big-endian format. This
/// option only has an effect if the '--group-size' is larger than 1. '-e'
/// can be used as an alias for '--endianness=little'.
#[arg(long, value_enum, default_value_t, value_name("FORMAT"))]
endianness: Endianness,
/// An alias for '--endianness=little'.
#[arg(short('e'), hide(true), overrides_with("endianness"))]
little_endian_format: bool,
/// Sets the base used for the bytes. The possible options are binary,
/// octal, decimal, and hexadecimal.
#[arg(short('b'), long, default_value("hexadecimal"), value_name("B"))]
base: String,
#[arg(
help(TERMINAL_WIDTH_HELP_TEXT),
long,
value_name("N"),
conflicts_with("panels")
)]
terminal_width: Option<NonZeroU64>,
/// Print a table showing how different types of bytes are colored.
#[arg(long)]
print_color_table: bool,
/// Output in C include file style (similar to xxd -i).
#[arg(
short('i'),
long("include"),
help = "Output in C include file style",
conflicts_with("little_endian_format"),
conflicts_with("endianness")
)]
include_mode: bool,
/// Show shell completion for a certain shell
#[arg(long, value_name("SHELL"))]
completion: Option<Shell>,
}
#[derive(Clone, Debug, Default, ValueEnum)]
enum ColorWhen {
/// Always use colorized output.
#[default]
Always,
/// Only displays colors if the output goes to an interactive terminal.
Auto,
/// Do not use colorized output.
Never,
/// Override the NO_COLOR environment variable.
Force,
}
#[derive(Clone, Debug, Default, ValueEnum)]
enum GroupSize {
/// Grouped together every byte/octet.
#[default]
#[value(name = "1")]
One,
/// Grouped together every 2 bytes/octets.
#[value(name = "2")]
Two,
/// Grouped together every 4 bytes/octets.
#[value(name = "4")]
Four,
/// Grouped together every 8 bytes/octets.
#[value(name = "8")]
Eight,
}
impl From<GroupSize> for u8 {
fn from(number: GroupSize) -> Self {
match number {
GroupSize::One => 1,
GroupSize::Two => 2,
GroupSize::Four => 4,
GroupSize::Eight => 8,
}
}
}
fn run() -> Result<()> {
let opt = Opt::parse();
if opt.print_color_table {
return print_color_table().map_err(|e| anyhow!(e));
}
if let Some(sh) = opt.completion {
let mut cmd = Opt::command();
let name = cmd.get_name().to_string();
generate(sh, &mut cmd, name, &mut io::stdout());
return Ok(());
}
let stdin = io::stdin();
let mut reader = match &opt.file {
Some(filename) => {
if filename.as_os_str() == "-" {
Input::Stdin(stdin.lock())
} else {
if filename.is_dir() {
bail!("'{}' is a directory.", filename.to_string_lossy());
}
let file = File::open(filename)?;
Input::File(file)
}
}
None => Input::Stdin(stdin.lock()),
};
if let Some(hex_number) = try_parse_as_hex_number(&opt.block_size) {
return hex_number
.map_err(|e| anyhow!(e))
.and_then(|x| {
PositiveI64::new(x).ok_or_else(|| anyhow!("block size argument must be positive"))
})
.map(|_| ());
}
let (num, unit) = extract_num_and_unit_from(&opt.block_size)?;
if let Unit::Block { custom_size: _ } = unit {
return Err(anyhow!(
"can not use 'block(s)' as a unit to specify block size"
));
};
let block_size = num
.checked_mul(unit.get_multiplier())
.ok_or_else(|| anyhow!(ByteOffsetParseError::UnitMultiplicationOverflow))
.and_then(|x| {
PositiveI64::new(x).ok_or_else(|| anyhow!("block size argument must be positive"))
})?;
let skip_arg = opt
.skip
.as_ref()
.map(|s| {
parse_byte_offset(s, block_size).context(anyhow!(
"failed to parse `--skip` arg {:?} as byte count",
s
))
})
.transpose()?;
let skip_offset = if let Some(ByteOffset { kind, value }) = skip_arg {
let value = value.into_inner();
reader
.seek(match kind {
ByteOffsetKind::ForwardFromBeginning | ByteOffsetKind::ForwardFromLastOffset => {
SeekFrom::Current(value)
}
ByteOffsetKind::BackwardFromEnd => SeekFrom::End(value.checked_neg().unwrap()),
})
.map_err(|_| {
anyhow!(
"Failed to jump to the desired input position. \
This could be caused by a negative offset that is too large or by \
an input that is not seek-able (e.g. if the input comes from a pipe)."
)
})?
} else {
0
};
let parse_byte_count = |s| -> Result<u64> {
Ok(parse_byte_offset(s, block_size)?
.assume_forward_offset_from_start()?
.into())
};
let mut reader = if let Some(ref length) = opt.length {
let length = parse_byte_count(length).context(anyhow!(
"failed to parse `--length` arg {:?} as byte count",
length
))?;
Box::new(reader.take(length))
} else {
reader.into_inner()
};
let no_color = std::env::var_os("NO_COLOR").is_some();
let show_color = match opt.color {
ColorWhen::Never => false,
ColorWhen::Always => !no_color,
ColorWhen::Force => true,
ColorWhen::Auto => {
if no_color {
false
} else {
supports_color::on(supports_color::Stream::Stdout)
.map(|level| level.has_basic)
.unwrap_or(false)
}
}
};
let border_style = opt.border;
let &squeeze = &!opt.no_squeezing;
let show_char_panel = !opt.no_characters && !opt.plain;
let show_position_panel = !opt.no_position && !opt.plain;
let display_offset: u64 = parse_byte_count(&opt.display_offset).context(anyhow!(
"failed to parse `--display-offset` arg {:?} as byte count",
opt.display_offset
))?;
let max_panels_fn = |terminal_width: u64, base_digits: u64, group_size: u64| {
let offset = if show_position_panel { 10 } else { 1 };
let col_width = if show_char_panel {
((8 / group_size) * (base_digits * group_size + 1)) + 2 + 8
} else {
((8 / group_size) * (base_digits * group_size + 1)) + 2
};
if (terminal_width.saturating_sub(offset)) / col_width < 1 {
1
} else {
(terminal_width - offset) / col_width
}
};
let base = if let Ok(base_num) = opt.base.parse::<u8>() {
match base_num {
2 => Ok(Base::Binary),
8 => Ok(Base::Octal),
10 => Ok(Base::Decimal),
16 => Ok(Base::Hexadecimal),
_ => Err(anyhow!(
"The number provided is not a valid base. Valid bases are 2, 8, 10, and 16."
)),
}
} else {
match opt.base.as_str() {
"b" | "bin" | "binary" => Ok(Base::Binary),
"o" | "oct" | "octal" => Ok(Base::Octal),
"d" | "dec" | "decimal" => Ok(Base::Decimal),
"x" | "hex" | "hexadecimal" => Ok(Base::Hexadecimal),
_ => Err(anyhow!(
"The base provided is not valid. Valid bases are \"b\", \"o\", \"d\", and \"x\"."
)),
}
}?;
let base_digits = match base {
Base::Binary => 8,
Base::Octal => 3,
Base::Decimal => 3,
Base::Hexadecimal => 2,
};
let group_size = u8::from(opt.group_size);
let terminal_width = terminal_size().map(|s| s.0 .0 as u64).unwrap_or(80);
let panels = if opt.panels.as_deref() == Some("auto") {
max_panels_fn(terminal_width, base_digits, group_size.into())
} else if let Some(panels) = opt.panels {
panels
.parse::<NonZeroU64>()
.map(u64::from)
.context(anyhow!(
"failed to parse `--panels` arg {:?} as unsigned nonzero integer",
panels
))?
} else if let Some(terminal_width) = opt.terminal_width {
max_panels_fn(terminal_width.into(), base_digits, group_size.into())
} else {
std::cmp::min(
2,
max_panels_fn(terminal_width, base_digits, group_size.into()),
)
};
let endianness = if opt.little_endian_format {
Endianness::Little
} else {
opt.endianness
};
let character_table = opt.character_table;
let color_scheme = opt.color_scheme;
let mut stdout = BufWriter::new(io::stdout().lock());
let include_mode = match opt.include_mode {
// include mode on
true => {
if let Some(include_file) = opt.file {
// input from a file
if include_file.as_os_str() == "-" {
IncludeMode::File("stdin".to_string())
} else {
IncludeMode::File(
include_file
.file_name()
.and_then(|n| n.to_str())
.unwrap_or("file")
.to_string(),
)
}
} else {
// input from stdin
IncludeMode::Stdin
}
}
// include mode off
false => IncludeMode::Off,
};
let mut printer = PrinterBuilder::new(&mut stdout)
.show_color(show_color)
.show_char_panel(show_char_panel)
.show_position_panel(show_position_panel)
.with_border_style(border_style)
.enable_squeezing(squeeze)
.num_panels(panels)
.group_size(group_size)
.with_base(base)
.endianness(endianness)
.character_table(character_table)
.include_mode(include_mode)
.color_scheme(color_scheme)
.build();
printer.display_offset(skip_offset + display_offset);
printer.print_all(&mut reader).map_err(|e| anyhow!(e))?;
Ok(())
}
fn main() {
let result = run();
if let Err(err) = result {
if let Some(io_error) = err.downcast_ref::<io::Error>() {
if io_error.kind() == ::std::io::ErrorKind::BrokenPipe {
std::process::exit(0);
}
}
eprintln!("Error: {err:?}");
std::process::exit(1);
}
}
#[derive(Clone, Copy, Debug, Default, Hash, Eq, Ord, PartialEq, PartialOrd)]
pub struct NonNegativeI64(i64);
impl NonNegativeI64 {
pub fn new(x: i64) -> Option<Self> {
if x.is_negative() {
None
} else {
Some(Self(x))
}
}
pub fn into_inner(self) -> i64 {
self.0
}
}
impl From<NonNegativeI64> for u64 {
fn from(x: NonNegativeI64) -> u64 {
u64::try_from(x.0)
.expect("invariant broken: NonNegativeI64 should contain a non-negative i64 value")
}
}
fn print_color_table() -> io::Result<()> {
let mut stdout = BufWriter::new(io::stdout().lock());
writeln!(stdout, "hexyl color reference:\n")?;
// NULL bytes
stdout.write_all(COLOR_NULL.as_bytes())?;
writeln!(stdout, "⋄ NULL bytes (0x00)")?;
stdout.write_all(COLOR_RESET.as_bytes())?;
// ASCII printable
stdout.write_all(COLOR_ASCII_PRINTABLE.as_bytes())?;
writeln!(stdout, "a ASCII printable characters (0x20 - 0x7E)")?;
stdout.write_all(COLOR_RESET.as_bytes())?;
// ASCII whitespace
stdout.write_all(COLOR_ASCII_WHITESPACE.as_bytes())?;
writeln!(stdout, "_ ASCII whitespace (0x09 - 0x0D, 0x20)")?;
stdout.write_all(COLOR_RESET.as_bytes())?;
// ASCII other
stdout.write_all(COLOR_ASCII_OTHER.as_bytes())?;
writeln!(
stdout,
"• ASCII control characters (except NULL and whitespace)"
)?;
stdout.write_all(COLOR_RESET.as_bytes())?;
// Non-ASCII
stdout.write_all(COLOR_NONASCII.as_bytes())?;
writeln!(stdout, "× Non-ASCII bytes (0x80 - 0xFF)")?;
stdout.write_all(COLOR_RESET.as_bytes())?;
Ok(())
}
#[derive(Clone, Copy, Debug, Default, Hash, Eq, Ord, PartialEq, PartialOrd)]
pub struct PositiveI64(i64);
impl PositiveI64 {
pub fn new(x: i64) -> Option<Self> {
if x < 1 {
None
} else {
Some(Self(x))
}
}
pub fn into_inner(self) -> i64 {
self.0
}
}
impl From<PositiveI64> for u64 {
fn from(x: PositiveI64) -> u64 {
u64::try_from(x.0)
.expect("invariant broken: PositiveI64 should contain a positive i64 value")
}
}
#[derive(Debug, PartialEq)]
enum Unit {
Byte,
Kilobyte,
Megabyte,
Gigabyte,
Terabyte,
Kibibyte,
Mebibyte,
Gibibyte,
Tebibyte,
/// a customizable amount of bytes
Block {
custom_size: Option<NonZeroI64>,
},
}
impl Unit {
const fn get_multiplier(self) -> i64 {
match self {
Self::Byte => 1,
Self::Kilobyte => 1000,
Self::Megabyte => 1_000_000,
Self::Gigabyte => 1_000_000_000,
Self::Terabyte => 1_000_000_000_000,
Self::Kibibyte => 1 << 10,
Self::Mebibyte => 1 << 20,
Self::Gibibyte => 1 << 30,
Self::Tebibyte => 1 << 40,
Self::Block {
custom_size: Some(size),
} => size.get(),
Self::Block { custom_size: None } => DEFAULT_BLOCK_SIZE,
}
}
}
const HEX_PREFIX: &str = "0x";
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
enum ByteOffsetKind {
ForwardFromBeginning,
ForwardFromLastOffset,
BackwardFromEnd,
}
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
struct ByteOffset {
value: NonNegativeI64,
kind: ByteOffsetKind,
}
#[derive(Clone, Debug, ThisError)]
#[error(
"negative offset specified, but only positive offsets (counts) are accepted in this context"
)]
struct NegativeOffsetSpecifiedError;
impl ByteOffset {
fn assume_forward_offset_from_start(
&self,
) -> Result<NonNegativeI64, NegativeOffsetSpecifiedError> {
let &Self { value, kind } = self;
match kind {
ByteOffsetKind::ForwardFromBeginning | ByteOffsetKind::ForwardFromLastOffset => {
Ok(value)
}
ByteOffsetKind::BackwardFromEnd => Err(NegativeOffsetSpecifiedError),
}
}
}
#[derive(Clone, Debug, Eq, PartialEq, ThisError)]
enum ByteOffsetParseError {
#[error("no character data found, did you forget to write it?")]
Empty,
#[error("no digits found after sign, did you forget to write them?")]
EmptyAfterSign,
#[error(
"found {0:?} sign after hex prefix ({:?}); signs should go before it",
HEX_PREFIX
)]
SignFoundAfterHexPrefix(char),
#[error("{0:?} is not of the expected form <pos-integer>[<unit>]")]
InvalidNumAndUnit(String),
#[error("{0:?} is a valid unit, but an integer should come before it")]
EmptyWithUnit(String),
#[error("invalid unit {0:?}")]
InvalidUnit(String),
#[error("failed to parse integer part")]
ParseNum(#[source] std::num::ParseIntError),
#[error("count multiplied by the unit overflowed a signed 64-bit integer; are you sure it should be that big?")]
UnitMultiplicationOverflow,
}
fn parse_byte_offset(n: &str, block_size: PositiveI64) -> Result<ByteOffset, ByteOffsetParseError> {
use ByteOffsetParseError::*;
let (n, kind) = process_sign_of(n)?;
let into_byte_offset = |value| {
Ok(ByteOffset {
value: NonNegativeI64::new(value).unwrap(),
kind,
})
};
if let Some(hex_number) = try_parse_as_hex_number(n) {
return hex_number.map(into_byte_offset)?;
}
let (num, mut unit) = extract_num_and_unit_from(n)?;
if let Unit::Block { custom_size: None } = unit {
unit = Unit::Block {
custom_size: Some(
NonZeroI64::new(block_size.into_inner()).expect("PositiveI64 was zero"),
),
};
}
num.checked_mul(unit.get_multiplier())
.ok_or(UnitMultiplicationOverflow)
.and_then(into_byte_offset)
}
/// Takes a string containing a base-10 number and an optional unit, and returns them with their proper types.
/// The unit must directly follow the number (e.g. no whitespace is allowed between them).
/// When no unit is given, [Unit::Byte] is assumed.
/// When the unit is [Unit::Block], it is returned without custom size.
/// No normalization is performed, that is "1024" is extracted to (1024, Byte), not (1, Kibibyte).
fn extract_num_and_unit_from(n: &str) -> Result<(i64, Unit), ByteOffsetParseError> {
use ByteOffsetParseError::*;
if n.is_empty() {
return Err(Empty);
}
match n.chars().position(|c| !c.is_ascii_digit()) {
Some(unit_begin_idx) => {
let (n, raw_unit) = n.split_at(unit_begin_idx);
let unit = match raw_unit.to_lowercase().as_str() {
"" => Unit::Byte, // no "b" => Byte to allow hex nums with units
"kb" => Unit::Kilobyte,
"mb" => Unit::Megabyte,
"gb" => Unit::Gigabyte,
"tb" => Unit::Terabyte,
"kib" => Unit::Kibibyte,
"mib" => Unit::Mebibyte,
"gib" => Unit::Gibibyte,
"tib" => Unit::Tebibyte,
"block" | "blocks" => Unit::Block { custom_size: None },
_ => {
return if n.is_empty() {
Err(InvalidNumAndUnit(raw_unit.to_string()))
} else {
Err(InvalidUnit(raw_unit.to_string()))
}
}
};
let num = n.parse::<i64>().map_err(|e| {
if n.is_empty() {
EmptyWithUnit(raw_unit.to_owned())
} else {
ParseNum(e)
}
})?;
Ok((num, unit))
}
None => {
// no unit part
let num = n.parse::<i64>().map_err(ParseNum)?;
Ok((num, Unit::Byte))
}
}
}
/// Extracts a [ByteOffsetKind] based on the sign at the beginning of the given string.
/// Returns the input string without the sign (or an equal string if there wasn't any sign).
fn process_sign_of(n: &str) -> Result<(&str, ByteOffsetKind), ByteOffsetParseError> {
use ByteOffsetParseError::*;
let mut chars = n.chars();
let next_char = chars.next();
let check_empty_after_sign = || {
if chars.clone().next().is_none() {
Err(EmptyAfterSign)
} else {
Ok(chars.as_str())
}
};
match next_char {
Some('+') => Ok((
check_empty_after_sign()?,
ByteOffsetKind::ForwardFromLastOffset,
)),
Some('-') => Ok((check_empty_after_sign()?, ByteOffsetKind::BackwardFromEnd)),
None => Err(Empty),
_ => Ok((n, ByteOffsetKind::ForwardFromBeginning)),
}
}
/// If `n` starts with a hex prefix, its remaining part is returned as some number (if possible),
/// otherwise None is returned.
fn try_parse_as_hex_number(n: &str) -> Option<Result<i64, ByteOffsetParseError>> {
use ByteOffsetParseError::*;
n.strip_prefix(HEX_PREFIX).map(|num| {
let mut chars = num.chars();
match chars.next() {
Some(c @ '+') | Some(c @ '-') => {
return if chars.next().is_none() {
Err(EmptyAfterSign)
} else {
Err(SignFoundAfterHexPrefix(c))
}
}
_ => (),
}
i64::from_str_radix(num, 16).map_err(ParseNum)
})
}
-407
View File
@@ -1,407 +0,0 @@
#[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,
lsize: u64,
}
#[derive(Debug, PartialEq)]
pub enum SqueezeAction {
Ignore,
Print,
Delete,
}
impl Squeezer {
pub fn new(enabled: bool, lsize: u64) -> Squeezer {
Squeezer {
state: if enabled {
SqueezeState::Probe
} else {
SqueezeState::Disabled
},
byte: 0,
lsize,
}
}
pub fn process(&mut self, b: u8, i: u64) {
use self::SqueezeState::*;
if self.state == Disabled {
return;
}
let eq = b == self.byte;
if i % self.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 % self.lsize == 1 {
self.state = Probe;
} else if i % self.lsize != 1 {
self.state = NoSqueeze;
}
}
self.byte = b;
}
pub fn active(&self) -> bool {
use self::SqueezeState::*;
matches!(
self.state,
Squeeze | SqueezeActive | SqueezeFirstLine | SqueezeActiveFirstLine
)
}
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::*;
const LSIZE: u64 = 16;
const LSIZE_USIZE: usize = LSIZE as usize;
#[test]
fn three_same_lines() {
const LINES: usize = 3;
let v = vec![0u8; LINES * LSIZE_USIZE];
let mut s = Squeezer::new(true, LSIZE);
// 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 idx = 1;
for (line, z) in v.chunks(LSIZE_USIZE).enumerate() {
for i in z {
s.process(*i, idx);
idx += 1;
}
let action = s.action();
s.advance();
assert_eq!(action, exp[line]);
}
}
#[test]
fn incomplete_while_squeeze() {
// fourth line only has 12 bytes and should be printed
let v = vec![0u8; 3 * LSIZE_USIZE + 12];
let mut s = Squeezer::new(true, LSIZE);
// 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 idx = 1;
for (line, z) in v.chunks(LSIZE_USIZE).enumerate() {
for i in z {
s.process(*i, idx);
idx += 1;
}
assert_eq!(s.action(), exp[line]);
s.advance();
}
}
#[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, LSIZE);
// 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 idx = 1;
for (line, z) in v.chunks(LSIZE_USIZE).enumerate() {
for i in z {
s.process(*i, idx);
idx += 1;
}
let action = s.action();
assert_eq!(action, exp[line]);
s.advance();
}
}
#[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_USIZE];
v.extend(vec![1u8; 16]);
let mut s = Squeezer::new(true, LSIZE);
// 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 idx = 1;
for (line, z) in v.chunks(LSIZE_USIZE).enumerate() {
for i in z {
s.process(*i, idx);
idx += 1;
}
let action = s.action();
s.advance();
assert_eq!(action, exp[line]);
}
}
#[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_USIZE];
v.extend(vec![0u8; 8]);
v.extend(vec![1u8; 8]);
let mut s = Squeezer::new(true, LSIZE);
// 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 idx = 1;
for (line, z) in v.chunks(LSIZE_USIZE).enumerate() {
for i in z {
s.process(*i, idx);
idx += 1;
}
let action = s.action();
s.advance();
assert_eq!(action, exp[line]);
}
}
#[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, LSIZE);
// 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 idx = 1;
for (line, z) in v.chunks(LSIZE_USIZE).enumerate() {
for i in z {
s.process(*i, idx);
idx += 1;
}
let action = s.action();
s.advance();
assert_eq!(action, exp[line]);
}
}
#[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, LSIZE);
// 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 idx = 1;
for (line, z) in v.chunks(LSIZE_USIZE).enumerate() {
for i in z {
s.process(*i, idx);
idx += 1;
}
let action = s.action();
s.advance();
assert_eq!(action, exp[line]);
}
}
#[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, LSIZE);
// 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 idx = 1;
for (line, z) in v.chunks(LSIZE_USIZE).enumerate() {
for i in z {
s.process(*i, idx);
idx += 1;
}
assert_eq!(s.action(), exp[line]);
s.advance();
}
}
#[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, LSIZE);
// 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 idx = 1;
for (line, z) in v.chunks(LSIZE_USIZE).enumerate() {
for i in z {
s.process(*i, idx);
idx += 1;
}
assert_eq!(s.action(), exp[line]);
s.advance();
}
}
}
+156
View File
@@ -0,0 +1,156 @@
use super::*;
#[test]
fn unit_multipliers() {
use Unit::*;
assert_eq!(Kilobyte.get_multiplier(), 1000 * Byte.get_multiplier());
assert_eq!(Megabyte.get_multiplier(), 1000 * Kilobyte.get_multiplier());
assert_eq!(Gigabyte.get_multiplier(), 1000 * Megabyte.get_multiplier());
assert_eq!(Terabyte.get_multiplier(), 1000 * Gigabyte.get_multiplier());
assert_eq!(Kibibyte.get_multiplier(), 1024 * Byte.get_multiplier());
assert_eq!(Mebibyte.get_multiplier(), 1024 * Kibibyte.get_multiplier());
assert_eq!(Gibibyte.get_multiplier(), 1024 * Mebibyte.get_multiplier());
assert_eq!(Tebibyte.get_multiplier(), 1024 * Gibibyte.get_multiplier());
}
#[test]
fn test_process_sign() {
use ByteOffsetKind::*;
use ByteOffsetParseError::*;
assert_eq!(process_sign_of("123"), Ok(("123", ForwardFromBeginning)));
assert_eq!(process_sign_of("+123"), Ok(("123", ForwardFromLastOffset)));
assert_eq!(process_sign_of("-123"), Ok(("123", BackwardFromEnd)));
assert_eq!(process_sign_of("-"), Err(EmptyAfterSign));
assert_eq!(process_sign_of("+"), Err(EmptyAfterSign));
assert_eq!(process_sign_of(""), Err(Empty));
}
#[test]
fn test_parse_as_hex() {
assert_eq!(try_parse_as_hex_number("73"), None);
assert_eq!(try_parse_as_hex_number("0x1337"), Some(Ok(0x1337)));
assert!(matches!(try_parse_as_hex_number("0xnope"), Some(Err(_))));
assert!(matches!(try_parse_as_hex_number("0x-1"), Some(Err(_))));
}
#[test]
fn extract_num_and_unit() {
use ByteOffsetParseError::*;
use Unit::*;
// byte is default unit
assert_eq!(extract_num_and_unit_from("4"), Ok((4, Byte)));
// blocks are returned without customization
assert_eq!(
extract_num_and_unit_from("2blocks"),
Ok((2, Block { custom_size: None }))
);
// no normalization is performed
assert_eq!(extract_num_and_unit_from("1024kb"), Ok((1024, Kilobyte)));
// unit without number results in error
assert_eq!(
extract_num_and_unit_from("gib"),
Err(EmptyWithUnit("gib".to_string()))
);
// empty string results in error
assert_eq!(extract_num_and_unit_from(""), Err(Empty));
// an invalid unit results in an error
assert_eq!(
extract_num_and_unit_from("25litres"),
Err(InvalidUnit("litres".to_string()))
);
}
#[test]
fn test_parse_byte_offset() {
use ByteOffsetParseError::*;
macro_rules! success {
($input: expr, $expected_kind: ident $expected_value: expr) => {
success!($input, $expected_kind $expected_value; block_size: DEFAULT_BLOCK_SIZE)
};
($input: expr, $expected_kind: ident $expected_value: expr; block_size: $block_size: expr) => {
assert_eq!(
parse_byte_offset($input, PositiveI64::new($block_size).unwrap()),
Ok(
ByteOffset {
value: NonNegativeI64::new($expected_value).unwrap(),
kind: ByteOffsetKind::$expected_kind,
}
),
);
};
}
macro_rules! error {
($input: expr, $expected_err: expr) => {
assert_eq!(
parse_byte_offset($input, PositiveI64::new(DEFAULT_BLOCK_SIZE).unwrap()),
Err($expected_err),
);
};
}
success!("0", ForwardFromBeginning 0);
success!("1", ForwardFromBeginning 1);
success!("1", ForwardFromBeginning 1);
success!("100", ForwardFromBeginning 100);
success!("+100", ForwardFromLastOffset 100);
success!("0x0", ForwardFromBeginning 0);
success!("0xf", ForwardFromBeginning 15);
success!("0xdeadbeef", ForwardFromBeginning 3_735_928_559);
success!("1KB", ForwardFromBeginning 1000);
success!("2MB", ForwardFromBeginning 2000000);
success!("3GB", ForwardFromBeginning 3000000000);
success!("4TB", ForwardFromBeginning 4000000000000);
success!("+4TB", ForwardFromLastOffset 4000000000000);
success!("1GiB", ForwardFromBeginning 1073741824);
success!("2TiB", ForwardFromBeginning 2199023255552);
success!("+2TiB", ForwardFromLastOffset 2199023255552);
success!("0xff", ForwardFromBeginning 255);
success!("0xEE", ForwardFromBeginning 238);
success!("+0xFF", ForwardFromLastOffset 255);
success!("1block", ForwardFromBeginning 512; block_size: 512);
success!("2block", ForwardFromBeginning 1024; block_size: 512);
success!("1block", ForwardFromBeginning 4; block_size: 4);
success!("2block", ForwardFromBeginning 8; block_size: 4);
// empty string is invalid
error!("", Empty);
// These are also bad.
error!("+", EmptyAfterSign);
error!("-", EmptyAfterSign);
error!("K", InvalidNumAndUnit("K".to_owned()));
error!("k", InvalidNumAndUnit("k".to_owned()));
error!("m", InvalidNumAndUnit("m".to_owned()));
error!("block", EmptyWithUnit("block".to_owned()));
// leading/trailing space is invalid
error!(" 0", InvalidNumAndUnit(" 0".to_owned()));
error!("0 ", InvalidUnit(" ".to_owned()));
// Signs after the hex prefix make no sense
error!("0x-12", SignFoundAfterHexPrefix('-'));
// This was previously accepted but shouldn't be.
error!("0x+12", SignFoundAfterHexPrefix('+'));
// invalid suffix
error!("1234asdf", InvalidUnit("asdf".to_owned()));
// bad numbers
error!("asdf1234", InvalidNumAndUnit("asdf1234".to_owned()));
error!("a1s2d3f4", InvalidNumAndUnit("a1s2d3f4".to_owned()));
// multiplication overflows u64
error!("20000000TiB", UnitMultiplicationOverflow);
assert!(
match parse_byte_offset("99999999999999999999", PositiveI64::new(512).unwrap()) {
// We can't check against the kind of the `ParseIntError`, so we'll just make sure it's the
// same as trying to do the parse directly.
Err(ParseNum(e)) => e == "99999999999999999999".parse::<i64>().unwrap_err(),
_ => false,
}
);
}
+696 -5
View File
@@ -5,6 +5,26 @@ fn hexyl() -> Command {
cmd.current_dir("tests/examples");
cmd
}
trait PrettyAssert<S>
where
S: AsRef<str>,
{
fn pretty_stdout(self, other: S);
}
// https://github.com/assert-rs/assert_cmd/issues/121#issuecomment-849937376
//
impl<S> PrettyAssert<S> for assert_cmd::assert::Assert
where
S: AsRef<str>,
{
fn pretty_stdout(self, other: S) {
println!("{}", other.as_ref().len());
let self_str = String::from_utf8(self.get_output().stdout.clone()).unwrap();
println!("{}", self_str.len());
pretty_assertions::assert_eq!(self_str, other.as_ref());
}
}
mod basic {
use super::hexyl;
@@ -51,7 +71,7 @@ mod basic {
.success()
.stdout(
"┌────────┬─────────────────────────┬─────────────────────────┬────────┬────────┐\n\
│ │ No content to print │ │ │ │\n\
│ │ No content │ │ │ │\n\
└────────┴─────────────────────────┴─────────────────────────┴────────┴────────┘\n",
);
}
@@ -70,8 +90,8 @@ mod length {
.success()
.stdout(
"┌────────┬─────────────────────────┬─────────────────────────┬────────┬────────┐\n\
│00000000│ 7f 45 4c 46 02 01 01 00 ┊ 00 00 00 00 00 00 00 00 │•ELF•••0┊00000000│\n\
│00000010│ 02 00 3e 00 01 00 00 00 ┊ 00 10 40 00 00 00 00 00 │•0>0•000┊0•@00000│\n\
│00000000│ 7f 45 4c 46 02 01 01 00 ┊ 00 00 00 00 00 00 00 00 │•ELF•••⋄┊⋄⋄⋄⋄⋄⋄⋄⋄│\n\
│00000010│ 02 00 3e 00 01 00 00 00 ┊ 00 10 40 00 00 00 00 00 │•⋄>⋄•⋄⋄⋄┊⋄•@⋄⋄⋄⋄⋄│\n\
└────────┴─────────────────────────┴─────────────────────────┴────────┴────────┘\n",
);
}
@@ -140,7 +160,7 @@ mod skip {
.success()
.stdout(
"┌────────┬─────────────────────────┬─────────────────────────┬────────┬────────┐\n\
│ │ No content to print │ │ │ │\n\
│ │ No content │ │ │ │\n\
└────────┴─────────────────────────┴─────────────────────────┴────────┴────────┘\n",
);
}
@@ -203,7 +223,7 @@ mod display_offset {
.success()
.stdout(
"┌────────┬─────────────────────────┬─────────────────────────┬────────┬────────┐\n\
│00000030│ 02 00 3e 00 01 00 00 00 ┊ 00 10 40 00 00 00 00 00 │•0>0•000┊0•@00000│\n\
│00000030│ 02 00 3e 00 01 00 00 00 ┊ 00 10 40 00 00 00 00 00 │•⋄>⋄•⋄⋄⋄┊⋄•@⋄⋄⋄⋄⋄│\n\
└────────┴─────────────────────────┴─────────────────────────┴────────┴────────┘\n",
);
}
@@ -271,3 +291,674 @@ mod display_settings {
);
}
}
mod group_and_endianness {
use super::hexyl;
use super::PrettyAssert;
#[test]
fn group_2_bytes_be() {
hexyl()
.arg("ascii")
.arg("--color=never")
.arg("--group-size=2")
.assert()
.success()
.stdout(
"┌────────┬─────────────────────┬─────────────────────┬────────┬────────┐\n\
│00000000│ 3031 3233 3435 3637 ┊ 3839 6162 6364 650a │01234567┊89abcde_│\n\
└────────┴─────────────────────┴─────────────────────┴────────┴────────┘\n",
);
}
#[test]
fn group_2_bytes_le() {
hexyl()
.arg("ascii")
.arg("--color=never")
.arg("--group-size=2")
.arg("--endianness=little")
.assert()
.success()
.stdout(
"┌────────┬─────────────────────┬─────────────────────┬────────┬────────┐\n\
│00000000│ 3130 3332 3534 3736 ┊ 3938 6261 6463 0a65 │01234567┊89abcde_│\n\
└────────┴─────────────────────┴─────────────────────┴────────┴────────┘\n",
);
}
#[test]
fn group_4_bytes_be() {
hexyl()
.arg("ascii")
.arg("--color=never")
.arg("--group-size=4")
.assert()
.success()
.stdout(
"┌────────┬───────────────────┬───────────────────┬────────┬────────┐\n\
│00000000│ 30313233 34353637 ┊ 38396162 6364650a │01234567┊89abcde_│\n\
└────────┴───────────────────┴───────────────────┴────────┴────────┘\n",
);
}
#[test]
fn group_4_bytes_le() {
hexyl()
.arg("ascii")
.arg("--color=never")
.arg("--group-size=4")
.arg("--endianness=little")
.assert()
.success()
.stdout(
"┌────────┬───────────────────┬───────────────────┬────────┬────────┐\n\
│00000000│ 33323130 37363534 ┊ 62613938 0a656463 │01234567┊89abcde_│\n\
└────────┴───────────────────┴───────────────────┴────────┴────────┘\n",
);
}
#[test]
fn group_8_bytes_be() {
hexyl()
.arg("ascii")
.arg("--color=never")
.arg("--group-size=8")
.assert()
.success()
.stdout(
"┌────────┬──────────────────┬──────────────────┬────────┬────────┐\n\
│00000000│ 3031323334353637 ┊ 383961626364650a │01234567┊89abcde_│\n\
└────────┴──────────────────┴──────────────────┴────────┴────────┘\n",
);
}
#[test]
fn group_8_bytes_le() {
hexyl()
.arg("ascii")
.arg("--color=never")
.arg("--group-size=8")
.arg("--endianness=little")
.assert()
.success()
.stdout(
"┌────────┬──────────────────┬──────────────────┬────────┬────────┐\n\
│00000000│ 3736353433323130 ┊ 0a65646362613938 │01234567┊89abcde_│\n\
└────────┴──────────────────┴──────────────────┴────────┴────────┘\n",
);
}
#[test]
fn group_size_plain() {
hexyl()
.arg("ascii")
.arg("--color=never")
.arg("--plain")
.arg("--group-size=2")
.assert()
.success()
.stdout(" 3031 3233 3435 3637 3839 6162 6364 650a \n");
}
#[test]
fn group_size_fill_space() {
hexyl()
.arg("--color=never")
.arg("--group-size=2")
.write_stdin("abc")
.assert()
.success()
.stdout(
"┌────────┬─────────────────────┬─────────────────────┬────────┬────────┐\n\
│00000000│ 6162 63 ┊ │abc ┊ │\n\
└────────┴─────────────────────┴─────────────────────┴────────┴────────┘\n",
);
}
#[test]
fn group_size_invalid() {
hexyl()
.arg("ascii")
.arg("--color=never")
.arg("--plain")
.arg("--group-size=3")
.assert()
.failure();
}
#[test]
fn squeeze_no_chars() {
hexyl()
.arg("hello_world_elf64")
.arg("--color=never")
.arg("--skip=1024")
.arg("--length=4096")
.arg("--no-characters")
.assert()
.success()
.pretty_stdout(
"\
┌────────┬─────────────────────────┬─────────────────────────┐
│00000400│ 00 00 00 00 00 00 00 00 ┊ 00 00 00 00 00 00 00 00 │
│* │ ┊ │
│00001000│ ba 0e 00 00 00 b9 00 20 ┊ 40 00 bb 01 00 00 00 b8 │
│00001010│ 04 00 00 00 cd 80 b8 01 ┊ 00 00 00 cd 80 00 00 00 │
│00001020│ 00 00 00 00 00 00 00 00 ┊ 00 00 00 00 00 00 00 00 │
│* │ ┊ │
│00001400│ ┊ │
└────────┴─────────────────────────┴─────────────────────────┘
",
);
}
#[test]
fn squeeze_no_chars_one_panel() {
hexyl()
.arg("hello_world_elf64")
.arg("--color=never")
.arg("--skip=1024")
.arg("--length=4096")
.arg("--no-characters")
.arg("--panels=1")
.assert()
.success()
.pretty_stdout(
"\
┌────────┬─────────────────────────┐
│00000400│ 00 00 00 00 00 00 00 00 │
│* │ │
│00001000│ ba 0e 00 00 00 b9 00 20 │
│00001008│ 40 00 bb 01 00 00 00 b8 │
│00001010│ 04 00 00 00 cd 80 b8 01 │
│00001018│ 00 00 00 cd 80 00 00 00 │
│00001020│ 00 00 00 00 00 00 00 00 │
│* │ │
│00001400│ │
└────────┴─────────────────────────┘
",
);
}
#[test]
fn squeeze_no_position() {
hexyl()
.arg("hello_world_elf64")
.arg("--color=never")
.arg("--skip=1024")
.arg("--length=4096")
.arg("--no-position")
.assert()
.success()
.pretty_stdout(
"\
┌─────────────────────────┬─────────────────────────┬────────┬────────┐
│ 00 00 00 00 00 00 00 00 ┊ 00 00 00 00 00 00 00 00 │⋄⋄⋄⋄⋄⋄⋄⋄┊⋄⋄⋄⋄⋄⋄⋄⋄│
│* ┊ │ ┊ │
│ ba 0e 00 00 00 b9 00 20 ┊ 40 00 bb 01 00 00 00 b8 │ו⋄⋄⋄×⋄ ┊@⋄ו⋄⋄⋄×│
│ 04 00 00 00 cd 80 b8 01 ┊ 00 00 00 cd 80 00 00 00 │•⋄⋄⋄××ו┊⋄⋄⋄××⋄⋄⋄│
│ 00 00 00 00 00 00 00 00 ┊ 00 00 00 00 00 00 00 00 │⋄⋄⋄⋄⋄⋄⋄⋄┊⋄⋄⋄⋄⋄⋄⋄⋄│
│* ┊ │ ┊ │
│* ┊ │ ┊ │
└─────────────────────────┴─────────────────────────┴────────┴────────┘
",
);
}
#[test]
fn squeeze_no_position_one_panel() {
hexyl()
.arg("hello_world_elf64")
.arg("--color=never")
.arg("--skip=1024")
.arg("--length=4096")
.arg("--no-position")
.arg("--panels=1")
.assert()
.success()
.pretty_stdout(
"\
┌─────────────────────────┬────────┐
│ 00 00 00 00 00 00 00 00 │⋄⋄⋄⋄⋄⋄⋄⋄│
│* │ │
│ ba 0e 00 00 00 b9 00 20 │ו⋄⋄⋄×⋄ │
│ 40 00 bb 01 00 00 00 b8 │@⋄ו⋄⋄⋄×│
│ 04 00 00 00 cd 80 b8 01 │•⋄⋄⋄××ו│
│ 00 00 00 cd 80 00 00 00 │⋄⋄⋄××⋄⋄⋄│
│ 00 00 00 00 00 00 00 00 │⋄⋄⋄⋄⋄⋄⋄⋄│
│* │ │
│* │ │
└─────────────────────────┴────────┘
",
);
}
#[test]
fn squeeze_odd_panels_remainder_bytes() {
hexyl()
.arg("hello_world_elf64")
.arg("--color=never")
.arg("--skip=1024")
.arg("--length=4092") // 4 byte remainder
.arg("--panels=3")
.assert()
.success()
.pretty_stdout(
"\
┌────────┬─────────────────────────┬─────────────────────────┬─────────────────────────┬────────┬────────┬────────┐
│00000400│ 00 00 00 00 00 00 00 00 ┊ 00 00 00 00 00 00 00 00 ┊ 00 00 00 00 00 00 00 00 │⋄⋄⋄⋄⋄⋄⋄⋄┊⋄⋄⋄⋄⋄⋄⋄⋄┊⋄⋄⋄⋄⋄⋄⋄⋄│
│* │ ┊ ┊ │ ┊ ┊ │
│00001000│ ba 0e 00 00 00 b9 00 20 ┊ 40 00 bb 01 00 00 00 b8 ┊ 04 00 00 00 cd 80 b8 01 │ו⋄⋄⋄×⋄ ┊@⋄ו⋄⋄⋄×┊•⋄⋄⋄××ו│
│00001018│ 00 00 00 cd 80 00 00 00 ┊ 00 00 00 00 00 00 00 00 ┊ 00 00 00 00 00 00 00 00 │⋄⋄⋄××⋄⋄⋄┊⋄⋄⋄⋄⋄⋄⋄⋄┊⋄⋄⋄⋄⋄⋄⋄⋄│
│00001030│ 00 00 00 00 00 00 00 00 ┊ 00 00 00 00 00 00 00 00 ┊ 00 00 00 00 00 00 00 00 │⋄⋄⋄⋄⋄⋄⋄⋄┊⋄⋄⋄⋄⋄⋄⋄⋄┊⋄⋄⋄⋄⋄⋄⋄⋄│
│* │ ┊ ┊ │ ┊ ┊ │
│000013f0│ 00 00 00 00 00 00 00 00 ┊ 00 00 00 00 ┊ │⋄⋄⋄⋄⋄⋄⋄⋄┊⋄⋄⋄⋄ ┊ │
└────────┴─────────────────────────┴─────────────────────────┴─────────────────────────┴────────┴────────┴────────┘
",
);
}
#[test]
fn squeeze_plain() {
hexyl()
.arg("hello_world_elf64")
.arg("--color=never")
.arg("--skip=1024")
.arg("--length=4096")
.arg("--plain")
.assert()
.success()
.pretty_stdout(
" \
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
*
ba 0e 00 00 00 b9 00 20 40 00 bb 01 00 00 00 b8
04 00 00 00 cd 80 b8 01 00 00 00 cd 80 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
*
*
",
);
}
#[test]
fn squeeze_plain_remainder() {
hexyl()
.arg("hello_world_elf64")
.arg("--color=never")
.arg("--skip=1024")
.arg("--length=4092") // 4 byte remainder
.arg("--plain")
.assert()
.success()
.pretty_stdout(
" \
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
*
ba 0e 00 00 00 b9 00 20 40 00 bb 01 00 00 00 b8
04 00 00 00 cd 80 b8 01 00 00 00 cd 80 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
*
00 00 00 00 00 00 00 00 00 00 00 00
",
);
}
}
mod base {
use super::hexyl;
use super::PrettyAssert;
#[test]
fn base2() {
hexyl()
.arg("ascii")
.arg("--plain")
.arg("--base=binary")
.assert()
.success()
.pretty_stdout(
" 00110000 00110001 00110010 00110011 00110100 00110101 00110110 00110111 \n \
00111000 00111001 01100001 01100010 01100011 01100100 01100101 00001010 \n",
);
}
}
mod character_table {
use super::hexyl;
use super::PrettyAssert;
#[test]
fn ascii() {
hexyl()
.arg("hello_world_elf64")
.arg("--color=never")
.arg("--character-table=ascii")
.assert()
.success()
.pretty_stdout(
"┌────────┬─────────────────────────┬─────────────────────────┬────────┬────────┐
│00000000│ 7f 45 4c 46 02 01 01 00 ┊ 00 00 00 00 00 00 00 00 │.ELF....┊........│
│00000010│ 02 00 3e 00 01 00 00 00 ┊ 00 10 40 00 00 00 00 00 │..>.....┊..@.....│
│00000020│ 40 00 00 00 00 00 00 00 ┊ 28 20 00 00 00 00 00 00 │@.......┊( ......│
│00000030│ 00 00 00 00 40 00 38 00 ┊ 03 00 40 00 04 00 03 00 │....@.8.┊..@.....│
│00000040│ 01 00 00 00 04 00 00 00 ┊ 00 00 00 00 00 00 00 00 │........┊........│
│00000050│ 00 00 40 00 00 00 00 00 ┊ 00 00 40 00 00 00 00 00 │..@.....┊..@.....│
│00000060│ e8 00 00 00 00 00 00 00 ┊ e8 00 00 00 00 00 00 00 │........┊........│
│00000070│ 00 10 00 00 00 00 00 00 ┊ 01 00 00 00 05 00 00 00 │........┊........│
│00000080│ 00 10 00 00 00 00 00 00 ┊ 00 10 40 00 00 00 00 00 │........┊..@.....│
│00000090│ 00 10 40 00 00 00 00 00 ┊ 1d 00 00 00 00 00 00 00 │..@.....┊........│
│000000a0│ 1d 00 00 00 00 00 00 00 ┊ 00 10 00 00 00 00 00 00 │........┊........│
│000000b0│ 01 00 00 00 06 00 00 00 ┊ 00 20 00 00 00 00 00 00 │........┊. ......│
│000000c0│ 00 20 40 00 00 00 00 00 ┊ 00 20 40 00 00 00 00 00 │. @.....┊. @.....│
│000000d0│ 0e 00 00 00 00 00 00 00 ┊ 0e 00 00 00 00 00 00 00 │........┊........│
│000000e0│ 00 10 00 00 00 00 00 00 ┊ 00 00 00 00 00 00 00 00 │........┊........│
│000000f0│ 00 00 00 00 00 00 00 00 ┊ 00 00 00 00 00 00 00 00 │........┊........│
│* │ ┊ │ ┊ │
│00001000│ ba 0e 00 00 00 b9 00 20 ┊ 40 00 bb 01 00 00 00 b8 │....... ┊@.......│
│00001010│ 04 00 00 00 cd 80 b8 01 ┊ 00 00 00 cd 80 00 00 00 │........┊........│
│00001020│ 00 00 00 00 00 00 00 00 ┊ 00 00 00 00 00 00 00 00 │........┊........│
│* │ ┊ │ ┊ │
│00002000│ 48 65 6c 6c 6f 2c 20 77 ┊ 6f 72 6c 64 21 0a 00 2e │Hello, w┊orld!...│
│00002010│ 73 68 73 74 72 74 61 62 ┊ 00 2e 74 65 78 74 00 2e │shstrtab┊..text..│
│00002020│ 64 61 74 61 00 00 00 00 ┊ 00 00 00 00 00 00 00 00 │data....┊........│
│00002030│ 00 00 00 00 00 00 00 00 ┊ 00 00 00 00 00 00 00 00 │........┊........│
│* │ ┊ │ ┊ │
│00002060│ 00 00 00 00 00 00 00 00 ┊ 0b 00 00 00 01 00 00 00 │........┊........│
│00002070│ 06 00 00 00 00 00 00 00 ┊ 00 10 40 00 00 00 00 00 │........┊..@.....│
│00002080│ 00 10 00 00 00 00 00 00 ┊ 1d 00 00 00 00 00 00 00 │........┊........│
│00002090│ 00 00 00 00 00 00 00 00 ┊ 10 00 00 00 00 00 00 00 │........┊........│
│000020a0│ 00 00 00 00 00 00 00 00 ┊ 11 00 00 00 01 00 00 00 │........┊........│
│000020b0│ 03 00 00 00 00 00 00 00 ┊ 00 20 40 00 00 00 00 00 │........┊. @.....│
│000020c0│ 00 20 00 00 00 00 00 00 ┊ 0e 00 00 00 00 00 00 00 │. ......┊........│
│000020d0│ 00 00 00 00 00 00 00 00 ┊ 04 00 00 00 00 00 00 00 │........┊........│
│000020e0│ 00 00 00 00 00 00 00 00 ┊ 01 00 00 00 03 00 00 00 │........┊........│
│000020f0│ 00 00 00 00 00 00 00 00 ┊ 00 00 00 00 00 00 00 00 │........┊........│
│00002100│ 0e 20 00 00 00 00 00 00 ┊ 17 00 00 00 00 00 00 00 │. ......┊........│
│00002110│ 00 00 00 00 00 00 00 00 ┊ 01 00 00 00 00 00 00 00 │........┊........│
│00002120│ 00 00 00 00 00 00 00 00 ┊ │........┊ │
└────────┴─────────────────────────┴─────────────────────────┴────────┴────────┘
",
);
}
#[test]
fn codepage_437() {
hexyl()
.arg("hello_world_elf64")
.arg("--color=never")
.arg("--character-table=codepage-437")
.assert()
.success()
.pretty_stdout(
"┌────────┬─────────────────────────┬─────────────────────────┬────────┬────────┐
│00000000│ 7f 45 4c 46 02 01 01 00 ┊ 00 00 00 00 00 00 00 00 │⌂ELF☻☺☺⋄┊⋄⋄⋄⋄⋄⋄⋄⋄│
│00000010│ 02 00 3e 00 01 00 00 00 ┊ 00 10 40 00 00 00 00 00 │☻⋄>⋄☺⋄⋄⋄┊⋄►@⋄⋄⋄⋄⋄│
│00000020│ 40 00 00 00 00 00 00 00 ┊ 28 20 00 00 00 00 00 00 │@⋄⋄⋄⋄⋄⋄⋄┊( ⋄⋄⋄⋄⋄⋄│
│00000030│ 00 00 00 00 40 00 38 00 ┊ 03 00 40 00 04 00 03 00 │⋄⋄⋄⋄@⋄8⋄┊♥⋄@⋄♦⋄♥⋄│
│00000040│ 01 00 00 00 04 00 00 00 ┊ 00 00 00 00 00 00 00 00 │☺⋄⋄⋄♦⋄⋄⋄┊⋄⋄⋄⋄⋄⋄⋄⋄│
│00000050│ 00 00 40 00 00 00 00 00 ┊ 00 00 40 00 00 00 00 00 │⋄⋄@⋄⋄⋄⋄⋄┊⋄⋄@⋄⋄⋄⋄⋄│
│00000060│ e8 00 00 00 00 00 00 00 ┊ e8 00 00 00 00 00 00 00 │Φ⋄⋄⋄⋄⋄⋄⋄┊Φ⋄⋄⋄⋄⋄⋄⋄│
│00000070│ 00 10 00 00 00 00 00 00 ┊ 01 00 00 00 05 00 00 00 │⋄►⋄⋄⋄⋄⋄⋄┊☺⋄⋄⋄♣⋄⋄⋄│
│00000080│ 00 10 00 00 00 00 00 00 ┊ 00 10 40 00 00 00 00 00 │⋄►⋄⋄⋄⋄⋄⋄┊⋄►@⋄⋄⋄⋄⋄│
│00000090│ 00 10 40 00 00 00 00 00 ┊ 1d 00 00 00 00 00 00 00 │⋄►@⋄⋄⋄⋄⋄┊↔⋄⋄⋄⋄⋄⋄⋄│
│000000a0│ 1d 00 00 00 00 00 00 00 ┊ 00 10 00 00 00 00 00 00 │↔⋄⋄⋄⋄⋄⋄⋄┊⋄►⋄⋄⋄⋄⋄⋄│
│000000b0│ 01 00 00 00 06 00 00 00 ┊ 00 20 00 00 00 00 00 00 │☺⋄⋄⋄♠⋄⋄⋄┊⋄ ⋄⋄⋄⋄⋄⋄│
│000000c0│ 00 20 40 00 00 00 00 00 ┊ 00 20 40 00 00 00 00 00 │⋄ @⋄⋄⋄⋄⋄┊⋄ @⋄⋄⋄⋄⋄│
│000000d0│ 0e 00 00 00 00 00 00 00 ┊ 0e 00 00 00 00 00 00 00 │♫⋄⋄⋄⋄⋄⋄⋄┊♫⋄⋄⋄⋄⋄⋄⋄│
│000000e0│ 00 10 00 00 00 00 00 00 ┊ 00 00 00 00 00 00 00 00 │⋄►⋄⋄⋄⋄⋄⋄┊⋄⋄⋄⋄⋄⋄⋄⋄│
│000000f0│ 00 00 00 00 00 00 00 00 ┊ 00 00 00 00 00 00 00 00 │⋄⋄⋄⋄⋄⋄⋄⋄┊⋄⋄⋄⋄⋄⋄⋄⋄│
│* │ ┊ │ ┊ │
│00001000│ ba 0e 00 00 00 b9 00 20 ┊ 40 00 bb 01 00 00 00 b8 │║♫⋄⋄⋄╣⋄ ┊@⋄╗☺⋄⋄⋄╕│
│00001010│ 04 00 00 00 cd 80 b8 01 ┊ 00 00 00 cd 80 00 00 00 │♦⋄⋄⋄═Ç╕☺┊⋄⋄⋄═Ç⋄⋄⋄│
│00001020│ 00 00 00 00 00 00 00 00 ┊ 00 00 00 00 00 00 00 00 │⋄⋄⋄⋄⋄⋄⋄⋄┊⋄⋄⋄⋄⋄⋄⋄⋄│
│* │ ┊ │ ┊ │
│00002000│ 48 65 6c 6c 6f 2c 20 77 ┊ 6f 72 6c 64 21 0a 00 2e │Hello, w┊orld!◙⋄.│
│00002010│ 73 68 73 74 72 74 61 62 ┊ 00 2e 74 65 78 74 00 2e │shstrtab┊⋄.text⋄.│
│00002020│ 64 61 74 61 00 00 00 00 ┊ 00 00 00 00 00 00 00 00 │data⋄⋄⋄⋄┊⋄⋄⋄⋄⋄⋄⋄⋄│
│00002030│ 00 00 00 00 00 00 00 00 ┊ 00 00 00 00 00 00 00 00 │⋄⋄⋄⋄⋄⋄⋄⋄┊⋄⋄⋄⋄⋄⋄⋄⋄│
│* │ ┊ │ ┊ │
│00002060│ 00 00 00 00 00 00 00 00 ┊ 0b 00 00 00 01 00 00 00 │⋄⋄⋄⋄⋄⋄⋄⋄┊♂⋄⋄⋄☺⋄⋄⋄│
│00002070│ 06 00 00 00 00 00 00 00 ┊ 00 10 40 00 00 00 00 00 │♠⋄⋄⋄⋄⋄⋄⋄┊⋄►@⋄⋄⋄⋄⋄│
│00002080│ 00 10 00 00 00 00 00 00 ┊ 1d 00 00 00 00 00 00 00 │⋄►⋄⋄⋄⋄⋄⋄┊↔⋄⋄⋄⋄⋄⋄⋄│
│00002090│ 00 00 00 00 00 00 00 00 ┊ 10 00 00 00 00 00 00 00 │⋄⋄⋄⋄⋄⋄⋄⋄┊►⋄⋄⋄⋄⋄⋄⋄│
│000020a0│ 00 00 00 00 00 00 00 00 ┊ 11 00 00 00 01 00 00 00 │⋄⋄⋄⋄⋄⋄⋄⋄┊◄⋄⋄⋄☺⋄⋄⋄│
│000020b0│ 03 00 00 00 00 00 00 00 ┊ 00 20 40 00 00 00 00 00 │♥⋄⋄⋄⋄⋄⋄⋄┊⋄ @⋄⋄⋄⋄⋄│
│000020c0│ 00 20 00 00 00 00 00 00 ┊ 0e 00 00 00 00 00 00 00 │⋄ ⋄⋄⋄⋄⋄⋄┊♫⋄⋄⋄⋄⋄⋄⋄│
│000020d0│ 00 00 00 00 00 00 00 00 ┊ 04 00 00 00 00 00 00 00 │⋄⋄⋄⋄⋄⋄⋄⋄┊♦⋄⋄⋄⋄⋄⋄⋄│
│000020e0│ 00 00 00 00 00 00 00 00 ┊ 01 00 00 00 03 00 00 00 │⋄⋄⋄⋄⋄⋄⋄⋄┊☺⋄⋄⋄♥⋄⋄⋄│
│000020f0│ 00 00 00 00 00 00 00 00 ┊ 00 00 00 00 00 00 00 00 │⋄⋄⋄⋄⋄⋄⋄⋄┊⋄⋄⋄⋄⋄⋄⋄⋄│
│00002100│ 0e 20 00 00 00 00 00 00 ┊ 17 00 00 00 00 00 00 00 │♫ ⋄⋄⋄⋄⋄⋄┊↨⋄⋄⋄⋄⋄⋄⋄│
│00002110│ 00 00 00 00 00 00 00 00 ┊ 01 00 00 00 00 00 00 00 │⋄⋄⋄⋄⋄⋄⋄⋄┊☺⋄⋄⋄⋄⋄⋄⋄│
│00002120│ 00 00 00 00 00 00 00 00 ┊ │⋄⋄⋄⋄⋄⋄⋄⋄┊ │
└────────┴─────────────────────────┴─────────────────────────┴────────┴────────┘
",
);
}
#[test]
fn codepage_1047() {
hexyl()
.arg("hello_world_elf64")
.arg("--color=never")
.arg("--character-table=codepage-1047")
.assert()
.success()
.pretty_stdout(
"┌────────┬─────────────────────────┬─────────────────────────┬────────┬────────┐
│00000000│ 7f 45 4c 46 02 01 01 00 ┊ 00 00 00 00 00 00 00 00 │..<.....┊........│
│00000010│ 02 00 3e 00 01 00 00 00 ┊ 00 10 40 00 00 00 00 00 │........┊.. .....│
│00000020│ 40 00 00 00 00 00 00 00 ┊ 28 20 00 00 00 00 00 00 │ .......┊........│
│00000030│ 00 00 00 00 40 00 38 00 ┊ 03 00 40 00 04 00 03 00 │.... ...┊.. .....│
│00000040│ 01 00 00 00 04 00 00 00 ┊ 00 00 00 00 00 00 00 00 │........┊........│
│00000050│ 00 00 40 00 00 00 00 00 ┊ 00 00 40 00 00 00 00 00 │.. .....┊.. .....│
│00000060│ e8 00 00 00 00 00 00 00 ┊ e8 00 00 00 00 00 00 00 │Y.......┊Y.......│
│00000070│ 00 10 00 00 00 00 00 00 ┊ 01 00 00 00 05 00 00 00 │........┊........│
│00000080│ 00 10 00 00 00 00 00 00 ┊ 00 10 40 00 00 00 00 00 │........┊.. .....│
│00000090│ 00 10 40 00 00 00 00 00 ┊ 1d 00 00 00 00 00 00 00 │.. .....┊........│
│000000a0│ 1d 00 00 00 00 00 00 00 ┊ 00 10 00 00 00 00 00 00 │........┊........│
│000000b0│ 01 00 00 00 06 00 00 00 ┊ 00 20 00 00 00 00 00 00 │........┊........│
│000000c0│ 00 20 40 00 00 00 00 00 ┊ 00 20 40 00 00 00 00 00 │.. .....┊.. .....│
│000000d0│ 0e 00 00 00 00 00 00 00 ┊ 0e 00 00 00 00 00 00 00 │........┊........│
│000000e0│ 00 10 00 00 00 00 00 00 ┊ 00 00 00 00 00 00 00 00 │........┊........│
│000000f0│ 00 00 00 00 00 00 00 00 ┊ 00 00 00 00 00 00 00 00 │........┊........│
│* │ ┊ │ ┊ │
│00001000│ ba 0e 00 00 00 b9 00 20 ┊ 40 00 bb 01 00 00 00 b8 │[.......┊ .].....│
│00001010│ 04 00 00 00 cd 80 b8 01 ┊ 00 00 00 cd 80 00 00 00 │........┊........│
│00001020│ 00 00 00 00 00 00 00 00 ┊ 00 00 00 00 00 00 00 00 │........┊........│
│* │ ┊ │ ┊ │
│00002000│ 48 65 6c 6c 6f 2c 20 77 ┊ 6f 72 6c 64 21 0a 00 2e │..%%?...┊?.%.....│
│00002010│ 73 68 73 74 72 74 61 62 ┊ 00 2e 74 65 78 74 00 2e │....../.┊........│
│00002020│ 64 61 74 61 00 00 00 00 ┊ 00 00 00 00 00 00 00 00 │././....┊........│
│00002030│ 00 00 00 00 00 00 00 00 ┊ 00 00 00 00 00 00 00 00 │........┊........│
│* │ ┊ │ ┊ │
│00002060│ 00 00 00 00 00 00 00 00 ┊ 0b 00 00 00 01 00 00 00 │........┊........│
│00002070│ 06 00 00 00 00 00 00 00 ┊ 00 10 40 00 00 00 00 00 │........┊.. .....│
│00002080│ 00 10 00 00 00 00 00 00 ┊ 1d 00 00 00 00 00 00 00 │........┊........│
│00002090│ 00 00 00 00 00 00 00 00 ┊ 10 00 00 00 00 00 00 00 │........┊........│
│000020a0│ 00 00 00 00 00 00 00 00 ┊ 11 00 00 00 01 00 00 00 │........┊........│
│000020b0│ 03 00 00 00 00 00 00 00 ┊ 00 20 40 00 00 00 00 00 │........┊.. .....│
│000020c0│ 00 20 00 00 00 00 00 00 ┊ 0e 00 00 00 00 00 00 00 │........┊........│
│000020d0│ 00 00 00 00 00 00 00 00 ┊ 04 00 00 00 00 00 00 00 │........┊........│
│000020e0│ 00 00 00 00 00 00 00 00 ┊ 01 00 00 00 03 00 00 00 │........┊........│
│000020f0│ 00 00 00 00 00 00 00 00 ┊ 00 00 00 00 00 00 00 00 │........┊........│
│00002100│ 0e 20 00 00 00 00 00 00 ┊ 17 00 00 00 00 00 00 00 │........┊........│
│00002110│ 00 00 00 00 00 00 00 00 ┊ 01 00 00 00 00 00 00 00 │........┊........│
│00002120│ 00 00 00 00 00 00 00 00 ┊ │........┊ │
└────────┴─────────────────────────┴─────────────────────────┴────────┴────────┘
",
);
}
}
mod colors {
use super::hexyl;
use owo_colors::{colors, Color};
use std::collections::HashMap;
// This is a helper for testing color in output. Writing tests to expect
// raw color codes is ugly and hard to look at. Loading expected output
// from files works fine, but you end up with a lot of files for all the
// tests, and you have to cross-reference the file with the test that uses
// it. The files also suffer from the same problem of being hard to
// visually inspect. Just catting the file to see the colorized output
// loses the nuance of where exactly the color codes appear (before or
// after spaces, for example), or whether there are redundant codes.
//
// So this ColorMap solves the problem neatly by having two inputs:
// - the easy to read expected output in plain format without any colors
// - a mapping with identical structure except some characters replaced
// with single character color codes.
// This makes it easy to reference the output and expected colors side by
// side, and provides fairly precise control over exactly where color codes
// are expected (the only caveat being you can't have two color codes back
// to back). ColorMap combines these into the actual expected output.
//
// The color mapping needs to be identical to the expected output, except
// it has some chars replaced by color code stand ins. These are replaced
// with the actual color codes by the colorize method. The '.' character
// is also ignored (it doesn't need to match the input). This makes the
// color map more readable and avoids input characters from conflicting
// with color chars.
struct ColorMap {
text_map: &'static str,
char_to_color: HashMap<char, &'static str>,
}
impl ColorMap {
fn from(text_map: &'static str) -> Self {
ColorMap {
text_map,
char_to_color: HashMap::new(),
}
}
fn with<C: Color>(&mut self, c: char) -> &mut Self {
self.char_to_color.insert(c, C::ANSI_FG);
self
}
fn colorize(&self, input: &str) -> String {
let mut output = String::new();
let mut input_chars = input.chars();
for c in self.text_map.chars() {
let next_input = input_chars.next().expect("input and color map don't match");
if let Some(color) = self.char_to_color.get(&c) {
output.push_str(color);
} else if c != '.' {
// ignore '.' in the mapping for readability
assert_eq!(c, next_input, "input and color map don't match");
}
output.push(next_input);
}
output
}
}
#[test]
fn hex_colors() {
let input = b"He\x11\0 \xff\0\xdd";
let expected_text = "\
┌────────┬─────────────────────────┬─────────────────────────┬────────┬────────┐\n\
│00000000│ 48 65 11 00 20 ff 00 dd ┊ │He•⋄ ×⋄×┊ │\n\
└────────┴─────────────────────────┴─────────────────────────┴────────┴────────┘\n";
let expected = ColorMap::from(
"\
┌────────┬─────────────────────────┬─────────────────────────┬────────┬────────┐\n\
│r.......d y. .. b. c. g. m. c. m.d┊ d│y.bcgmcmd d\n\
└────────┴─────────────────────────┴─────────────────────────┴────────┴────────┘\n",
)
.with::<colors::Red>('r')
.with::<colors::Default>('d')
.with::<colors::Yellow>('y')
.with::<colors::Blue>('b')
.with::<colors::Green>('g')
.with::<colors::BrightMagenta>('m')
.with::<colors::CustomColor<0xab, 0xcd, 0xef>>('c')
.colorize(expected_text);
hexyl()
.write_stdin(input)
.arg("--color=always")
.env("HEXYL_COLOR_OFFSET", "red")
.env("HEXYL_COLOR_ASCII_PRINTABLE", "yellow")
.env("HEXYL_COLOR_ASCII_WHITESPACE", "green")
.env("HEXYL_COLOR_ASCII_OTHER", "blue")
.env("HEXYL_COLOR_NONASCII", "bright magenta")
.env("HEXYL_COLOR_NULL", "#abcdef")
.assert()
.success()
.stdout(expected);
}
#[test]
fn binary_colors() {
let input = b"He\x11\0 \xff\0\xdd";
let expected_text = "\
┌────────┬─────────────────────────────────────────────────────────────────────────┬────────┐\n\
│00000000│ 01001000 01100101 00010001 00000000 00100000 11111111 00000000 11011101 │He•⋄ ×⋄×│\n\
└────────┴─────────────────────────────────────────────────────────────────────────┴────────┘\n";
let expected = ColorMap::from(
"\
┌────────┬─────────────────────────────────────────────────────────────────────────┬────────┐\n\
│r.......d y....... ........ b....... c....... g....... m....... c....... m.......d│y.bcgmcmd\n\
└────────┴─────────────────────────────────────────────────────────────────────────┴────────┘\n"
)
.with::<colors::Red>('r')
.with::<colors::Default>('d')
.with::<colors::Yellow>('y')
.with::<colors::Blue>('b')
.with::<colors::Green>('g')
.with::<colors::BrightMagenta>('m')
.with::<colors::CustomColor<0xab, 0xcd, 0xef>>('c')
.colorize(expected_text);
hexyl()
.write_stdin(input)
.arg("--color=always")
.arg("--panels=1")
.arg("--base=binary")
.env("HEXYL_COLOR_OFFSET", "red")
.env("HEXYL_COLOR_ASCII_PRINTABLE", "yellow")
.env("HEXYL_COLOR_ASCII_WHITESPACE", "green")
.env("HEXYL_COLOR_ASCII_OTHER", "blue")
.env("HEXYL_COLOR_NONASCII", "bright magenta")
.env("HEXYL_COLOR_NULL", "#abcdef")
.assert()
.success()
.stdout(expected);
}
#[test]
fn groupsize_colors() {
let input = b"He\x11\0 \xff\0\xdd";
let expected_text = "\
┌────────┬─────────────────────┬────────┐\n\
│00000000│ 4865 1100 20ff 00dd │He•⋄ ×⋄×│\n\
└────────┴─────────────────────┴────────┘\n";
let expected = ColorMap::from(
"\
┌────────┬─────────────────────┬────────┐\n\
│r.......d y... b.c. g.m. c.m.d│y.bcgmcmd\n\
└────────┴─────────────────────┴────────┘\n",
)
.with::<colors::Red>('r')
.with::<colors::Default>('d')
.with::<colors::Yellow>('y')
.with::<colors::Blue>('b')
.with::<colors::Green>('g')
.with::<colors::BrightMagenta>('m')
.with::<colors::CustomColor<0xab, 0xcd, 0xef>>('c')
.colorize(expected_text);
hexyl()
.write_stdin(input)
.arg("--color=always")
.arg("--panels=1")
.arg("--groupsize=2")
.env("HEXYL_COLOR_OFFSET", "red")
.env("HEXYL_COLOR_ASCII_PRINTABLE", "yellow")
.env("HEXYL_COLOR_ASCII_WHITESPACE", "green")
.env("HEXYL_COLOR_ASCII_OTHER", "blue")
.env("HEXYL_COLOR_NONASCII", "bright magenta")
.env("HEXYL_COLOR_NULL", "#abcdef")
.assert()
.success()
.stdout(expected);
}
}