135 Commits
Author SHA1 Message Date
David Peter becc75caf1 Fix #254 2026-02-14 13:42:51 +01:00
Sharif Haason cc9476d665 Return a proper error if terminal size fails 2022-07-17 15:53:56 -04:00
Sharif Haason ab381e88f8 Change columns type to u16 2022-07-17 15:53:23 -04:00
Sharif Haason 361ded43fd Remove debugging line 2022-07-17 15:48:05 -04:00
Sharif Haason 20056fb4d9 Bump MSRV 2022-07-17 15:26:43 -04:00
Sharif Haason 34c55b5e55 Implement -a and -t for terminal width setting 2022-07-17 15:20:00 -04:00
Sharif Haason f4c4b22c57 Added test for multiple columns 2022-07-17 13:37:25 -04:00
Sharif Haason 98c65d94e7 Prevent overflow when columns >= 32 2022-07-17 11:31:46 -04:00
sharifandDavid Peter d74520fbab Update src/squeezer.rs
Co-authored-by: David Peter <sharkdp@users.noreply.github.com>
2022-07-17 08:54:24 -04:00
Sharif Haason c8b05ff96f cargo fmt 2022-07-12 11:49:50 -04:00
Sharif Haason 734365723b Adds --columns for Print using custom width #13 2022-07-12 11:39:24 -04:00
Giovanni Tirloni 55cf7b034c Add Fedora install instructions 2022-06-23 21:56:49 +02:00
Vadym-Valdis Yudaiev c622c8217f doc: Add example usage with terminal pager 2022-05-24 18:57:21 +02:00
David Peter 49484fa0f0 Bump MSRV 2022-05-22 22:26:59 +02:00
David Peter 3661b06b60 Update CHANGELOG 2022-05-22 22:24:47 +02:00
David Peter f5814d4442 Update version 2022-05-22 22:23:25 +02:00
David Peter 49ebf4771f Update to clap 3 2022-05-22 22:21:49 +02:00
David Peter 4904a7e26f Relax constraints 2022-05-22 21:53:26 +02:00
David Peter becc1b80f1 Update dependencies 2022-05-22 21:52:44 +02:00
David Peter 1fb5263c3b Fix clippy suggestions 2022-05-22 21:48:40 +02:00
David Peter 0ae89168ad Update CHANGELOG 2022-05-22 21:39:42 +02:00
Mikhail Katychev 6873867658 table -> panel 2022-05-22 21:34:26 +02:00
Mikhail Katychev f15e049bf5 added proposed table suffix to no_chars and no_position 2022-05-22 21:34:26 +02:00
Mikhail Katychev 1d04cdf119 bump min version 2022-05-22 21:34:26 +02:00
Mikhail Katychev 5d92f07e21 cargo clippy warning 2022-05-22 21:34:26 +02:00
Mikhail Katychev a4ed50e1f0 clippy feedback 2022-05-22 21:34:26 +02:00
Mikhail Katychev f7f7447994 added --plain functionality 2022-05-22 21:34:26 +02:00
cuishuang e4f0e8a092 fix some typos
Signed-off-by: cuishuang <imcusg@gmail.com>
2022-05-18 20:10:29 +02:00
Shun Sakai a23645ddfd Add man page 2022-03-27 10:22:14 +02:00
Shun Sakai 7da04c6bff Change -l to conflict with -n and -c 2022-03-25 22:00:40 +01:00
Shun Sakai c497c1fac9 Fix Clippy warnings 2022-03-25 22:00:40 +01:00
Shun Sakai 653cf02954 Add rustfmt and clippy to the CI 2022-03-25 22:00:40 +01:00
David Peter 18cdbe02c5 Fix Debian package description 2021-11-14 09:36:35 +01:00
merkrafter d1ae68585f Make code compatible with rust 1.46 2021-11-14 09:26:21 +01:00
merkrafter 7a225fe732 Apply clippy suggestions 2021-11-14 09:26:21 +01:00
merkrafter 1b33e516d9 Move hex number processing into own function 2021-11-14 09:26:21 +01:00
merkrafter 55d02c933a Move sign (+/-) processing into own function 2021-11-14 09:26:21 +01:00
merkrafter b8ed8d5df9 Raise minimum Rust version for executable to 1.46 2021-11-14 09:26:21 +01:00
merkrafter 641c32e395 Mention new feature in CHANGELOG 2021-11-14 09:26:21 +01:00
merkrafter 51b28ed0b0 Allow hex numbers for --block-size 2021-11-14 09:26:21 +01:00
merkrafter 825100c6d6 Deny 'block' as unit for --block-size 2021-11-14 09:26:21 +01:00
merkrafter 1b1fc4b053 Allow units for --block-size 2021-11-14 09:26:21 +01:00
merkrafter c3dd4e871e Use extraction function for length arg parsing 2021-11-14 09:26:21 +01:00
merkrafter 923bceffaa Add function to extract num and unit from string
Having this as an own function allows special handling of the
'block' unit in --block-size argument.
2021-11-14 09:26:21 +01:00
merkrafter 2942d2419f Introduce enum for byte size units 2021-11-14 09:26:21 +01:00
merkrafter 37d4180f9a Refactor default block size into constant 2021-11-14 09:26:21 +01:00
Mark Umnus 99435cd215 Mention ability to specify length in hex 2021-10-20 22:52:01 +02:00
Issac Lin 27f039d72a README: Add scoop instruction (on Windows) 2021-09-20 08:41:25 +02:00
a1346054 e72422ecfb Trim trailing whitespace 2021-08-28 09:56:56 +02:00
a1346054 3c56ed2b0e Fix typos 2021-08-28 09:56:56 +02:00
a1346054 a09ea8ee27 Use HTTPS links 2021-08-28 09:56:56 +02:00
David Peter d6bdfe9897 Update package links 2021-08-25 22:17:21 +02:00
HRXN 7c9f7e1bd0 README.md does not mention availability of binary builds for Windows
Could not check this, but release page lists Windows binaries, apparently.
2021-07-12 07:15:41 +02:00
David Peter b1d2a47590 Remove old ci/ directory 2021-07-11 22:03:19 +02:00
David Peter c3e9cb9518 Bump MSRV 2021-07-11 20:09:28 +02:00
David Peter 3489998c67 cargo fmt 2021-07-11 20:09:28 +02:00
David Peter 4594d29f73 Bump version to 0.9.0 2021-07-11 20:09:28 +02:00
David Peter cc73f01c34 Update dependencies 2021-07-11 20:09:28 +02:00
David Peter 6da15cd1e9 Added alias notice in help text 2021-07-11 19:57:55 +02:00
David Peter c82ccd5a59 Add CHANGELOG entry 2021-07-11 19:57:55 +02:00
TheDoctor314 19d2805729 Add alias '-l' for '-n/--length'
Set `.hidden(true)` option

This won't clutter the help text too much.
2021-07-11 19:57:55 +02:00
David Peter 06387bba3e Add GitHub actions badge 2021-07-11 19:16:53 +02:00
David Peter 595803e5e2 Migrate to GitHub actions 2021-07-11 19:16:06 +02:00
Herby Gillot 3793559d6f README: add MacPorts instructions 2021-06-07 19:00:11 +02:00
Mihir Gadgil 71626c28e9 Update CHANGELOG.md 2021-05-07 10:05:16 +02:00
Mihir Gadgil 532eab762d Fix #131
Don't print the error for version output, instead add a newline print.
Move the eprint for help only.
2021-05-07 10:05:16 +02:00
dm9pZCAq 642eff6061 README: added Gentoo instruction 2021-02-13 20:36:44 +01:00
Gabriel Arazas 15dc96a946 Add installation instructions for Guix 2020-11-25 12:18:16 +01:00
sharkdp 6b2cc80cc1 Change ASCII file to contain increasing hex digits 2020-11-01 22:56:59 +01:00
sharkdp 1db77094da Organize integration tests into modules 2020-11-01 22:56:59 +01:00
sharkdp 8c3f2230c6 Better error message when seek fails
> hexyl "$(which hexyl)" --skip=-5gib
    Error: 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).

ref: #114
2020-11-01 22:56:59 +01:00
sharkdp 66f8508b57 Add stdin test 2020-11-01 21:35:35 +01:00
sharkdp afb249e854 Run cargo fmt 2020-11-01 21:04:39 +01:00
sharkdp 9432677d3a Disallow block sizes of 0
closes #110
2020-10-31 10:17:19 +01:00
Tom Milligan 524a6c607f bin: offset should add to skip, not override it 2020-10-31 10:00:03 +01:00
sharkdp d69a7d8e04 Update CHANGELOG 2020-10-30 21:58:02 +01:00
sharkdp b1c8606918 Print warning on empty content
closes #107
closes #108
2020-10-30 21:58:02 +01:00
sharkdp d9723592bf Add CONTRIBUTING guide 2020-10-30 20:04:08 +01:00
sharkdp 23fbb5b027 Update CHANGELOG 2020-10-30 20:02:16 +01:00
sharkdp 4d7004f06b Move CHANGELOG entry 2020-10-30 19:57:07 +01:00
purvesh-linux11 c4ed6d9936 add snapstore link to readme 2020-10-30 19:56:44 +01:00
purvesh-linux11 aab0596ea9 add snap information in changelog 2020-10-30 19:56:44 +01:00
purvesh-linux11 7500efffa5 add snap installation steps in redme 2020-10-30 19:56:44 +01:00
sharkdp 80e8026416 Update CHANGELOG 2020-10-30 19:00:36 +01:00
David Peter 13452f5174 Fix .gitattributes
Branch: fix-105
2020-10-30 10:07:24 +01:00
sharkdp 48ff6ff956 Bump min. required Rust version to 1.39 2020-10-30 10:07:24 +01:00
sharkdp 54cab89821 Force LF line ending to fix tests on Windows 2020-10-30 10:07:24 +01:00
sharkdp bb6a3c0650 Add test for --length, fix #105 2020-10-30 10:07:24 +01:00
sharkdp 09c22e09b9 Add simple integration tests 2020-10-30 10:07:24 +01:00
suchenguo cc5b308fc9 two New Installation ways
I added two new installation ways of Termux.
2020-08-17 21:25:26 +02:00
Erich Gubler 153b1cccdf feat: make signs significant for byte offsets 2020-08-15 14:11:10 +02:00
sharkdp 7217a60e6e Switch back to MSVC 2020-08-15 14:05:24 +02:00
Erich Gubler 13aec946a3 improvement: make diagnostics for flags involving byte count parsing actually good
This adds an error type called `ByteCountParseError` with a vanilla
`std::error::Error` implementation. The code could be significantly
compressed with a dependency like `thiserror`.

I opted to implement a separate error type for `parse_byte_count` rather
than using `anyhow`'s small ecosystem of error types there (though they
are used in `<binary>::run`) because it allows pairing diagnostic
results with tests, incl. those already written. This can be helpful in
preventing regressions in error diagnostics.
2020-06-17 18:05:42 +02:00
Erich Gubler e0da7b3f82 style: normalize indentation of doc strings literals for args help 2020-06-17 18:05:42 +02:00
Erich Gubler e29a63358d improvement: elide type of reader 2020-06-17 18:05:42 +02:00
Erich Gubler 2bdf5d768f feat: use anyhow for more structured error messages and better error reporting flexibility
This significantly changes the structure of potential output of
top-level errors.  Before, errors that have a `source` were printed on a
single line, with the `source` omitted. Now, an error with a `source`
will be printed like:

```
Error: <`Display` impl of error>

Caused by:
    <`Display` impl of `source` error>
```

If there are multiple `source` errors in a chain, then it will be of the
form:

```
Error: <`Display` impl of error>

Caused by:
    0: <`Display` impl of first `source` error>
    1: <`Display` impl of `source` of `source` error>
    # etc.
```

Now, in practice this never happened before, since the only error
type that ever could return from `<binary>::run` was `std::io::Error`,
which has no `source`. However, `source`s can start being added if, say,
future work involved using `anyhow::Context::context` or custom errors
that implement `std::Error::source` are added to the codebase. I believe
this is a good choice going forward, but I definitely want to foster
discussion and acceptance for it in the open first!
2020-06-17 18:05:42 +02:00
sharkdp a7892881f3 Wrap help text at 90 characters 2020-06-08 23:19:56 +02:00
sharkdp 921a138962 Update version in README 2020-06-07 15:57:20 +02:00
sharkdp f8f410b8ee Simplify is_printable check, closes #78 2020-06-07 15:26:07 +02:00
sharkdp 7307077a66 Run lib example on TravisCI 2020-06-07 15:14:14 +02:00
sharkdp 3ca002638c Improve help text 2020-06-07 15:12:35 +02:00
sharkdp ccfceaf58c Minor formatting 2020-06-07 15:12:35 +02:00
sharkdp 751b670d5b Fix clippy suggestions 2020-06-07 15:12:35 +02:00
sharkdp 478cc25ffd Bump version to 0.8.0 2020-06-07 15:12:35 +02:00
sharkdp b1c4b0bf2b Update dependencies 2020-06-07 15:12:35 +02:00
sharkdp 1951c59b0c Add CHANGELOG 2020-06-07 15:12:35 +02:00
Erich Gubler ddc3a5c04a BREAKING CHANGE: improvement: change byte counts to be a positive i64 (with newtype PositiveI64) instead of a u64 to take advantage of new skip impl for --seek with non-Seek Inputs like STDIN
This is technically a breaking change because it halves the upper range
of accepted values for byte counts. I thought that fine under the
assumption it will be rare for users to ever want to specify something
in the upper half of `u64`'s value range, especially considering that
this can pave the way for having negative byte offsets (instead of just
counts).
2020-06-07 11:08:01 +02:00
Erich Gubler 6d5c61b671 BREAKING CHANGE: improvement(lib): use u64 for Printer::display_offset and Squeezer::process, not usize
Technically a breaking change for architectures where
`size_of::<usize>() > size_of::<u64>()`, but that strikes me as
extremely unlikely.

This change is important because most file seek offsets, even on older
architectures with word sizes smaller than `u32`, accept a `u64`. In
particular, Rust's own `std` acknowledges this. So, we're actually going
against prior art in Rust by using a `usize`.
2020-06-07 11:08:01 +02:00
Erich Gubler c5c8d1190f BREAKING CHANGE: feat(lib): skip forward when relative forward seeks are specified on non-seekable streams
This is technically a breaking change, since the data and behavior of
`Input` is exposed publicly. However, it seems unlikely to me that
clients actually depended on a failure in the case that's being changed.
2020-06-07 11:08:01 +02:00
Erich Gubler 1459616d27 improvement: use or instead of unwrap_or_else for display_offset 2020-06-07 11:08:01 +02:00
Erich Gubler 8af8a94699 improvement(lib): align string literals better for black-box tests 2020-05-28 18:44:23 +02:00
Erich Gubler 07cdc97e74 improvement: replace trim_start_matches with slicing op 2020-05-28 18:43:53 +02:00
Erich Gubler bcca1fae03 feat: add block unit and block-size option 2020-05-28 18:43:53 +02:00
Erich Gubler 48ad95efe6 feat: add units system to decimals with {k,m,g,t}[[i]b] support 2020-05-28 18:43:53 +02:00
Erich Gubler 3b3889967c refactor: extract "0x" literal into HEX_PREFIX 2020-05-28 18:43:53 +02:00
Erich Gubler 57f1fb4dc4 BREAKING CHANGE: improvement: disallow leading '+' after "0x" prefix for u64 parse 2020-05-28 18:43:53 +02:00
Erich Gubler ef40be0d53 improvement: fix or_fun_call lint from clippy 2020-05-28 18:43:53 +02:00
Erich Gubler 6b235b1d2e improvement: remove unused atty import 2020-05-28 18:43:53 +02:00
notramo 7cff10b274 Added Void Linux package install to README 2020-05-26 20:03:09 +02:00
Mario Reder 5327b71215 fix review comments 2020-05-13 18:55:24 +02:00
Mario Reder c336259159 skip option error messages and display offset 2020-05-13 18:55:24 +02:00
sharkdp 133fdd9156 Remove unnecessary RefCell, backport to Rust 1.36 2020-05-13 18:55:24 +02:00
Mario Reder 244f71691d feat: add skip option
resolves #16 and #87
2020-05-13 18:55:24 +02:00
sharkdp 6f73f4c0f0 Remove custom .github folder 2020-03-16 07:55:46 +01:00
sharkdp 7843b2d038 Update links 2020-03-15 11:33:08 +01:00
David Peter 692ff64e4b Add links to Ubuntu / Debian packages
closes #77
2020-03-09 21:05:26 +01:00
sharkdp 1d156282da Try -gnu 2020-03-09 20:58:50 +01:00
sharkdp b4bdf0f85e Re-enable Windows build 2020-03-09 20:58:50 +01:00
sharkdp 2bda250906 Bump version 2020-03-09 20:41:00 +01:00
sharkdp f32431e552 Update dependencies 2020-03-09 20:33:08 +01:00
sharkdp f9cb2bc872 Fix tests 2020-03-09 20:23:46 +01:00
sharkdp f71ad1e00d Remove Ctrl-C handler, fixes #84 2020-03-09 20:20:12 +01:00
sharkdp 3b01d0ce19 Disable Windows build 2020-01-26 08:50:55 +01:00
sharkdp abc79396df Add simple hexyl-as-a-library example, see #81 2020-01-26 08:12:02 +01:00
David Peter 67b6c2533d Clarify Windows Terminal requirements 2019-10-08 20:52:54 +02:00
22 changed files with 2659 additions and 590 deletions

No files matched your search

-1
View File
@@ -1 +0,0 @@
github: sharkdp
+342
View File
@@ -0,0 +1,342 @@
name: CICD
env:
MIN_SUPPORTED_RUST_VERSION: "1.56.1"
CICD_INTERMEDIATES_DIR: "_cicd-intermediates"
on:
workflow_dispatch:
pull_request:
push:
branches:
- master
tags:
- '*'
jobs:
min_version:
name: Minimum supported rust version
runs-on: ubuntu-18.04
steps:
- name: Checkout source code
uses: actions/checkout@v2
- name: Install rust toolchain (v${{ env.MIN_SUPPORTED_RUST_VERSION }})
uses: actions-rs/toolchain@v1
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
- 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
- name: Run tests
uses: actions-rs/cargo@v1
with:
command: test
args: --locked
build:
name: ${{ matrix.job.os }} (${{ matrix.job.target }})
runs-on: ${{ matrix.job.os }}
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 }
steps:
- name: Checkout source code
uses: actions/checkout@v2
- 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 ;;
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
with:
toolchain: stable
target: ${{ matrix.job.target }}
override: true
profile: minimal # minimal component installation (ie, no documentation)
- name: Show version information (Rust, cargo, GCC)
shell: bash
run: |
gcc --version || true
rustup -V
rustup toolchain list
rustup default
cargo -V
rustc -V
- name: Build
uses: actions-rs/cargo@v1
with:
use-cross: ${{ matrix.job.use-cross }}
command: build
args: --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
- name: Strip debug information from executable
id: strip
shell: bash
run: |
# Figure out suffix of binary
EXE_suffix=""
case ${{ matrix.job.target }} in
*-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}"
# 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}
- name: Set testing options
id: test-options
shell: bash
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}
- 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}}
- 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"
- 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_NAME=${PKG_BASENAME}${PKG_suffix}
echo ::set-output name=PKG_NAME::${PKG_NAME}
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"
# README, LICENSE and CHANGELOG files
cp "README.md" "LICENSE-MIT" "LICENSE-APACHE" "CHANGELOG.md" "$ARCHIVE_DIR"
# base compressed package
pushd "${PKG_STAGING}/" >/dev/null
case ${{ matrix.job.target }} in
*-pc-windows-*) 7z -y a "${PKG_NAME}" "${PKG_BASENAME}"/* | tail -2 ;;
*) tar czf "${PKG_NAME}" "${PKG_BASENAME}"/* ;;
esac;
popd >/dev/null
# Let subsequent steps know where to find the compressed package
echo ::set-output name=PKG_PATH::"${PKG_STAGING}/${PKG_NAME}"
- name: Create Debian package
id: debian-package
shell: bash
if: startsWith(matrix.job.os, 'ubuntu')
run: |
COPYRIGHT_YEARS="2018 - "$(date "+%Y")
DPKG_STAGING="${{ env.CICD_INTERMEDIATES_DIR }}/debian-package"
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}
unset DPKG_ARCH
case ${{ matrix.job.target }} in
aarch64-*-linux-*) DPKG_ARCH=arm64 ;;
arm-*-linux-*hf) DPKG_ARCH=armhf ;;
i686-*-linux-*) DPKG_ARCH=i686 ;;
x86_64-*-linux-*) DPKG_ARCH=amd64 ;;
*) DPKG_ARCH=notset ;;
esac;
DPKG_NAME="${DPKG_BASENAME}_${DPKG_VERSION}_${DPKG_ARCH}.deb"
echo ::set-output name=DPKG_NAME::${DPKG_NAME}
# Binary
install -Dm755 "${{ steps.strip.outputs.BIN_PATH }}" "${DPKG_DIR}/usr/bin/${{ steps.strip.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"
# README and LICENSE
install -Dm644 "README.md" "${DPKG_DIR}/usr/share/doc/${DPKG_BASENAME}/README.md"
install -Dm644 "LICENSE-MIT" "${DPKG_DIR}/usr/share/doc/${DPKG_BASENAME}/LICENSE-MIT"
install -Dm644 "LICENSE-APACHE" "${DPKG_DIR}/usr/share/doc/${DPKG_BASENAME}/LICENSE-APACHE"
install -Dm644 "CHANGELOG.md" "${DPKG_DIR}/usr/share/doc/${DPKG_BASENAME}/changelog"
gzip -n --best "${DPKG_DIR}/usr/share/doc/${DPKG_BASENAME}/changelog"
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 }}
Files: *
Copyright: ${{ env.PROJECT_MAINTAINER }}
Copyright: $COPYRIGHT_YEARS ${{ env.PROJECT_MAINTAINER }}
License: Apache-2.0 or MIT
License: Apache-2.0
On Debian systems, the complete text of the Apache-2.0 can be found in the
file /usr/share/common-licenses/Apache-2.0.
License: MIT
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:
.
The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.
.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
EOF
chmod 644 "${DPKG_DIR}/usr/share/doc/${DPKG_BASENAME}/copyright"
# control file
mkdir -p "${DPKG_DIR}/DEBIAN"
cat > "${DPKG_DIR}/DEBIAN/control" <<EOF
Package: ${DPKG_BASENAME}
Version: ${DPKG_VERSION}
Section: utils
Priority: optional
Maintainer: ${{ env.PROJECT_MAINTAINER }}
Homepage: ${{ env.PROJECT_HOMEPAGE }}
Architecture: ${DPKG_ARCH}
Provides: ${{ env.PROJECT_NAME }}
Conflicts: ${DPKG_CONFLICTS}
Description: colorized hex viewer for the terminal.
EOF
DPKG_PATH="${DPKG_STAGING}/${DPKG_NAME}"
echo ::set-output name=DPKG_PATH::${DPKG_PATH}
# build dpkg
fakeroot dpkg-deb --build "${DPKG_DIR}" "${DPKG_PATH}"
- name: "Artifact upload: tarball"
uses: actions/upload-artifact@master
with:
name: ${{ steps.package.outputs.PKG_NAME }}
path: ${{ steps.package.outputs.PKG_PATH }}
- name: "Artifact upload: Debian package"
uses: actions/upload-artifact@master
if: steps.debian-package.outputs.DPKG_NAME
with:
name: ${{ steps.debian-package.outputs.DPKG_NAME }}
path: ${{ steps.debian-package.outputs.DPKG_PATH }}
- name: Check for release
id: is-release
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}
- name: Publish archives and packages
uses: softprops/action-gh-release@v1
if: steps.is-release.outputs.IS_RELEASE
with:
files: |
${{ steps.package.outputs.PKG_PATH }}
${{ steps.debian-package.outputs.DPKG_PATH }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+3
View File
@@ -1,2 +1,5 @@
/target
**/*.rs.bk
# Generated files
hexyl.1
-79
View File
@@ -1,79 +0,0 @@
language: rust
cache: cargo
matrix:
include:
# Stable channel.
- os: linux
rust: stable
env: TARGET=x86_64-unknown-linux-gnu
- os: linux
rust: stable
env: TARGET=x86_64-unknown-linux-musl
- os: osx
rust: stable
env: TARGET=x86_64-apple-darwin
- os: windows
rust: stable
env: TARGET=x86_64-pc-windows-msvc
# Minimum Rust supported channel.
- os: linux
rust: 1.31.0
env: TARGET=x86_64-unknown-linux-gnu
addons:
apt:
packages:
# needed to build deb packages
- fakeroot
env:
global:
# Default target on travis-ci.
# Used as conditional check in the install stage
- HOST=x86_64-unknown-linux-gnu
# Used on the deployment script
- PROJECT_NAME=hexyl
install:
# prevent target re-add error from rustup
- if [[ $TRAVIS_OS_NAME = linux && $HOST != $TARGET ]]; then rustup target add $TARGET; fi
script:
# Incorporate TARGET env var to the build and test process
- cargo build --target $TARGET --verbose
- cargo test --target $TARGET --verbose
before_deploy:
- bash ci/before_deploy.bash
deploy:
provider: releases
# NOTE updating the `api_key.secure`
# - go to: https://github.com/settings/tokens/new
# - generate new token using `public_repo` scope
# - encrypt it using: `travis encrypt API_KEY_HERE`
# - paste the output below
api_key:
secure: "XHpyp21JUWI6GkV7APTwPZiJQJkRJ13E/9VALuWvDrNYF00OE8m+T0ZbG+N1WCuwewa2QQChqd5yBDlwJn+LlDPqstie69xjCt7PEhvI5ERZlbT8KUtMLI4bFIudsaa6LS6phWi/6aKb4cfn0nEYmWhpqfVfsn4Hh4F9OyOFSRmMSSXziU3kxLdIBBO4jOfoDYz+4KUX48CtIhmFy6dSb4G22jzVXL3ixGq895lVFlUzfiGJhHXoS+6KsOx1q0eIEMPC6V0VTT1loBNxKpjBPQ1b0KysCim/jRnr9mB3ko/4yWcMtIvCaZxTaQXWsQn9wAEN62x3ruKugZh3LJbtaxSvnH9PMntXZliz1HJw3Hpmn+tGLxAOm+uwHRz2C/T2ibemgmw6F8KjsjdNv7Nc2//ErIbmMvV4OlJjJI2Q3LWjnYa2gqI0L0sMqUz9HAfiY/BXLSxp19LgSvZPuHNVzMfh9ovF+Rdk1I3TyoByS8ZUi2ngnra8bSjmjtWHmkntOksjWNG6iobpjz9UxdWXPWxNJHQbRfEyTJSISpClykcSZWj+E2+tfuB3YvuRUaVOIhA3THdOSsqEesl3jkUeIhQqgRhE+Uq/XqB7MdfZ9ZTX8ZgfGFL0gjygviuWznTb0q5i4t1SgEJ2ie+ykjVyqQWa0dePtaQp8yx2PtzchJs="
# for uploading multiple files
file_glob: true
# NOTE explanation on each env variable
# - PROJECT_NAME: name of the project, set on the `env.global` above
# - TRAVIS_TAG: tag name that the build is being deployed for, usually the version number
# - TARGET: target triple of the build
file:
- $PROJECT_NAME-$TRAVIS_TAG-$TARGET.*
- $PROJECT_NAME*.deb
# don't delete artifacts from previous stage
skip_cleanup: true
on:
# deploy only if we push a tag
tags: true
# deploy only on stable channel that has TARGET env variable sets
condition: $TRAVIS_RUST_VERSION = stable && $TARGET != "x86_64-pc-windows-msvc"
notifications:
email:
on_success: never
+172
View File
@@ -0,0 +1,172 @@
# unreleased
## Changes
## Features
## Bugfixes
## Other
## Packaging
# v0.10.0
## Features
- Added new `--plain`, `--no-characters`, and `--no-position` flags, see #154 (@mkatychev)
- Allow hex numbers and units for `--block-size` argument, see #111 and #144 (@merkrafter)
## Other
- Added a man page, see #151 (@sorairolake)
- Mention ability to specify length in hex, see #143 (@merkrafter)
- `--length` and `--bytes` are now marked as conflicting command-line options, see #152 (@sorairolake)
# v0.9.0
## Changes
- Breaking change (binary): setting the `-o/--display-offset` flag no longer overrides the value set by `--skip` [#115](https://github.com/sharkdp/hexyl/issues/115). The first displayed address is now the sum of the two values - this matches the behaviour of `xxd`.
## Features
- Allow relative and negative byte offsets (e.g. `hexyl --skip=-1block`), see #99 (@ErichDonGubler)
- Added `-l` as another alias for '-n/--length' (`xxd` compatibility), see #121 and #135 (@TheDoctor314)
## Bugfixes
- Argument `--length` silently takes precedence over `--bytes`, see #105
- Print warning on empty content, see #107 and #108
- Disallow block sizes of zero, see #110
- Fix newline appearing in `--version` output, see #131 and #133 (@scimas)
## Other
- Better diagnostic messages, see #98 (@ErichDonGubler)
## Packaging
- `hexyl` is now available on snapstore, see #116 (@purveshpatel511)
# v0.8.0
## Features
- A new `--skip <N>` / `-s <N>` option can be used to skip the first `N` bytes of the input, see #16, #88 (@Tarnadas, @MaxJohansen, @ErichDonGubler)
- The `--length`/`--bytes`/`--skip`/`--display-offset` options can now take units for their value argument, for example:
``` bash
hexyl /dev/random --length=1KiB
hexyl $(which hexyl) --skip=1MiB --length=10KiB
```
Both decimal SI prefixes (kB, MB, …) as well as binary IEC prefixes (KiB, MiB, …) are supported.
In addition, there is a new `--block-size <SIZE>` option that can be used to control the size of the `block`
unit:
``` bash
hexyl /dev/random --block-size=4kB --length=2block
```
See: #44 (@ErichDonGubler and @aswild)
## Other
- Various improvements throughout the code base by @ErichDonGubler
## Packaging
- `hexyl` is now available on Void Linux, see #91 (@notramo)
# v0.7.0
## Bugfixes
- hexyl can now be closed with `Ctrl-C` when reading input from STDIN, see #84
## Changes
- Breaking change (library): [`Printer::print_all`](https://docs.rs/hexyl/latest/hexyl/struct.Printer.html#method.print_all) does not take a second argument anymore.
- Added an example on how to use `hexyl` as a library: https://github.com/sharkdp/hexyl/blob/v0.7.0/examples/simple.rs
# v0.6.0
## Features
- `hexyl` can now be used as a library, see #67 (@tommilligan)
- Added a new `-o`/`--display-offset` option to add a certain offset to the
reported file positions, see #57 (@tommilligan)
## Bugfixes
- Remove additional space on short input, see #69 (@nalshihabi)
## Other
- Performance improvements, see #73 and #66
# v0.5.1
## Bugfixes
- A bug in the squeezing logic caused a wrong hexdump, see #62 (@awidegreen)
- Some colors are printed even if they're disabled, see #64 (@awidegreen)
- Fixed build failure on OpenBSD 6.5, see #61
# v0.5.0
## Features
- Added support for squeezing where reoccurring lines are squashed together and visualized with an asterisk. A new `-v`/`--no-squeezing` option can be used to disable the feature. For details, see #59 (@awidegreen)
- Added a new `--border` option with support for various styles (Unicode, ASCII, None), see #54 (@dmke)
- The `--length`/`-n` argument can be passed as a hexadecimal number (`hexyl -n 0xff /dev/urandom`), see #45 (@Qyriad)
- Added `--bytes`/`-c` as an alias for `--length`/`-n`, see #48 (@selfup)
## Changes
- Print header immediately before the first line, see #51 (@mziter)
# v0.4.0
## Features
- Added a new `--color=always/auto/never` option which can be used
to control `hexyl`s color output, see #30 (@bennetthardwick)
- Use 16 colors instead of 256, see #38
## Changes
- Various speed improvements, see #33 (@kballard)
## Bugfixes
- Proper Ctrl-C handling, see #35
- Proper handling of broken pipes (`hexyl … | head`)
# v0.3.1
- Various (huge) performance improvements, see #23 and #24 (@kballard)
- Replaced 24-bit truecolor ANSI codes by 8-bit codes to support
more terminal emulators, fixes #9
# v0.3.0
Windows support
# v0.2.0
Initial release
+29
View File
@@ -0,0 +1,29 @@
# Contributing
Thank you for considering to contribute to `hexyl`!
## Add an entry to the changelog
If your contribution changes the behavior of `hexyl` (as opposed to a typo-fix
in the documentation), please update the [`CHANGELOG.md`](CHANGELOG.md) file
and describe your changes. This makes the release process much easier and
therefore helps to get your changes into a new `hexyl` release faster.
The top of the `CHANGELOG` contains a *"unreleased"* section with a few
subsections (Features, Bugfixes, …). Please add your entry to the subsection
that best describes your change
Entries follow this format:
```
- Short description of what has been changed, see #123 (@user)
```
Here, `#123` is the number of the original issue and/or your pull request.
Please replace `@user` by your GitHub username.
## Adding a new feature
Please consider opening a [ticket](https://github.com/sharkdp/hexyl/issues/new)
first in order to give us a chance to discuss the feature first.
Generated
+469 -107
View File
@@ -1,193 +1,555 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "ansi_term"
version = "0.11.0"
name = "aho-corasick"
version = "0.7.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f"
dependencies = [
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
"memchr",
]
[[package]]
name = "ansi_term"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
dependencies = [
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi",
]
[[package]]
name = "anstyle"
version = "1.0.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78"
[[package]]
name = "anyhow"
version = "1.0.57"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08f9b8508dccb7687a1d6c4ce66b2b0ecef467c94667de27d8d7fe1f8d2a9cdc"
[[package]]
name = "assert_cmd"
version = "2.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c5bcfa8749ac45dd12cb11055aeeb6b27a3895560d60d71e3c23bf979e60514"
dependencies = [
"anstyle",
"bstr",
"libc",
"predicates 3.1.4",
"predicates-core",
"predicates-tree",
"wait-timeout",
]
[[package]]
name = "atty"
version = "0.2.13"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
dependencies = [
"libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
"hermit-abi",
"libc",
"winapi",
]
[[package]]
name = "autocfg"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "bitflags"
version = "1.2.0"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "cc"
version = "1.0.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "cfg-if"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "clap"
version = "2.33.0"
name = "bstr"
version = "1.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "63044e1ae8e69f3b5a92c736ca6269b8d12fa7efe39bf34ddb06d102cf0e2cab"
dependencies = [
"ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
"atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)",
"bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
"term_size 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-width 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
"vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
"memchr",
"regex-automata",
"serde",
]
[[package]]
name = "ctrlc"
version = "3.1.3"
name = "cc"
version = "1.0.73"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11"
[[package]]
name = "clap"
version = "3.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d646c7ade5eb07c4aa20e907a922750df0c448892513714fd3e4acbc7130829f"
dependencies = [
"nix 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
"atty",
"bitflags",
"clap_lex",
"indexmap",
"once_cell",
"strsim",
"termcolor",
"terminal_size 0.1.17",
"textwrap",
]
[[package]]
name = "clap_lex"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5"
dependencies = [
"os_str_bytes",
]
[[package]]
name = "const_format"
version = "0.2.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0936ffe6d0c8d6a51b3b0a73b2acbe925d786f346cf45bfddc8341d79fb7dc8a"
dependencies = [
"const_format_proc_macros",
]
[[package]]
name = "const_format_proc_macros"
version = "0.2.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef196d5d972878a48da7decb7686eded338b4858fbabeed513d63a7c98b2b82d"
dependencies = [
"proc-macro2",
"quote",
"unicode-xid",
]
[[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"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1"
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",
]
[[package]]
name = "float-cmp"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4"
dependencies = [
"num-traits",
]
[[package]]
name = "hashbrown"
version = "0.11.2"
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",
]
[[package]]
name = "hexyl"
version = "0.6.0"
version = "0.10.0"
dependencies = [
"ansi_term 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)",
"atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)",
"clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
"ctrlc 3.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
"ansi_term",
"anyhow",
"assert_cmd",
"atty",
"clap",
"const_format",
"libc",
"predicates 2.1.1",
"terminal_size 0.2.1",
"thiserror",
]
[[package]]
name = "kernel32-sys"
version = "0.2.2"
name = "indexmap"
version = "1.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0f647032dfaa1f8b6dc29bd3edb7bbef4861b8b8007ebb118d6db284fd59f6ee"
dependencies = [
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"autocfg",
"hashbrown",
]
[[package]]
name = "io-lifetimes"
version = "0.7.2"
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",
]
[[package]]
name = "libc"
version = "0.2.62"
version = "0.2.182"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112"
[[package]]
name = "nix"
version = "0.14.1"
name = "linux-raw-sys"
version = "0.0.46"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4d2456c373231a208ad294c33dc5bff30051eafd954cd4caae83a712b12854d"
[[package]]
name = "memchr"
version = "2.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
[[package]]
name = "normalize-line-endings"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be"
[[package]]
name = "num-traits"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
dependencies = [
"bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)",
"cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
"void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"autocfg",
]
[[package]]
name = "once_cell"
version = "1.13.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"
[[package]]
name = "predicates"
version = "2.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a5aab5be6e4732b473071984b3164dbbfb7a3674d30ea5ff44410b6bcd960c3c"
dependencies = [
"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"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cad38746f3166b4031b1a0d39ad9f954dd291e7854fcc0eed52ee41a0b50d144"
[[package]]
name = "predicates-tree"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4d86de6de25020a36c6d3643a86d9a6a9f552107c0559c60ea03551b5e16c032"
dependencies = [
"predicates-core",
"termtree",
]
[[package]]
name = "proc-macro2"
version = "1.0.39"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c54b25569025b7fc9651de43004ae593a75ad88543b17178aa5e1b9c4f15f56f"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1"
dependencies = [
"proc-macro2",
]
[[package]]
name = "regex"
version = "1.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d83f127d94bdbcda4c8cc2e50f6f84f4b611f69c902699ca385a39c3a75f9ff1"
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"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49b3de9ec5dc0a3417da371aab17d729997c15010e7fd24ff707773a33bddb64"
[[package]]
name = "rustix"
version = "0.35.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d51cc38aa10f6bbb377ed28197aa052aa4e2b762c22be9d3153d01822587e787"
dependencies = [
"bitflags",
"errno",
"io-lifetimes",
"libc",
"linux-raw-sys",
"windows-sys",
]
[[package]]
name = "serde"
version = "1.0.185"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be9b6f69f1dfd54c3b568ffa45c310d6973a5e5148fd40cf515acaf38cf5bc31"
[[package]]
name = "strsim"
version = "0.8.0"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]]
name = "term_size"
version = "0.3.1"
name = "syn"
version = "1.0.95"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fbaf6116ab8924f39d52792136fb74fd60a80194cf1b1c6ffa6453eef1c3f942"
dependencies = [
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
"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"
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"
dependencies = [
"rustix",
"windows-sys",
]
[[package]]
name = "termtree"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "507e9898683b6c43a9aa55b64259b721b52ba226e0f3779137e50ad114a4c90b"
[[package]]
name = "textwrap"
version = "0.11.0"
version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb"
dependencies = [
"term_size 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-width 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
"terminal_size 0.1.17",
]
[[package]]
name = "unicode-width"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "vec_map"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "void"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "winapi"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "winapi"
version = "0.3.8"
name = "thiserror"
version = "1.0.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a"
dependencies = [
"winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"thiserror-impl",
]
[[package]]
name = "winapi-build"
version = "0.1.1"
name = "thiserror-impl"
version = "1.0.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "unicode-ident"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d22af068fba1eb5edcb4aea19d382b2a3deb4c8f9d475c589b6ada9e0fd493ee"
[[package]]
name = "unicode-xid"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "957e51f3646910546462e67d5f7599b9e4fb8acdd304b087a6494730f9eebf04"
[[package]]
name = "wait-timeout"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6"
dependencies = [
"libc",
]
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[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"
[metadata]
"checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
"checksum ansi_term 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
"checksum atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)" = "1803c647a3ec87095e7ae7acfca019e98de5ec9a7d01343f611cf3152ed71a90"
"checksum bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8a606a02debe2813760609f57a64a2ffd27d9fdf5b2f133eaca0b248dd92cdd2"
"checksum cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)" = "4fc9a35e1f4290eb9e5fc54ba6cf40671ed2a2514c3eeb2b2a908dda2ea5a1be"
"checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
"checksum clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9"
"checksum ctrlc 3.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c7dfd2d8b4c82121dfdff120f818e09fc4380b0b7e17a742081a89b94853e87f"
"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
"checksum libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)" = "34fcd2c08d2f832f376f4173a231990fa5aef4e99fb569867318a227ef4c06ba"
"checksum nix 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6c722bee1037d430d0f8e687bbdbf222f27cc6e4e68d5caf630857bb2b6dbdce"
"checksum strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
"checksum term_size 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9e5b9a66db815dcfd2da92db471106457082577c3c278d4138ab3e3b4e189327"
"checksum textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
"checksum unicode-width 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "7007dbd421b92cc6e28410fe7362e2e0a2503394908f417b68ec8d1c364c4e20"
"checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a"
"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
"checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6"
"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"
"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-sys"
version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2"
dependencies = [
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_msvc"
version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47"
[[package]]
name = "windows_i686_gnu"
version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6"
[[package]]
name = "windows_i686_msvc"
version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024"
[[package]]
name = "windows_x86_64_gnu"
version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1"
[[package]]
name = "windows_x86_64_msvc"
version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680"
+14 -5
View File
@@ -7,17 +7,26 @@ license = "MIT/Apache-2.0"
name = "hexyl"
readme = "README.md"
repository = "https://github.com/sharkdp/hexyl"
version = "0.6.0"
edition = "2018"
version = "0.10.0"
edition = "2021"
[dependencies]
ansi_term = "0.12"
anyhow = "1.0"
atty = "0.2"
ctrlc = "3.1"
const_format = "0.2"
libc = "0.2"
thiserror = "1.0"
terminal_size = "0.2"
[dependencies.clap]
version = "2"
features = ["suggestions", "color", "wrap_help"]
version = "3"
default-features = false
features = ["std", "suggestions", "color", "wrap_help", "cargo"]
[dev-dependencies]
assert_cmd = "2.1"
predicates = "2.0"
[profile.release]
lto = true
+105 -9
View File
@@ -1,6 +1,6 @@
![](doc/logo.svg)
[![Build Status](https://travis-ci.org/sharkdp/hexyl.svg?branch=master)](https://travis-ci.org/sharkdp/hexyl)
[![CICD](https://github.com/sharkdp/hexyl/actions/workflows/CICD.yml/badge.svg)](https://github.com/sharkdp/hexyl/actions/workflows/CICD.yml)
[![](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)
@@ -19,11 +19,37 @@ of bytes (NULL bytes, printable ASCII characters, ASCII whitespace characters, o
## Installation
### On Debian-based systems
### On Ubuntu
*... and other Debian-based Linux distributions.*
If you run Ubuntu 19.10 (Eoan Ermine) or newer, you can install the [officially maintained package](https://packages.ubuntu.com/search?keywords=hexyl):
```bash
sudo apt install hexyl
```
If you use an older version of Ubuntu, you can download
the latest `.deb` package from the release page and install it via:
``` bash
wget "https://github.com/sharkdp/hexyl/releases/download/v0.6.0/hexyl_0.6.0_amd64.deb"
sudo dpkg -i hexyl_0.6.0_amd64.deb
sudo dpkg -i hexyl_0.10.0_amd64.deb # adapt version number and architecture
```
### On Debian
If you run Debian Buster or newer, you can install the [officially maintained Debian package](https://packages.debian.org/search?searchon=names&keywords=hexyl):
```bash
sudo apt-get install hexyl
```
If you run an older version of Debian, see above for instructions on how to
manually install `hexyl`.
### On Fedora
If you run Fedora 35 or newer, you can install the [officially maintained Fedora package](https://packages.fedoraproject.org/pkgs/rust-hexyl/hexyl):
```bash
sudo dnf install hexyl
```
### On Arch Linux
@@ -34,45 +60,115 @@ You can install `hexyl` from [the official package repository](https://www.archl
pacman -S hexyl
```
### On Void Linux
```
xbps-install hexyl
```
### On Gentoo Linux
Available in [dm9pZCAq overlay](https://github.com/gentoo-mirror/dm9pZCAq)
```
sudo eselect repository enable dm9pZCAq
sudo emerge --sync dm9pZCAq
sudo emerge sys-apps/hexyl::dm9pZCAq
```
### On macOS
Via [Homebrew](https://brew.sh):
```
brew install hexyl
```
...or via [MacPorts](https://www.macports.org):
```
sudo port install hexyl
```
### On FreeBSD
```
pkg install hexyl
```
### on Termux
```
pkg install hexyl
```
or
```
apt install hexyl
```
### Via Nix
```
nix-env -i hexyl
```
### Via Guix
```
guix package -i hexyl
```
Or add the `hexyl` package in the list of packages to be installed in your system configuration (e.g., `/etc/config.scm`).
### On other distributions
Check out the [release page](https://github.com/sharkdp/hexyl/releases) for binary builds.
### On Windows
For now, you will have to install from source via `cargo` (see below). Make sure that
you use a terminal that supports ANSI escape sequences (like PowerShell on Windows 10).
Check out the [release page](https://github.com/sharkdp/hexyl/releases) for binary builds.
Alternatively, install from source via `cargo`, `snap` or `scoop` (see below).
Make sure that you use a terminal that supports ANSI escape sequences (like ConHost v2 since Windows 10 1703
or Windows Terminal since Windows 10 1903).
### Via cargo
If you have Rust 1.31 or higher, you can install `hexyl` from source via `cargo`:
If you have Rust 1.56 or higher, you can install `hexyl` from source via `cargo`:
```
cargo install hexyl
```
Alternatively, you can install `hexyl` directly from the repository by using:
```
git clone https://github.com/sharkdp/hexyl
cargo install --path ./hexyl
```
Note: To convert the man page, you will need [Pandoc](https://pandoc.org/).
You can convert from Markdown by using (in the project root):
```
pandoc -s -f markdown -t man -o ./doc/hexyl.1 ./doc/hexyl.1.md
```
### Via snap package
```
sudo snap install hexyl
```
[Get it from the Snap Store](https://snapcraft.io/hexyl)
### Via [Scoop](https://scoop.sh)
```
scoop install hexyl
```
## License
Licensed under either of
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
* MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or https://www.apache.org/licenses/LICENSE-2.0)
* MIT license ([LICENSE-MIT](LICENSE-MIT) or https://opensource.org/licenses/MIT)
at your option.
-1
View File
@@ -1 +0,0 @@
*.bash linguist-vendored
-103
View File
@@ -1,103 +0,0 @@
#!/usr/bin/env bash
# Building and packaging for release
set -ex
build() {
cargo build --target "$TARGET" --release --verbose
}
pack() {
local tempdir
local out_dir
local package_name
tempdir=$(mktemp -d 2>/dev/null || mktemp -d -t tmp)
out_dir=$(pwd)
package_name="$PROJECT_NAME-$TRAVIS_TAG-$TARGET"
# create a "staging" directory
mkdir "$tempdir/$package_name"
# copying the main binary
cp "target/$TARGET/release/$PROJECT_NAME" "$tempdir/$package_name/"
strip "$tempdir/$package_name/$PROJECT_NAME"
# readme and license
cp README.md "$tempdir/$package_name"
cp LICENSE-MIT "$tempdir/$package_name"
cp LICENSE-APACHE "$tempdir/$package_name"
# archiving
pushd "$tempdir"
tar czf "$out_dir/$package_name.tar.gz" "$package_name"/*
popd
rm -r "$tempdir"
}
make_deb() {
local tempdir
local architecture
local version
local dpkgname
local conflictname
case $TARGET in
x86_64*)
architecture=amd64
;;
i686*)
architecture=i386
;;
*)
echo "ERROR: unknown target" >&2
return 1
;;
esac
version=${TRAVIS_TAG#v}
if [[ $TARGET = *musl* ]]; then
dpkgname=$PROJECT_NAME-musl
conflictname=$PROJECT_NAME
else
dpkgname=$PROJECT_NAME
conflictname=$PROJECT_NAME-musl
fi
tempdir=$(mktemp -d 2>/dev/null || mktemp -d -t tmp)
# copy the main binary
install -Dm755 "target/$TARGET/release/$PROJECT_NAME" "$tempdir/usr/bin/$PROJECT_NAME"
strip "$tempdir/usr/bin/$PROJECT_NAME"
# readme and license
install -Dm644 README.md "$tempdir/usr/share/doc/$PROJECT_NAME/README.md"
install -Dm644 LICENSE-MIT "$tempdir/usr/share/doc/$PROJECT_NAME/LICENSE-MIT"
install -Dm644 LICENSE-APACHE "$tempdir/usr/share/doc/$PROJECT_NAME/LICENSE-APACHE"
# Control file
mkdir "$tempdir/DEBIAN"
cat > "$tempdir/DEBIAN/control" <<EOF
Package: $dpkgname
Version: $version
Section: utils
Priority: optional
Maintainer: David Peter <mail@david-peter.de>
Architecture: $architecture
Provides: $PROJECT_NAME
Conflicts: $conflictname
Description: A command-line hex viewer.
EOF
fakeroot dpkg-deb --build "$tempdir" "${dpkgname}_${version}_${architecture}.deb"
}
main() {
build
pack
if [[ $TARGET = *linux* ]]; then
make_deb
fi
}
main
+148
View File
@@ -0,0 +1,148 @@
% HEXYL(1) hexyl 0.10.0 | General Commands Manual
%
% 2022-03-23
<!--
This man page is written in Pandoc's Markdown.
See: https://pandoc.org/MANUAL.html#pandocs-markdown
-->
# NAME
hexyl - a command-line hex viewer
# SYNOPSIS
**hexyl** [_OPTIONS_] [_FILE_]
# DESCRIPTION
**hexyl** is a simple hex viewer for the terminal.
It uses a colored output to distinguish different categories of bytes (NULL
bytes, printable ASCII characters, ASCII whitespace characters, other ASCII
characters and non-ASCII).
# POSITIONAL ARGUMENTS
_FILE_
: The file to display.
If no _FILE_ argument is given, read from STDIN.
# OPTIONS
**-n**, **\--length** _N_
: 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.
Examples:
:
Read the first 64 bytes:
: $ **hexyl \--length=64**
Read the first 4 kibibytes:
: $ **hexyl \--length=4KiB**
Read the first 255 bytes (specified using a hex number):
: $ **hexyl \--length=0xff**
**-c**, **\--bytes** _N_
: An alias for **-n**/**\--length**.
**-l** _N_
: Yet another alias for **-n**/**\--length**.
**-s**, **\--skip** _N_
: 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.
**\--block-size** _SIZE_
: Sets the size of the block unit to _SIZE_ (default is 512).
Examples:
:
Sets the block size to 1024 bytes:
: $ **hexyl \--block-size=1024 \--length=5block**
Sets the block size to 4 kilobytes:
: $ **hexyl \--block-size=4kB \--length=2block**
**-v**, **\--no-squeezing**
: 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.
**\--color** _WHEN_
: When to use colors.
The auto-mode only displays colors if the output goes to an interactive
terminal.
Possible values:
: - **always** (default)
- **auto**
- **never**
**\--border** _STYLE_
: Whether to draw a border with Unicode characters, ASCII characters, or none
at all.
Possible values:
: - **unicode** (default)
- **ascii**
- **none**
**-o**, **\--display-offset** _N_
: 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.
**-h**, **\--help**
: Prints help information.
**-V**, **\--version**
: Prints version information.
# NOTES
Source repository:
: <https://github.com/sharkdp/hexyl>
# EXAMPLES
Print a given file:
: $ **hexyl small.png**
Print and view a given file in the terminal pager:
: $ **hexyl big.png | less -r**
Print the first 256 bytes of a given special file:
: $ **hexyl -n 256 /dev/urandom**
# AUTHORS
**hexyl** was written by David Peter <mail@david-peter.de>.
# REPORTING BUGS
Bugs can be reported on GitHub at:
: <https://github.com/sharkdp/hexyl/issues>
# COPYRIGHT
**hexyl** is dual-licensed under:
: - Apache License 2.0 (<https://www.apache.org/licenses/LICENSE-2.0>)
- MIT License (<https://opensource.org/licenses/MIT>)
# SEE ALSO
**hexdump**(1), **xxd**(1)
+31
View File
@@ -0,0 +1,31 @@
use std::io;
use hexyl::{BorderStyle, Printer};
fn main() {
let input = vec![
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,
];
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,
);
printer.print_all(&input[..]).unwrap();
}
+701 -67
View File
@@ -1,48 +1,96 @@
#[macro_use]
extern crate clap;
use atty;
use ctrlc;
use std::convert::TryFrom;
use std::fs::File;
use std::io::{self, prelude::*};
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::io::{self, prelude::*, SeekFrom};
use std::num::{NonZeroI64, NonZeroU16};
use clap::{App, AppSettings, Arg};
use clap::{crate_name, crate_version, AppSettings, Arg, ColorChoice, Command};
use atty::Stream;
use hexyl::{BorderStyle, Printer};
use anyhow::{anyhow, Context, Error as AnyhowError};
fn run() -> Result<(), Box<dyn std::error::Error>> {
let app = App::new(crate_name!())
.setting(AppSettings::ColorAuto)
.setting(AppSettings::ColoredHelp)
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)
.setting(AppSettings::UnifiedHelpMessage)
.color(ColorChoice::Auto)
.max_term_width(90)
.version(crate_version!())
.about(crate_description!())
.arg(Arg::with_name("file").help("File to display"))
.arg(
Arg::with_name("length")
.short("n")
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("Read only N bytes from the input"),
.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::with_name("bytes")
.short("c")
Arg::new("bytes")
.short('c')
.long("bytes")
.takes_value(true)
.value_name("N")
.conflicts_with("length")
.help("An alias for -n/--length"),
)
.arg(
Arg::with_name("nosqueezing")
.short("v")
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 \
@@ -51,48 +99,182 @@ fn run() -> Result<(), Box<dyn std::error::Error>> {
),
)
.arg(
Arg::with_name("color")
Arg::new("color")
.long("color")
.takes_value(true)
.value_name("when")
.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::with_name("border")
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 or ASCII characters, or none at all"),
.help(
"Whether to draw a border with Unicode characters, ASCII characters, \
or none at all",
),
)
.arg(
Arg::with_name("display_offset")
.short("o")
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("OFFSET")
.help("Add OFFSET to the displayed file position."),
.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 = app.get_matches_safe()?;
let matches = command.get_matches();
let stdin = io::stdin();
let mut reader: Box<dyn Read> = match matches.value_of("file") {
Some(filename) => Box::new(File::open(filename)?),
None => Box::new(stdin.lock()),
let mut reader = match matches.value_of("FILE") {
Some(filename) => Input::File(File::open(filename)?),
None => Input::Stdin(stdin.lock()),
};
let length_arg = matches.value_of("length").or(matches.value_of("bytes"));
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());
if let Some(length) = length_arg.and_then(parse_hex_or_int) {
reader = Box::new(reader.take(length));
}
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,
@@ -108,26 +290,77 @@ fn run() -> Result<(), Box<dyn std::error::Error>> {
let squeeze = !matches.is_present("nosqueezing");
let display_offset = matches
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")
.and_then(parse_hex_or_int)
.map(|s| {
parse_byte_count(s).context(anyhow!(
"failed to parse `--display-offset` arg {:?} as byte count",
s
))
})
.transpose()?
.unwrap_or(0);
// Set up Ctrl-C handler
let cancelled = Arc::new(AtomicBool::new(false));
let c = cancelled.clone();
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
};
ctrlc::set_handler(move || {
c.store(true, Ordering::SeqCst);
})
.expect("Error setting Ctrl-C handler");
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, border_style, squeeze);
printer.display_offset(display_offset as usize);
printer.print_all(&mut reader, Some(cancelled))?;
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(())
}
@@ -140,27 +373,428 @@ fn main() {
let result = run();
if let Err(err) = result {
if let Some(clap_err) = err.downcast_ref::<clap::Error>() {
eprint!("{}", clap_err); // Clap errors already have newlines
match clap_err.kind {
// The exit code should not indicate an error for --help / --version
clap::ErrorKind::HelpDisplayed | clap::ErrorKind::VersionDisplayed => {
std::process::exit(0)
}
_ => (),
}
} else {
eprintln!("Error: {}", err);
}
eprintln!("Error: {:?}", err);
std::process::exit(1);
}
}
fn parse_hex_or_int(n: &str) -> Option<u64> {
if n.starts_with("0x") {
u64::from_str_radix(n.trim_start_matches("0x"), 16).ok()
} else {
n.parse::<u64>().ok()
#[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,
}
);
}
+65
View File
@@ -0,0 +1,65 @@
use std::convert::TryFrom;
use std::fs;
use std::io::{self, copy, sink, Read, Seek, SeekFrom};
pub enum Input<'a> {
File(fs::File),
Stdin(io::StdinLock<'a>),
}
impl<'a> Read for Input<'a> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
match *self {
Input::File(ref mut file) => file.read(buf),
Input::Stdin(ref mut stdin) => stdin.read(buf),
}
}
}
impl<'a> Seek for Input<'a> {
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 offset = match pos {
SeekFrom::Current(o) => u64::try_from(o).or_else(|_e| cant_seek_abs_err())?,
SeekFrom::Start(_) | SeekFrom::End(_) => cant_seek_abs_err()?,
};
copy(&mut reader.take(offset), &mut sink())
}
match *self {
Input::File(ref mut file) => {
let seek_res = file.seek(pos);
if let Err(Some(libc::ESPIPE)) = seek_res.as_ref().map_err(|err| err.raw_os_error())
{
try_skip(
file,
pos,
"Pipes only support seeking forward with a relative offset",
)
} else {
seek_res
}
}
Input::Stdin(ref mut stdin) => try_skip(
stdin,
pos,
"STDIN only supports seeking forward with a relative offset",
),
}
}
}
impl<'a> Input<'a> {
pub fn into_inner(self) -> Box<dyn Read + 'a> {
match self {
Input::File(file) => Box::new(file),
Input::Stdin(stdin) => Box::new(stdin),
}
}
}
+269 -166
View File
@@ -1,16 +1,15 @@
pub(crate) mod input;
pub mod squeezer;
pub use input::*;
use std::io::{self, Read, Write};
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use ansi_term::Color;
use ansi_term::Color::Fixed;
use crate::squeezer::{SqueezeAction, Squeezer};
type Cancelled = Arc<AtomicBool>;
const BUFFER_SIZE: usize = 256;
const COLOR_NULL: Color = Fixed(242); // grey
@@ -35,10 +34,7 @@ impl Byte {
fn category(self) -> ByteCategory {
if self.0 == 0x00 {
ByteCategory::Null
} else if self.0.is_ascii_alphanumeric()
|| self.0.is_ascii_punctuation()
|| self.0.is_ascii_graphic()
{
} else if self.0.is_ascii_graphic() {
ByteCategory::AsciiPrintable
} else if self.0.is_ascii_whitespace() {
ByteCategory::AsciiWhitespace
@@ -143,27 +139,34 @@ impl BorderStyle {
}
pub struct Printer<'a, Writer: Write> {
idx: usize,
idx: u64,
/// The raw bytes used as input for the current line.
raw_line: Vec<u8>,
/// The buffered line built with each byte, ready to print to writer.
buffer_line: Vec<u8>,
writer: &'a mut Writer,
show_color: bool,
show_char_panel: bool,
show_position_panel: bool,
border_style: BorderStyle,
header_was_printed: bool,
byte_hex_table: Vec<String>,
byte_char_table: Vec<String>,
byte_hex_panel: Vec<String>,
byte_char_panel: Vec<String>,
squeezer: Squeezer,
display_offset: usize,
display_offset: u64,
/// The number of panels to draw.
columns: u16,
}
impl<'a, Writer: Write> Printer<'a, Writer> {
pub fn new(
writer: &'a mut Writer,
show_color: bool,
show_char_panel: bool,
show_position_panel: bool,
border_style: BorderStyle,
use_squeeze: bool,
columns: u16,
) -> Printer<'a, Writer> {
Printer {
idx: 1,
@@ -171,9 +174,11 @@ impl<'a, Writer: Write> Printer<'a, Writer> {
buffer_line: vec![],
writer,
show_color,
show_char_panel,
show_position_panel,
border_style,
header_was_printed: false,
byte_hex_table: (0u8..=u8::max_value())
byte_hex_panel: (0u8..=u8::MAX)
.map(|i| {
let byte_hex = format!("{:02x} ", i);
if show_color {
@@ -183,72 +188,88 @@ impl<'a, Writer: Write> Printer<'a, Writer> {
}
})
.collect(),
byte_char_table: (0u8..=u8::max_value())
.map(|i| {
let byte_char = format!("{}", Byte(i).as_char());
if show_color {
Byte(i).color().paint(byte_char).to_string()
} else {
byte_char
}
byte_char_panel: show_char_panel
.then(|| {
(0u8..=u8::MAX)
.map(|i| {
let byte_char = format!("{}", Byte(i).as_char());
if show_color {
Byte(i).color().paint(byte_char).to_string()
} else {
byte_char
}
})
.collect()
})
.collect(),
squeezer: Squeezer::new(use_squeeze),
.unwrap_or_default(),
squeezer: Squeezer::new(use_squeeze, 8 * columns as u64),
display_offset: 0,
columns,
}
}
pub fn display_offset(&mut self, display_offset: usize) -> &mut Self {
pub fn display_offset(&mut self, display_offset: u64) -> &mut Self {
self.display_offset = display_offset;
self
}
pub fn header(&mut self) {
if let Some(border_elements) = self.border_style.header_elems() {
let h = border_elements.horizontal_line;
let h8 = h.to_string().repeat(8);
let h25 = h.to_string().repeat(25);
fn write_border(&mut self, border_elements: BorderElements) {
let h = border_elements.horizontal_line;
let c = border_elements.column_separator;
let l = border_elements.left_corner;
let r = border_elements.right_corner;
let h8 = h.to_string().repeat(8);
let h25 = h.to_string().repeat(25);
writeln!(
self.writer,
"{l}{h8}{c}{h25}{c}{h25}{c}{h8}{c}{h8}{r}",
l = border_elements.left_corner,
c = border_elements.column_separator,
r = border_elements.right_corner,
h8 = h8,
h25 = h25
)
.ok();
if self.show_position_panel {
write!(self.writer, "{l}{h8}{c}", l = l, c = c, h8 = h8).ok();
} else {
write!(self.writer, "{}", l).ok();
}
for _ in 0..self.columns - 1 {
write!(self.writer, "{h25}{c}", h25 = h25, c = c).ok();
}
if self.show_char_panel {
write!(self.writer, "{h25}{c}", h25 = h25, c = c).ok();
} else {
write!(self.writer, "{h25}", h25 = h25).ok();
}
if self.show_char_panel {
for _ in 0..self.columns - 1 {
write!(self.writer, "{h8}{c}", h8 = h8, c = c).ok();
}
writeln!(self.writer, "{h8}{r}", h8 = h8, r = r).ok();
} else {
writeln!(self.writer, "{r}", r = r).ok();
}
}
pub fn footer(&mut self) {
if let Some(border_elements) = self.border_style.footer_elems() {
let h = border_elements.horizontal_line;
let h8 = h.to_string().repeat(8);
let h25 = h.to_string().repeat(25);
pub fn print_header(&mut self) {
if self.header_was_printed {
return;
}
if let Some(e) = self.border_style.header_elems() {
self.write_border(e)
}
self.header_was_printed = true;
}
writeln!(
self.writer,
"{l}{h8}{c}{h25}{c}{h25}{c}{h8}{c}{h8}{r}",
l = border_elements.left_corner,
c = border_elements.column_separator,
r = border_elements.right_corner,
h8 = h8,
h25 = h25
)
.ok();
pub fn print_footer(&mut self) {
if let Some(e) = self.border_style.footer_elems() {
self.write_border(e)
}
}
fn print_position_indicator(&mut self) {
if !self.header_was_printed {
self.header();
self.header_was_printed = true;
fn print_position_panel(&mut self) {
if !self.show_position_panel {
write!(&mut self.buffer_line, "{} ", self.border_style.outer_sep()).ok();
return;
}
let style = COLOR_OFFSET.normal();
let byte_index = format!("{:08x}", (self.idx - 1) + self.display_offset);
let byte_index = format!("{:08x}", self.idx - 1 + self.display_offset);
let formatted_string = if self.show_color {
format!("{}", style.paint(byte_index))
} else {
@@ -263,24 +284,60 @@ impl<'a, Writer: Write> Printer<'a, Writer> {
);
}
pub fn print_byte(&mut self, b: u8) -> io::Result<()> {
if self.idx % 16 == 1 {
self.print_position_indicator();
pub fn print_char_panel(&mut self) {
if !self.show_char_panel {
// just write newline if character panel is hidden
writeln!(&mut self.buffer_line).ok();
return;
}
write!(&mut self.buffer_line, "{}", self.byte_hex_table[b as usize])?;
let len = self.raw_line.len();
let mut idx = 1;
for &b in self.raw_line.iter() {
let _ = write!(
&mut self.buffer_line,
"{}",
self.byte_char_panel[b as usize]
);
if idx % 8 == 0 && idx % (u64::from(self.columns) * 8) != 0 {
let _ = write!(&mut self.buffer_line, "{}", self.border_style.inner_sep());
}
idx += 1;
}
if len < usize::from(8 * self.columns) {
let _ = write!(&mut self.buffer_line, "{0:1$}", "", 8 - len % 8);
for _ in 0..(usize::from(8 * self.columns) - (len + (8 - len % 8))) / 8 {
let _ = write!(
&mut self.buffer_line,
"{2}{0:1$}",
"",
8,
self.border_style.inner_sep()
);
}
}
let _ = writeln!(&mut self.buffer_line, "{}", self.border_style.outer_sep());
}
pub fn print_byte(&mut self, b: u8) -> io::Result<()> {
if self.idx % (u64::from(self.columns) * 8) == 1 {
self.print_header();
self.print_position_panel();
}
write!(&mut self.buffer_line, "{}", self.byte_hex_panel[b as usize])?;
self.raw_line.push(b);
self.squeezer.process(b, self.idx);
match self.idx % 16 {
8 => {
let _ = write!(&mut self.buffer_line, "{} ", self.border_style.inner_sep());
}
0 => {
self.print_textline()?;
}
_ => {}
if self.idx % (u64::from(self.columns) * 8) == 0 {
self.print_textline()?;
} else if self.idx % 8 == 0 {
let _ = write!(&mut self.buffer_line, "{} ", self.border_style.inner_sep());
}
self.idx += 1;
@@ -293,17 +350,34 @@ impl<'a, Writer: Write> Printer<'a, Writer> {
if len == 0 {
if self.squeezer.active() {
self.print_position_indicator();
let _ = writeln!(
self.print_position_panel();
write!(&mut self.buffer_line, "{0:1$}", "", 24)?;
for _ in 0..self.columns - 1 {
write!(
&mut self.buffer_line,
"{2}{0:1$}",
"",
25,
self.border_style.inner_sep()
)?;
}
write!(
&mut self.buffer_line,
"{0:1$}{4}{0:2$}{5}{0:3$}{4}{0:3$}{5}",
"{2}{0:1$}",
"",
24,
25,
8,
self.border_style.inner_sep(),
self.border_style.outer_sep(),
);
self.border_style.outer_sep()
)?;
for _ in 0..self.columns - 1 {
write!(
&mut self.buffer_line,
"{2}{0:1$}",
"",
8,
self.border_style.inner_sep()
)?;
}
writeln!(&mut self.buffer_line, "{}", self.border_style.outer_sep())?;
self.writer.write_all(&self.buffer_line)?;
}
return Ok(());
@@ -311,62 +385,23 @@ impl<'a, Writer: Write> Printer<'a, Writer> {
let squeeze_action = self.squeezer.action();
// print empty space on last line
if squeeze_action != SqueezeAction::Delete {
if len < 8 {
let _ = write!(
&mut self.buffer_line,
"{0:1$}{3}{0:2$}{4}",
"",
3 * (8 - len),
1 + 3 * 8,
self.border_style.inner_sep(),
self.border_style.outer_sep(),
);
} else {
let _ = write!(
&mut self.buffer_line,
"{0:1$}{2}",
"",
3 * (16 - len),
self.border_style.outer_sep()
);
}
let mut idx = 1;
for &b in self.raw_line.iter() {
let _ = write!(
&mut self.buffer_line,
"{}",
self.byte_char_table[b as usize]
);
if idx == 8 {
let _ = write!(&mut self.buffer_line, "{}", self.border_style.inner_sep());
if len < usize::from(8 * self.columns) {
write!(&mut self.buffer_line, "{0:1$}", "", 3 * (8 - len % 8))?;
for _ in 0..(usize::from(8 * self.columns) - (len + (8 - len % 8))) / 8 {
write!(
&mut self.buffer_line,
"{2}{0:1$}",
"",
1 + 3 * 8,
self.border_style.inner_sep()
)?;
}
idx += 1;
}
if len < 8 {
let _ = writeln!(
&mut self.buffer_line,
"{0:1$}{3}{0:2$}{4}",
"",
8 - len,
8,
self.border_style.inner_sep(),
self.border_style.outer_sep(),
);
} else {
let _ = writeln!(
&mut self.buffer_line,
"{0:1$}{2}",
"",
16 - len,
self.border_style.outer_sep()
);
}
write!(&mut self.buffer_line, "{}", self.border_style.outer_sep())?;
}
self.print_char_panel();
match squeeze_action {
SqueezeAction::Print => {
@@ -377,17 +412,33 @@ impl<'a, Writer: Write> Printer<'a, Writer> {
} else {
String::from("*")
};
let _ = writeln!(
write!(
&mut self.buffer_line,
"{5}{0}{1:2$}{5}{1:3$}{6}{1:3$}{5}{1:4$}{6}{1:4$}{5}",
"{3}{0}{1:2$}{3}",
asterisk,
"",
7,
25,
8,
self.border_style.outer_sep(),
self.border_style.inner_sep(),
);
self.border_style.outer_sep()
)?;
for i in 0..self.columns {
write!(&mut self.buffer_line, "{0:1$}", "", 25)?;
if i != self.columns - 1 {
write!(&mut self.buffer_line, "{}", self.border_style.inner_sep())?;
} else {
write!(&mut self.buffer_line, "{}", self.border_style.outer_sep())?;
}
}
for i in 0..self.columns {
write!(&mut self.buffer_line, "{0:1$}", "", 8)?;
if i != self.columns - 1 {
write!(&mut self.buffer_line, "{}", self.border_style.inner_sep())?;
} else {
writeln!(&mut self.buffer_line, "{}", self.border_style.outer_sep())?;
}
}
}
SqueezeAction::Delete => self.buffer_line.clear(),
SqueezeAction::Ignore => (),
@@ -408,12 +459,11 @@ impl<'a, Writer: Write> Printer<'a, Writer> {
}
/// Loop through the given `Reader`, printing until the `Reader` buffer
/// is exhausted, or the optional `cancelled` bool is set to true.
/// is exhausted.
pub fn print_all<Reader: Read>(
&mut self,
mut reader: Reader,
canceller: Option<Cancelled>,
) -> Result<(), Box<dyn std::error::Error>> {
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let mut buffer = [0; BUFFER_SIZE];
'mainloop: loop {
let size = reader.read(&mut buffer)?;
@@ -421,13 +471,6 @@ impl<'a, Writer: Write> Printer<'a, Writer> {
break;
}
if let Some(cancelled) = &canceller {
if cancelled.load(Ordering::SeqCst) {
eprintln!("hexyl has been cancelled.");
std::process::exit(130); // Set exit code to 128 + SIGINT
}
}
for b in &buffer[..size] {
let res = self.print_byte(*b);
@@ -440,10 +483,24 @@ impl<'a, Writer: Write> Printer<'a, Writer> {
// Finish last line
self.print_textline().ok();
if !self.header_was_printed() {
self.header();
self.print_header();
if self.show_position_panel {
write!(self.writer, "{0:9}", "│").ok();
}
write!(
self.writer,
"{0:2}{1:24}{0}{0:>26}",
"│", "No content to print"
)
.ok();
if self.show_char_panel {
write!(self.writer, "{0:>9}{0:>9}", "│").ok();
}
writeln!(self.writer).ok();
}
self.footer();
self.print_footer();
Ok(())
}
@@ -456,56 +513,102 @@ mod tests {
use super::*;
fn assert_print_all_output<Reader: Read>(input: Reader, expected_string: String) -> () {
fn assert_print_all_output<Reader: Read>(input: Reader, expected_string: String) {
let mut output = vec![];
let mut printer = Printer::new(&mut output, false, BorderStyle::Unicode, true);
let mut printer = Printer::new(
&mut output,
false,
true,
true,
BorderStyle::Unicode,
true,
2,
);
printer.print_all(input, None).unwrap();
printer.print_all(input).unwrap();
let actual_string: &str = str::from_utf8(&output).unwrap();
assert_eq!(actual_string, expected_string)
assert_eq!(actual_string, expected_string,)
}
#[test]
fn empty_file_passes() {
let input = io::empty();
let expected_string =
"┌────────┬─────────────────────────┬─────────────────────────┬────────┬────────┐
let expected_string = "\
┌────────┬─────────────────────────┬─────────────────────────┬────────┬────────┐
│ │ No content to print │ │ │ │
└────────┴─────────────────────────┴─────────────────────────┴────────┴────────┘
"
.to_owned();
.to_owned();
assert_print_all_output(input, expected_string);
}
#[test]
fn short_input_passes() {
let input = io::Cursor::new(b"spam");
let expected_string =
"┌────────┬─────────────────────────┬─────────────────────────┬────────┬────────┐
let expected_string = "\
┌────────┬─────────────────────────┬─────────────────────────┬────────┬────────┐
│00000000│ 73 70 61 6d ┊ │spam ┊ │
└────────┴─────────────────────────┴─────────────────────────┴────────┴────────┘
"
.to_owned();
.to_owned();
assert_print_all_output(input, expected_string);
}
#[test]
fn display_offset() {
let input = io::Cursor::new(b"spamspamspamspamspam");
let expected_string =
"┌────────┬─────────────────────────┬─────────────────────────┬────────┬────────┐
let expected_string = "\
┌────────┬─────────────────────────┬─────────────────────────┬────────┬────────┐
│deadbeef│ 73 70 61 6d 73 70 61 6d ┊ 73 70 61 6d 73 70 61 6d │spamspam┊spamspam│
│deadbeff│ 73 70 61 6d ┊ │spam ┊ │
└────────┴─────────────────────────┴─────────────────────────┴────────┴────────┘
"
.to_owned();
.to_owned();
let mut output = vec![];
let mut printer: Printer<Vec<u8>> =
Printer::new(&mut output, false, BorderStyle::Unicode, true);
let mut printer: Printer<Vec<u8>> = Printer::new(
&mut output,
false,
true,
true,
BorderStyle::Unicode,
true,
2,
);
printer.display_offset(0xdeadbeef);
printer.print_all(input, None).unwrap();
printer.print_all(input).unwrap();
let actual_string: &str = str::from_utf8(&output).unwrap();
assert_eq!(actual_string, expected_string)
}
#[test]
fn multiple_columns() {
let input = io::Cursor::new(b"supercalifragilisticexpialidocioussupercalifragilisticexpialidocioussupercalifragilisticexpialidocious");
let expected_string = "\
┌────────┬─────────────────────────┬─────────────────────────┬─────────────────────────┬─────────────────────────┬────────┬────────┬────────┬────────┐
│00000000│ 73 75 70 65 72 63 61 6c ┊ 69 66 72 61 67 69 6c 69 ┊ 73 74 69 63 65 78 70 69 ┊ 61 6c 69 64 6f 63 69 6f │supercal┊ifragili┊sticexpi┊alidocio│
│00000020│ 75 73 73 75 70 65 72 63 ┊ 61 6c 69 66 72 61 67 69 ┊ 6c 69 73 74 69 63 65 78 ┊ 70 69 61 6c 69 64 6f 63 │ussuperc┊alifragi┊listicex┊pialidoc│
│00000040│ 69 6f 75 73 73 75 70 65 ┊ 72 63 61 6c 69 66 72 61 ┊ 67 69 6c 69 73 74 69 63 ┊ 65 78 70 69 61 6c 69 64 │ioussupe┊rcalifra┊gilistic┊expialid│
│00000060│ 6f 63 69 6f 75 73 ┊ ┊ ┊ │ocious ┊ ┊ ┊ │
└────────┴─────────────────────────┴─────────────────────────┴─────────────────────────┴─────────────────────────┴────────┴────────┴────────┴────────┘
"
.to_owned();
let mut output = vec![];
let mut printer: Printer<Vec<u8>> = Printer::new(
&mut output,
false,
true,
true,
BorderStyle::Unicode,
true,
4,
);
printer.print_all(input).unwrap();
let actual_string: &str = str::from_utf8(&output).unwrap();
assert_eq!(actual_string, expected_string)
+36 -52
View File
@@ -22,6 +22,7 @@ enum SqueezeState {
pub struct Squeezer {
state: SqueezeState,
byte: u8,
lsize: u64,
}
#[derive(Debug, PartialEq)]
@@ -31,11 +32,8 @@ pub enum SqueezeAction {
Delete,
}
/// line size
const LSIZE: usize = 16;
impl Squeezer {
pub fn new(enabled: bool) -> Squeezer {
pub fn new(enabled: bool, lsize: u64) -> Squeezer {
Squeezer {
state: if enabled {
SqueezeState::Probe
@@ -43,17 +41,18 @@ impl Squeezer {
SqueezeState::Disabled
},
byte: 0,
lsize,
}
}
pub fn process(&mut self, b: u8, i: usize) {
pub fn process(&mut self, b: u8, i: u64) {
use self::SqueezeState::*;
if self.state == Disabled {
return;
}
let eq = b == self.byte;
if i % LSIZE == 0 {
if i % self.lsize == 0 {
if !eq {
self.state = Probe;
} else {
@@ -68,9 +67,9 @@ impl Squeezer {
};
}
} else if !eq {
if i % LSIZE == 1 {
if i % self.lsize == 1 {
self.state = Probe;
} else if i % LSIZE != 1 {
} else if i % self.lsize != 1 {
self.state = NoSqueeze;
}
}
@@ -80,10 +79,10 @@ impl Squeezer {
pub fn active(&self) -> bool {
use self::SqueezeState::*;
match self.state {
Squeeze | SqueezeActive | SqueezeFirstLine | SqueezeActiveFirstLine => true,
_ => false,
}
matches!(
self.state,
Squeeze | SqueezeActive | SqueezeFirstLine | SqueezeActiveFirstLine
)
}
pub fn action(&self) -> SqueezeAction {
@@ -111,11 +110,14 @@ impl Squeezer {
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];
let mut s = Squeezer::new(true);
let v = vec![0u8; LINES * LSIZE_USIZE];
let mut s = Squeezer::new(true, LSIZE);
// just initialized
assert_eq!(s.action(), SqueezeAction::Ignore);
s.advance();
@@ -126,9 +128,8 @@ mod tests {
SqueezeAction::Delete, // delete reoccurring line
];
let mut line = 0;
let mut idx = 1;
for z in v.chunks(LSIZE) {
for (line, z) in v.chunks(LSIZE_USIZE).enumerate() {
for i in z {
s.process(*i, idx);
idx += 1;
@@ -136,15 +137,14 @@ mod tests {
let action = s.action();
s.advance();
assert_eq!(action, exp[line]);
line += 1;
}
}
#[test]
fn incomplete_while_squeeze() {
// fourth line only has 12 bytes and should be printed
let v = vec![0u8; 3 * LSIZE + 12];
let mut s = Squeezer::new(true);
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();
@@ -156,16 +156,14 @@ mod tests {
SqueezeAction::Ignore, // last line only 12 bytes, print it
];
let mut line = 0;
let mut idx = 1;
for z in v.chunks(LSIZE) {
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();
line += 1;
}
}
@@ -177,7 +175,7 @@ mod tests {
v.extend(vec![1u8; 16]);
v.extend(vec![2u8; 16]);
let mut s = Squeezer::new(true);
let mut s = Squeezer::new(true, LSIZE);
// just initialized
assert_eq!(s.action(), SqueezeAction::Ignore);
s.advance();
@@ -188,9 +186,8 @@ mod tests {
SqueezeAction::Ignore, // different
];
let mut line = 0;
let mut idx = 1;
for z in v.chunks(LSIZE) {
for (line, z) in v.chunks(LSIZE_USIZE).enumerate() {
for i in z {
s.process(*i, idx);
idx += 1;
@@ -198,7 +195,6 @@ mod tests {
let action = s.action();
assert_eq!(action, exp[line]);
s.advance();
line += 1;
}
}
@@ -207,10 +203,10 @@ mod tests {
/// print
fn one_squeeze_no_delete() {
const LINES: usize = 3;
let mut v = vec![0u8; (LINES - 1) * LSIZE];
let mut v = vec![0u8; (LINES - 1) * LSIZE_USIZE];
v.extend(vec![1u8; 16]);
let mut s = Squeezer::new(true);
let mut s = Squeezer::new(true, LSIZE);
// just initialized
assert_eq!(s.action(), SqueezeAction::Ignore);
s.advance();
@@ -221,9 +217,8 @@ mod tests {
SqueezeAction::Ignore, // different lines, print again
];
let mut line = 0;
let mut idx = 1;
for z in v.chunks(LSIZE) {
for (line, z) in v.chunks(LSIZE_USIZE).enumerate() {
for i in z {
s.process(*i, idx);
idx += 1;
@@ -231,7 +226,6 @@ mod tests {
let action = s.action();
s.advance();
assert_eq!(action, exp[line]);
line += 1;
}
}
@@ -239,11 +233,11 @@ mod tests {
/// 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];
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);
let mut s = Squeezer::new(true, LSIZE);
// just initialized
assert_eq!(s.action(), SqueezeAction::Ignore);
s.advance();
@@ -253,9 +247,8 @@ mod tests {
SqueezeAction::Ignore, // print squeeze symbol
];
let mut line = 0;
let mut idx = 1;
for z in v.chunks(LSIZE) {
for (line, z) in v.chunks(LSIZE_USIZE).enumerate() {
for i in z {
s.process(*i, idx);
idx += 1;
@@ -263,7 +256,6 @@ mod tests {
let action = s.action();
s.advance();
assert_eq!(action, exp[line]);
line += 1;
}
}
@@ -278,7 +270,7 @@ mod tests {
v.extend(vec![0u8; 8]);
v.extend(vec![1u8; 8]);
let mut s = Squeezer::new(true);
let mut s = Squeezer::new(true, LSIZE);
// just initialized
assert_eq!(s.action(), SqueezeAction::Ignore);
s.advance();
@@ -289,9 +281,8 @@ mod tests {
SqueezeAction::Ignore, // print squeeze symbol
];
let mut line = 0;
let mut idx = 1;
for z in v.chunks(LSIZE) {
for (line, z) in v.chunks(LSIZE_USIZE).enumerate() {
for i in z {
s.process(*i, idx);
idx += 1;
@@ -299,7 +290,6 @@ mod tests {
let action = s.action();
s.advance();
assert_eq!(action, exp[line]);
line += 1;
}
}
@@ -319,7 +309,7 @@ mod tests {
v.extend(vec![20u8; 16]); // print
v.extend(vec![0u8; 12]); // print, only 12 bytes
let mut s = Squeezer::new(true);
let mut s = Squeezer::new(true, LSIZE);
// just initialized
assert_eq!(s.action(), SqueezeAction::Ignore);
s.advance();
@@ -339,9 +329,8 @@ mod tests {
SqueezeAction::Ignore,
];
let mut line = 0;
let mut idx = 1;
for z in v.chunks(LSIZE) {
for (line, z) in v.chunks(LSIZE_USIZE).enumerate() {
for i in z {
s.process(*i, idx);
idx += 1;
@@ -349,7 +338,6 @@ mod tests {
let action = s.action();
s.advance();
assert_eq!(action, exp[line]);
line += 1;
}
}
@@ -363,7 +351,7 @@ mod tests {
v.extend(vec![20u8; 16]);
v.extend(vec![20u8; 16]);
let mut s = Squeezer::new(true);
let mut s = Squeezer::new(true, LSIZE);
// just initialized
assert_eq!(s.action(), SqueezeAction::Ignore);
s.advance();
@@ -375,16 +363,14 @@ mod tests {
SqueezeAction::Print, // print '*' char
];
let mut line = 0;
let mut idx = 1;
for z in v.chunks(LSIZE) {
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();
line += 1;
}
}
@@ -397,7 +383,7 @@ mod tests {
v.extend(vec![20u8; 15]);
v.extend(vec![20u8; 16]);
let mut s = Squeezer::new(true);
let mut s = Squeezer::new(true, LSIZE);
// just initialized
assert_eq!(s.action(), SqueezeAction::Ignore);
s.advance();
@@ -408,16 +394,14 @@ mod tests {
SqueezeAction::Ignore, // print as is
];
let mut line = 0;
let mut idx = 1;
for z in v.chunks(LSIZE) {
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();
line += 1;
}
}
}
+1
View File
@@ -0,0 +1 @@
ascii text eol=lf
+1
View File
@@ -0,0 +1 @@
0123456789abcde
View File
Whitespace-only changes.
BIN
View File
Binary file not shown.
+273
View File
@@ -0,0 +1,273 @@
use assert_cmd::Command;
fn hexyl() -> Command {
let mut cmd = Command::new(assert_cmd::cargo_bin!("hexyl"));
cmd.current_dir("tests/examples");
cmd
}
mod basic {
use super::hexyl;
#[test]
fn can_print_simple_ascii_file() {
hexyl()
.arg("ascii")
.arg("--color=never")
.assert()
.success()
.stdout(
"┌────────┬─────────────────────────┬─────────────────────────┬────────┬────────┐\n\
│00000000│ 30 31 32 33 34 35 36 37 ┊ 38 39 61 62 63 64 65 0a │01234567┊89abcde_│\n\
└────────┴─────────────────────────┴─────────────────────────┴────────┴────────┘\n",
);
}
#[test]
fn can_read_input_from_stdin() {
hexyl()
.arg("--color=never")
.write_stdin("abc")
.assert()
.success()
.stdout(
"┌────────┬─────────────────────────┬─────────────────────────┬────────┬────────┐\n\
│00000000│ 61 62 63 ┊ │abc ┊ │\n\
└────────┴─────────────────────────┴─────────────────────────┴────────┴────────┘\n",
);
}
#[test]
fn fails_on_non_existing_input() {
hexyl().arg("non-existing").assert().failure();
}
#[test]
fn prints_warning_on_empty_content() {
hexyl()
.arg("empty")
.arg("--color=never")
.assert()
.success()
.stdout(
"┌────────┬─────────────────────────┬─────────────────────────┬────────┬────────┐\n\
│ │ No content to print │ │ │ │\n\
└────────┴─────────────────────────┴─────────────────────────┴────────┴────────┘\n",
);
}
}
mod length {
use super::hexyl;
#[test]
fn length_restricts_output_size() {
hexyl()
.arg("hello_world_elf64")
.arg("--color=never")
.arg("--length=32")
.assert()
.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\
└────────┴─────────────────────────┴─────────────────────────┴────────┴────────┘\n",
);
}
#[test]
fn fail_if_length_and_bytes_options_are_used_simultaneously() {
hexyl()
.arg("hello_world_elf64")
.arg("--length=32")
.arg("--bytes=10")
.assert()
.failure();
}
#[test]
fn fail_if_length_and_count_options_are_used_simultaneously() {
hexyl()
.arg("hello_world_elf64")
.arg("--length=32")
.arg("-l=10")
.assert()
.failure();
}
}
mod bytes {
use super::hexyl;
#[test]
fn fail_if_bytes_and_count_options_are_used_simultaneously() {
hexyl()
.arg("hello_world_elf64")
.arg("--bytes=32")
.arg("-l=10")
.assert()
.failure();
}
}
mod skip {
use super::hexyl;
#[test]
fn basic() {
hexyl()
.arg("ascii")
.arg("--color=never")
.arg("--skip=2")
.arg("--length=4")
.assert()
.success()
.stdout(
"┌────────┬─────────────────────────┬─────────────────────────┬────────┬────────┐\n\
│00000002│ 32 33 34 35 ┊ │2345 ┊ │\n\
└────────┴─────────────────────────┴─────────────────────────┴────────┴────────┘\n",
);
}
#[test]
fn prints_warning_when_skipping_past_the_end() {
hexyl()
.arg("ascii")
.arg("--color=never")
.arg("--skip=1000")
.assert()
.success()
.stdout(
"┌────────┬─────────────────────────┬─────────────────────────┬────────┬────────┐\n\
│ │ No content to print │ │ │ │\n\
└────────┴─────────────────────────┴─────────────────────────┴────────┴────────┘\n",
);
}
#[test]
fn negative_offset() {
hexyl()
.arg("ascii")
.arg("--color=never")
.arg("--skip=-4")
.arg("--length=3")
.assert()
.success()
.stdout(
"┌────────┬─────────────────────────┬─────────────────────────┬────────┬────────┐\n\
│0000000c│ 63 64 65 ┊ │cde ┊ │\n\
└────────┴─────────────────────────┴─────────────────────────┴────────┴────────┘\n",
);
}
#[test]
fn fails_if_negative_offset_is_too_large() {
hexyl()
.arg("ascii")
.arg("--color=never")
.arg("--skip=-1MiB")
.assert()
.failure()
.stderr(predicates::str::contains("Failed to jump"));
}
}
mod display_offset {
use super::hexyl;
#[test]
fn basic() {
hexyl()
.arg("ascii")
.arg("--color=never")
.arg("--display-offset=0xc0ffee")
.assert()
.success()
.stdout(
"┌────────┬─────────────────────────┬─────────────────────────┬────────┬────────┐\n\
│00c0ffee│ 30 31 32 33 34 35 36 37 ┊ 38 39 61 62 63 64 65 0a │01234567┊89abcde_│\n\
└────────┴─────────────────────────┴─────────────────────────┴────────┴────────┘\n",
);
}
#[test]
fn display_offset_and_skip() {
hexyl()
.arg("hello_world_elf64")
.arg("--color=never")
.arg("--display-offset=0x20")
.arg("--skip=0x10")
.arg("--length=0x10")
.assert()
.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\
└────────┴─────────────────────────┴─────────────────────────┴────────┴────────┘\n",
);
}
}
mod blocksize {
use super::hexyl;
#[test]
fn fails_for_zero_or_negative_blocksize() {
hexyl()
.arg("ascii")
.arg("--block-size=0")
.assert()
.failure();
hexyl()
.arg("ascii")
.arg("--block-size=-16")
.assert()
.failure();
}
}
mod display_settings {
use super::hexyl;
#[test]
fn plain() {
hexyl()
.arg("ascii")
.arg("--plain")
.assert()
.success()
.stdout(" 30 31 32 33 34 35 36 37 38 39 61 62 63 64 65 0a \n");
}
#[test]
fn no_chars() {
hexyl()
.arg("ascii")
.arg("--no-characters")
.arg("--color=never")
.assert()
.success()
.stdout(
"┌────────┬─────────────────────────┬─────────────────────────┐\n\
│00000000│ 30 31 32 33 34 35 36 37 ┊ 38 39 61 62 63 64 65 0a │\n\
└────────┴─────────────────────────┴─────────────────────────┘\n",
);
}
#[test]
fn no_position() {
hexyl()
.arg("ascii")
.arg("--no-position")
.arg("--color=never")
.assert()
.success()
.stdout(
"┌─────────────────────────┬─────────────────────────┬────────┬────────┐\n\
│ 30 31 32 33 34 35 36 37 ┊ 38 39 61 62 63 64 65 0a │01234567┊89abcde_│\n\
└─────────────────────────┴─────────────────────────┴────────┴────────┘\n",
);
}
}