mirror of
https://github.com/sharkdp/hexyl.git
synced 2026-10-27 21:28:18 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
421bd73ec1 | ||
|
|
e888021a48 | ||
|
|
7d38f70918 | ||
|
|
c6af2edda8 | ||
|
|
a7fadfaaf2 | ||
|
|
8577a17b14 | ||
|
|
2d8b600c48 | ||
|
|
f8601e5bc9 | ||
|
|
9770b89cf4 | ||
|
|
571312dd6e | ||
|
|
fcd09fffac | ||
|
|
188e753675 | ||
|
|
030235afb7 | ||
|
|
7847e8cd33 | ||
|
|
32f4ce9987 | ||
|
|
2a4b17a162 | ||
|
|
5e3c49f933 | ||
|
|
fc794b290c | ||
|
|
e6000a56f4 | ||
|
|
2861cd51ac | ||
|
|
a8cffbb6df | ||
|
|
7bb6343d2a | ||
|
|
7de17c4f35 | ||
|
|
ec61c2a789 | ||
|
|
7f7b88a914 | ||
|
|
85fa75ba67 | ||
|
|
d5d645fc48 | ||
|
|
8d3aa65134 | ||
|
|
252b4e84c3 | ||
|
|
3dc118d0ad | ||
|
|
b1ff8b6ce4 | ||
|
|
fe65792b96 | ||
|
|
31c0bcfeae | ||
|
|
c0f94ad6aa | ||
|
|
55fed5e3c0 | ||
|
|
aeee0a1033 | ||
|
|
179c8f40a2 | ||
|
|
fb2500d7d6 | ||
|
|
ad9ecda952 | ||
|
|
c43effefd6 | ||
|
|
0b6454ed9d | ||
|
|
557ae66ed5 | ||
|
|
df1afdc73f | ||
|
|
9106b899b2 | ||
|
|
df1c36e5a7 | ||
|
|
ac610f7bad | ||
|
|
767740865f | ||
|
|
1fb324f411 | ||
|
|
3d0bc3d0eb | ||
|
|
8a52261a50 | ||
|
|
ccc4fb9ba9 | ||
|
|
0baa22f152 | ||
|
|
165ced9307 | ||
|
|
3888a20dfe | ||
|
|
75b37fa125 | ||
|
|
794e1935e3 | ||
|
|
c49aa079b4 | ||
|
|
675ef6d4a8 | ||
|
|
eade38c94a | ||
|
|
98ceec18cd | ||
|
|
a49f295acc | ||
|
|
0caab15255 | ||
|
|
828fb210ce | ||
|
|
1e43783616 | ||
|
|
6b7cfefec6 | ||
|
|
5f82942f98 | ||
|
|
f6a1c4e8e3 | ||
|
|
b4fb1b079c | ||
|
|
f9f59d653d | ||
|
|
c82695f0a9 | ||
|
|
9d938b11ed | ||
|
|
d5982c0eb0 | ||
|
|
1502419ad4 | ||
|
|
f5be845d70 | ||
|
|
664025477b | ||
|
|
074bc791c2 | ||
|
|
77a4ad25f1 | ||
|
|
ca31d466ba | ||
|
|
945e7a3472 | ||
|
|
75d612ff11 | ||
|
|
15e8abfc9a | ||
|
|
b36d74ed9d | ||
|
|
094bf63f02 | ||
|
|
e0e870de27 | ||
|
|
966421c522 | ||
|
|
166c2cc46b | ||
|
|
74a5e4bc84 | ||
|
|
363ea0a586 | ||
|
|
1c4c7ed9a5 | ||
|
|
b5c224c119 | ||
|
|
d6e34d9dd2 | ||
|
|
1cb346bd13 | ||
|
|
daa40900bd | ||
|
|
d98c6edb55 | ||
|
|
73bfc68cd0 | ||
|
|
5d3f36d066 | ||
|
|
d214bc63f1 | ||
|
|
a6aeabfde4 | ||
|
|
9602667991 | ||
|
|
9ccf18f4c0 | ||
|
|
aa921ca184 | ||
|
|
4823523b7d | ||
|
|
4569c8f871 | ||
|
|
80163764c3 | ||
|
|
9bc81820e7 | ||
|
|
273ef0c045 | ||
|
|
ac1a67e90c | ||
|
|
12324d615a | ||
|
|
6b062ba127 | ||
|
|
8c7c3a073c | ||
|
|
0565901fad | ||
|
|
41b3241718 | ||
|
|
975a406d20 | ||
|
|
aa43c20f39 | ||
|
|
1fdd770093 | ||
|
|
a3fde45c18 | ||
|
|
85659a9cd6 | ||
|
|
2a4a22c9cb | ||
|
|
b820bdfeca | ||
|
|
b9348ffb1d | ||
|
|
55cf7b034c | ||
|
|
c622c8217f | ||
|
|
49484fa0f0 | ||
|
|
3661b06b60 | ||
|
|
f5814d4442 | ||
|
|
49ebf4771f | ||
|
|
4904a7e26f | ||
|
|
becc1b80f1 | ||
|
|
1fb5263c3b | ||
|
|
0ae89168ad | ||
|
|
6873867658 | ||
|
|
f15e049bf5 | ||
|
|
1d04cdf119 | ||
|
|
5d92f07e21 | ||
|
|
a4ed50e1f0 | ||
|
|
f7f7447994 | ||
|
|
e4f0e8a092 | ||
|
|
a23645ddfd | ||
|
|
7da04c6bff | ||
|
|
c497c1fac9 | ||
|
|
653cf02954 | ||
|
|
18cdbe02c5 | ||
|
|
d1ae68585f | ||
|
|
7a225fe732 | ||
|
|
1b33e516d9 | ||
|
|
55d02c933a | ||
|
|
b8ed8d5df9 | ||
|
|
641c32e395 | ||
|
|
51b28ed0b0 | ||
|
|
825100c6d6 | ||
|
|
1b1fc4b053 | ||
|
|
c3dd4e871e | ||
|
|
923bceffaa | ||
|
|
2942d2419f | ||
|
|
37d4180f9a | ||
|
|
99435cd215 | ||
|
|
27f039d72a | ||
|
|
e72422ecfb | ||
|
|
3c56ed2b0e | ||
|
|
a09ea8ee27 | ||
|
|
d6bdfe9897 | ||
|
|
7c9f7e1bd0 | ||
|
|
b1d2a47590 | ||
|
|
c3e9cb9518 | ||
|
|
3489998c67 | ||
|
|
4594d29f73 | ||
|
|
cc73f01c34 | ||
|
|
6da15cd1e9 | ||
|
|
c82ccd5a59 | ||
|
|
19d2805729 | ||
|
|
06387bba3e | ||
|
|
595803e5e2 | ||
|
|
3793559d6f | ||
|
|
71626c28e9 | ||
|
|
532eab762d | ||
|
|
642eff6061 | ||
|
|
15dc96a946 | ||
|
|
6b2cc80cc1 | ||
|
|
1db77094da | ||
|
|
8c3f2230c6 | ||
|
|
66f8508b57 | ||
|
|
afb249e854 | ||
|
|
9432677d3a | ||
|
|
524a6c607f | ||
|
|
d69a7d8e04 | ||
|
|
b1c8606918 | ||
|
|
d9723592bf | ||
|
|
23fbb5b027 | ||
|
|
4d7004f06b | ||
|
|
c4ed6d9936 | ||
|
|
aab0596ea9 | ||
|
|
7500efffa5 | ||
|
|
80e8026416 | ||
|
|
13452f5174 | ||
|
|
48ff6ff956 | ||
|
|
54cab89821 | ||
|
|
bb6a3c0650 | ||
|
|
09c22e09b9 | ||
|
|
cc5b308fc9 | ||
|
|
153b1cccdf | ||
|
|
7217a60e6e | ||
|
|
13aec946a3 | ||
|
|
e0da7b3f82 | ||
|
|
e29a63358d | ||
|
|
2bdf5d768f | ||
|
|
a7892881f3 | ||
|
|
921a138962 | ||
|
|
f8f410b8ee | ||
|
|
7307077a66 | ||
|
|
3ca002638c | ||
|
|
ccfceaf58c | ||
|
|
751b670d5b | ||
|
|
478cc25ffd | ||
|
|
b1c4b0bf2b | ||
|
|
1951c59b0c | ||
|
|
ddc3a5c04a | ||
|
|
6d5c61b671 | ||
|
|
c5c8d1190f | ||
|
|
1459616d27 | ||
|
|
8af8a94699 | ||
|
|
07cdc97e74 | ||
|
|
bcca1fae03 | ||
|
|
48ad95efe6 | ||
|
|
3b3889967c | ||
|
|
57f1fb4dc4 | ||
|
|
ef40be0d53 | ||
|
|
6b235b1d2e | ||
|
|
7cff10b274 | ||
|
|
5327b71215 | ||
|
|
c336259159 | ||
|
|
133fdd9156 | ||
|
|
244f71691d | ||
|
|
6f73f4c0f0 | ||
|
|
7843b2d038 | ||
|
|
692ff64e4b | ||
|
|
1d156282da | ||
|
|
b4bdf0f85e | ||
|
|
2bda250906 | ||
|
|
f32431e552 | ||
|
|
f9cb2bc872 | ||
|
|
f71ad1e00d | ||
|
|
3b01d0ce19 | ||
|
|
abc79396df | ||
|
|
67b6c2533d | ||
|
|
c75a882081 | ||
|
|
81787d0858 | ||
|
|
8e77507686 | ||
|
|
9c46ced572 | ||
|
|
161ddd7ea1 | ||
|
|
53110bac6f | ||
|
|
d64053771c | ||
|
|
23f199ffbd | ||
|
|
175847787d | ||
|
|
622a084ea6 | ||
|
|
4c48eedb18 | ||
|
|
ca8970b5cc | ||
|
|
fb49b633b2 | ||
|
|
82c34fe39b | ||
|
|
c4028bb213 | ||
|
|
def066ff92 | ||
|
|
17c2c61bad | ||
|
|
2ad15413b9 | ||
|
|
710c86f6a1 | ||
|
|
a2ea80e960 | ||
|
|
3390bedee9 | ||
|
|
6978f7a277 | ||
|
|
2c7c857ccc | ||
|
|
55adb2db2c | ||
|
|
3a62809c8c | ||
|
|
7cc0b51537 | ||
|
|
8363e3f37e | ||
|
|
07c222947d | ||
|
|
2566d7b9f9 | ||
|
|
8bd7677c9b | ||
|
|
6a848b14f8 | ||
|
|
c6c4dd416e | ||
|
|
f918bfb911 | ||
|
|
10e3535999 | ||
|
|
8725f8cd25 | ||
|
|
c8e65d7528 | ||
|
|
170573fad0 | ||
|
|
880807619f | ||
|
|
7e46a5d788 | ||
|
|
19030470f9 | ||
|
|
b27ab61608 | ||
|
|
39a83021dc | ||
|
|
f6f41e76aa | ||
|
|
74861353fa | ||
|
|
78d8cc516f | ||
|
|
faebee7b8c | ||
|
|
92c974d066 | ||
|
|
866feb2966 | ||
|
|
952fff0ee4 | ||
|
|
1619cd5afe | ||
|
|
20694bb628 | ||
|
|
98311282e5 | ||
|
|
afb6669477 | ||
|
|
0dad8bf481 | ||
|
|
612a227a81 | ||
|
|
351fcf0aea | ||
|
|
3c3d91cd51 | ||
|
|
7b7a0ff32d | ||
|
|
2e403a2d11 | ||
|
|
155b14e865 | ||
|
|
8429b87594 | ||
|
|
1cddf395a8 | ||
|
|
2a1ae1d295 | ||
|
|
57e1c67bfb |
No files matched your search
@@ -0,0 +1,342 @@
|
||||
name: CICD
|
||||
|
||||
env:
|
||||
MIN_SUPPORTED_RUST_VERSION: "1.60.0"
|
||||
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 }}
|
||||
@@ -1,2 +1,5 @@
|
||||
/target
|
||||
**/*.rs.bk
|
||||
|
||||
# Generated files
|
||||
hexyl.1
|
||||
-79
@@ -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.29.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 != ""
|
||||
|
||||
notifications:
|
||||
email:
|
||||
on_success: never
|
||||
+180
@@ -0,0 +1,180 @@
|
||||
# unreleased
|
||||
|
||||
## Features
|
||||
|
||||
- Significantly improved performance, see #173 and #176 (@sharifhsn)
|
||||
- Added variable panels through the `--panels` and `--terminal-width` flags, see [#13](https://github.com/sharkdp/hexyl/issues/13) and [#164](https://github.com/sharkdp/hexyl/pull/164) (@sharifhsn)
|
||||
- Added new `--group-bytes`/`-g` option, see #104 and #170 (@RinHizakura)
|
||||
- Added new `--base B` option (where `B` can be `binary`, `octal`, `decimal` or `hexadecimal`), see #147 and #178 (@sharifhsn)
|
||||
- Show actual zero bytes as `⋄` in the character panel (previously: `0`), in order not to confuse them with ASCII
|
||||
`0` bytes if colors are deactivated. Closes #166 (@sharkdp)
|
||||
|
||||
## `hexyl` as a library
|
||||
|
||||
- Breaking change: `Printer::new` is deprecated as a part of the public API. Alternatively, you can now construct a `Printer` using the `PrinterBuilder` builder API, see [#168](https://github.com/sharkdp/hexyl/pull/168). (@sharifhsn)
|
||||
|
||||
## Other
|
||||
|
||||
- More tests for the squeezing feature, see #177 (@mkatychev)
|
||||
|
||||
## Thank you
|
||||
|
||||
Special thanks go to @sharifhsn, not just for the new features,
|
||||
bugfixes and performance improvements. But also for many internal
|
||||
improvements of the code base and other maintenance tasks.
|
||||
|
||||
|
||||
# v0.10.0
|
||||
|
||||
## 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
|
||||
@@ -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
+500
-99
@@ -1,166 +1,567 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "ansi_term"
|
||||
version = "0.11.0"
|
||||
name = "aho-corasick"
|
||||
version = "0.7.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac"
|
||||
dependencies = [
|
||||
"winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "atty"
|
||||
version = "0.2.11"
|
||||
name = "anstyle"
|
||||
version = "0.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "58bb0ec944fe041c41e32dcd60b49327fcb7ff761433838878cc97ddecf16db7"
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.66"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "216261ddc8289130e551ddcd5ce8a064710c0d064a4d2895c67151c92b5443f6"
|
||||
|
||||
[[package]]
|
||||
name = "assert_cmd"
|
||||
version = "2.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa3d466004a8b4cb1bc34044240a2fd29d17607e2e3bd613eb44fd48e8100da3"
|
||||
dependencies = [
|
||||
"libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"bstr",
|
||||
"doc-comment",
|
||||
"predicates",
|
||||
"predicates-core",
|
||||
"predicates-tree",
|
||||
"wait-timeout",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.0.4"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "bstr"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fca0852af221f458706eb0725c03e4ed6c46af9ac98e6a689d5e634215d594dd"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"once_cell",
|
||||
"regex-automata",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.77"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e9f73505338f7d905b19d18738976aae232eb46b8efc15554ffc56deb5d9ebe4"
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "2.32.0"
|
||||
version = "4.0.29"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4d63b9e9c07271b9957ad22c173bae2a4d9a81127680962039296abcd2f8251d"
|
||||
dependencies = [
|
||||
"ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"term_size 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"textwrap 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"bitflags",
|
||||
"clap_lex",
|
||||
"is-terminal",
|
||||
"once_cell",
|
||||
"strsim",
|
||||
"termcolor",
|
||||
"terminal_size",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0d4198f73e42b4936b35b5bb248d81d2b595ecb170da0bac7655c54eedfa8da8"
|
||||
dependencies = [
|
||||
"os_str_bytes",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "const_format"
|
||||
version = "0.2.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7309d9b4d3d2c0641e018d449232f2e28f1b22933c137f157d3dbc14228b8c0e"
|
||||
dependencies = [
|
||||
"const_format_proc_macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "const_format_proc_macros"
|
||||
version = "0.2.29"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d897f47bf7270cf70d370f8f98c1abb6d2d4cf60a6845d30e05bfb90c6568650"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ctor"
|
||||
version = "0.1.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6d2301688392eb071b0bf1a37be05c469d3cc4dbbd95df672fe28ab021e6a096"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "diff"
|
||||
version = "0.1.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8"
|
||||
|
||||
[[package]]
|
||||
name = "difflib"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8"
|
||||
|
||||
[[package]]
|
||||
name = "doc-comment"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10"
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797"
|
||||
|
||||
[[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 = "hermit-abi"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hexyl"
|
||||
version = "0.3.0"
|
||||
version = "0.11.0"
|
||||
dependencies = [
|
||||
"ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"anstyle",
|
||||
"anyhow",
|
||||
"assert_cmd",
|
||||
"clap",
|
||||
"const_format",
|
||||
"is-terminal",
|
||||
"libc",
|
||||
"predicates",
|
||||
"pretty_assertions",
|
||||
"terminal_size",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "kernel32-sys"
|
||||
version = "0.2.2"
|
||||
name = "io-lifetimes"
|
||||
version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "46112a93252b123d31a119a8d1a1ac19deac4fac6e0e8b0df58f0d4e5870e63c"
|
||||
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)",
|
||||
"libc",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "is-terminal"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "927609f78c2913a6f6ac3c27a4fe87f43e2a35367c0c4b0f8265e8f49a104330"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"io-lifetimes",
|
||||
"rustix",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.10.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
|
||||
dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.45"
|
||||
version = "0.2.138"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "db6d7e329c562c5dfab7a46a2afabc8b987ab9a4834c9d1ca04dc54c1546cef8"
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.1.49"
|
||||
name = "linux-raw-sys"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f9f08d8963a6c613f4b1a78f4f4a4dbfadf8e6545b2d72861731e4858b8b47f"
|
||||
|
||||
[[package]]
|
||||
name = "redox_termios"
|
||||
version = "0.1.1"
|
||||
name = "memchr"
|
||||
version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
|
||||
|
||||
[[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 = [
|
||||
"redox_syscall 0.1.49 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.16.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860"
|
||||
|
||||
[[package]]
|
||||
name = "os_str_bytes"
|
||||
version = "6.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee"
|
||||
|
||||
[[package]]
|
||||
name = "output_vt100"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "628223faebab4e3e40667ee0b2336d34a5b960ff60ea743ddfdbcf7770bcfb66"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "predicates"
|
||||
version = "2.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f54fc5dc63ed3bbf19494623db4f3af16842c0d975818e469022d09e53f0aa05"
|
||||
dependencies = [
|
||||
"difflib",
|
||||
"float-cmp",
|
||||
"itertools",
|
||||
"normalize-line-endings",
|
||||
"predicates-core",
|
||||
"regex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "predicates-core"
|
||||
version = "1.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72f883590242d3c6fc5bf50299011695fa6590c2c70eac95ee1bdb9a733ad1a2"
|
||||
|
||||
[[package]]
|
||||
name = "predicates-tree"
|
||||
version = "1.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "54ff541861505aabf6ea722d2131ee980b8276e10a1297b94e896dd8b621850d"
|
||||
dependencies = [
|
||||
"predicates-core",
|
||||
"termtree",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pretty_assertions"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a25e9bcb20aa780fd0bb16b72403a9064d6b3f22f026946029acb941a50af755"
|
||||
dependencies = [
|
||||
"ctor",
|
||||
"diff",
|
||||
"output_vt100",
|
||||
"yansi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.47"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e076559ef8e241f2ae3479e36f97bd5741c0330689e217ad51ce2c76808b868a"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.6.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848"
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.36.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cb93e85278e08bb5788653183213d3a60fc242b10cb9be96586f5a73dcb67c23"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"errno",
|
||||
"io-lifetimes",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.149"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "256b9932320c590e707b94576e3cc1f7c9024d0ee6612dfbcf1cb106cbe8e055"
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "term_size"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "termion"
|
||||
version = "1.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"redox_syscall 0.1.49 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "textwrap"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.105"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "60b9b43d45702de4c839cb9b51d9f529c5dd26a4aff255b42b1ebc03e88ee908"
|
||||
dependencies = [
|
||||
"term_size 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.1.5"
|
||||
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 = "winapi"
|
||||
version = "0.2.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.6"
|
||||
name = "termcolor"
|
||||
version = "1.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755"
|
||||
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)",
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-build"
|
||||
version = "0.1.1"
|
||||
name = "terminal_size"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cb20089a8ba2b69debd491f8d2d023761cbf196e999218c591fa1e7e15a21907"
|
||||
dependencies = [
|
||||
"rustix",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "termtree"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "95059e91184749cb66be6dc994f67f182b6d897cb3df74a5bf66b5e709295fd8"
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.37"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.37"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c"
|
||||
|
||||
[[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 atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9a7d5b8723950951411ee34d271d99dddcc2035a16ab25310ea2c8cfd4369652"
|
||||
"checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12"
|
||||
"checksum clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b957d88f4b6a63b9d70d5f454ac8011819c6efa7727858f458ab71c756ce2d3e"
|
||||
"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
|
||||
"checksum libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)" = "2d2857ec59fadc0773853c664d2d18e7198e83883e7060b63c924cb077bd5c74"
|
||||
"checksum redox_syscall 0.1.49 (registry+https://github.com/rust-lang/crates.io-index)" = "f22c50afdcf3f0a31ebb6b47697f6a7c5e5a24967e842858118bce0615f0afad"
|
||||
"checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76"
|
||||
"checksum strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4f380125926a99e52bc279241539c018323fab05ad6368b56f93d9369ff550"
|
||||
"checksum term_size 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9e5b9a66db815dcfd2da92db471106457082577c3c278d4138ab3e3b4e189327"
|
||||
"checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096"
|
||||
"checksum textwrap 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "307686869c93e71f94da64286f9a9524c0f308a9e1c87a583de8e9c9039ad3f6"
|
||||
"checksum unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "882386231c45df4700b275c7ff55b6f3698780a650026380e72dabe76fa46526"
|
||||
"checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a"
|
||||
"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
|
||||
"checksum winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "92c1eb33641e276cfa214a0522acad57be5c56b10cb348b3c5117db75f3ac4b0"
|
||||
"checksum winapi-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.42.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm",
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
"windows_i686_msvc",
|
||||
"windows_x86_64_gnu",
|
||||
"windows_x86_64_gnullvm",
|
||||
"windows_x86_64_msvc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.42.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.42.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.42.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.42.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.42.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.42.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.42.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5"
|
||||
|
||||
[[package]]
|
||||
name = "yansi"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec"
|
||||
+21
-4
@@ -7,11 +7,28 @@ license = "MIT/Apache-2.0"
|
||||
name = "hexyl"
|
||||
readme = "README.md"
|
||||
repository = "https://github.com/sharkdp/hexyl"
|
||||
version = "0.3.0"
|
||||
version = "0.11.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
ansi_term = "0.11"
|
||||
anstyle = "0.2"
|
||||
anyhow = "1.0"
|
||||
const_format = "0.2"
|
||||
is-terminal = "0.4"
|
||||
libc = "0.2"
|
||||
thiserror = "1.0"
|
||||
terminal_size = "0.2"
|
||||
|
||||
[dependencies.clap]
|
||||
version = "2"
|
||||
features = ["suggestions", "color", "wrap_help"]
|
||||
version = "4"
|
||||
default-features = false
|
||||
features = ["std", "suggestions", "color", "wrap_help", "cargo", "help", "usage", "error-context"]
|
||||
|
||||
[dev-dependencies]
|
||||
assert_cmd = "2.0"
|
||||
predicates = "2.0"
|
||||
pretty_assertions = "1.3.0"
|
||||
|
||||
[profile.release]
|
||||
lto = true
|
||||
codegen-units = 1
|
||||
@@ -1,6 +1,6 @@
|
||||

|
||||
|
||||
[](https://travis-ci.org/sharkdp/hexyl)
|
||||
[](https://github.com/sharkdp/hexyl/actions/workflows/CICD.yml)
|
||||
[](https://crates.io/crates/hexyl)
|
||||

|
||||
|
||||
@@ -19,38 +19,156 @@ 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.3.0/hexyl_0.3.0_amd64.deb"
|
||||
sudo dpkg -i hexyl_0.3.0_amd64.deb
|
||||
sudo dpkg -i hexyl_0.11.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
|
||||
|
||||
You can install `hexyl` from [this AUR package](https://aur.archlinux.org/packages/hexyl/):
|
||||
You can install `hexyl` from [the official package repository](https://www.archlinux.org/packages/community/x86_64/hexyl/):
|
||||
|
||||
```
|
||||
yaourt -S hexyl
|
||||
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
|
||||
|
||||
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.29 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 +0,0 @@
|
||||
*.bash linguist-vendored
|
||||
@@ -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
@@ -0,0 +1,148 @@
|
||||
% HEXYL(1) hexyl 0.11.0 | General Commands Manual
|
||||
%
|
||||
% 2022-12-05
|
||||
|
||||
<!--
|
||||
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)
|
||||
@@ -0,0 +1,24 @@
|
||||
use std::io;
|
||||
|
||||
use hexyl::{BorderStyle, PrinterBuilder};
|
||||
|
||||
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 mut printer = PrinterBuilder::new(&mut handle)
|
||||
.show_color(true)
|
||||
.show_char_panel(true)
|
||||
.show_position_panel(true)
|
||||
.with_border_style(BorderStyle::Unicode)
|
||||
.enable_squeezing(false)
|
||||
.num_panels(2)
|
||||
.num_group_bytes(1)
|
||||
.build();
|
||||
printer.print_all(&input[..]).unwrap();
|
||||
}
|
||||
@@ -0,0 +1,876 @@
|
||||
#[macro_use]
|
||||
extern crate clap;
|
||||
|
||||
use std::convert::TryFrom;
|
||||
use std::fs::File;
|
||||
use std::io::{self, prelude::*, BufWriter, SeekFrom};
|
||||
use std::num::{NonZeroI64, NonZeroU64, NonZeroU8};
|
||||
|
||||
use clap::builder::ArgPredicate;
|
||||
use clap::{crate_name, crate_version, Arg, ArgAction, ColorChoice, Command};
|
||||
|
||||
use is_terminal::IsTerminal;
|
||||
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
|
||||
use const_format::formatcp;
|
||||
|
||||
use thiserror::Error as ThisError;
|
||||
|
||||
use terminal_size::terminal_size;
|
||||
|
||||
use hexyl::{Base, BorderStyle, Input, PrinterBuilder};
|
||||
|
||||
const DEFAULT_BLOCK_SIZE: i64 = 512;
|
||||
|
||||
fn run() -> Result<()> {
|
||||
let command = Command::new(crate_name!())
|
||||
.color(ColorChoice::Auto)
|
||||
.max_term_width(90)
|
||||
.version(crate_version!())
|
||||
.about(crate_description!())
|
||||
.arg(
|
||||
Arg::new("FILE")
|
||||
.help("The file to display. If no FILE argument is given, read from STDIN."),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("length")
|
||||
.short('n')
|
||||
.long("length")
|
||||
.num_args(1)
|
||||
.value_name("N")
|
||||
.help(
|
||||
"Only read N bytes from the input. The N argument can also include a \
|
||||
unit with a decimal prefix (kB, MB, ..) or binary prefix (kiB, MiB, ..), \
|
||||
or can be specified using a hex number. \
|
||||
The short option '-l' can be used as an alias.\n\
|
||||
Examples: --length=64, --length=4KiB, --length=0xff",
|
||||
),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("bytes")
|
||||
.short('c')
|
||||
.long("bytes")
|
||||
.num_args(1)
|
||||
.value_name("N")
|
||||
.conflicts_with("length")
|
||||
.help("An alias for -n/--length"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("count")
|
||||
.short('l')
|
||||
.num_args(1)
|
||||
.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")
|
||||
.num_args(1)
|
||||
.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")
|
||||
.num_args(1)
|
||||
.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")
|
||||
.action(ArgAction::SetFalse)
|
||||
.help(
|
||||
"Displays all input data. Otherwise any number of groups of output \
|
||||
lines which would be identical to the preceding group of lines, are \
|
||||
replaced with a line comprised of a single asterisk.",
|
||||
),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("color")
|
||||
.long("color")
|
||||
.num_args(1)
|
||||
.value_name("WHEN")
|
||||
.value_parser(["always", "auto", "never"])
|
||||
.default_value_if("plain", ArgPredicate::IsPresent, 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("border")
|
||||
.long("border")
|
||||
.num_args(1)
|
||||
.value_name("STYLE")
|
||||
.value_parser(["unicode", "ascii", "none"])
|
||||
.default_value_if("plain", ArgPredicate::IsPresent, Some("none"))
|
||||
.default_value("unicode")
|
||||
.help(
|
||||
"Whether to draw a border with Unicode characters, ASCII characters, \
|
||||
or none at all",
|
||||
),
|
||||
)
|
||||
.arg(Arg::new("plain").short('p').long("plain").action(ArgAction::SetTrue).help(
|
||||
"Display output with --no-characters, --no-position, --border=none, and --color=never.",
|
||||
))
|
||||
.arg(
|
||||
Arg::new("no_chars")
|
||||
.short('C')
|
||||
.long("no-characters")
|
||||
.action(ArgAction::SetFalse)
|
||||
.help("Whether to display the character panel on the right."),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("no_position")
|
||||
.short('P')
|
||||
.long("no-position")
|
||||
.action(ArgAction::SetFalse)
|
||||
.help("Whether to display the position panel on the left."),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("display_offset")
|
||||
.short('o')
|
||||
.long("display-offset")
|
||||
.num_args(1)
|
||||
.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("panels")
|
||||
.long("panels")
|
||||
.num_args(1)
|
||||
.value_name("N")
|
||||
.help(
|
||||
"Sets the number of hex data panels to be displayed. \
|
||||
`--panels=auto` will display the maximum number of hex data panels \
|
||||
based on the current terminal width",
|
||||
),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("group_bytes")
|
||||
.short('g')
|
||||
.long("group-bytes")
|
||||
.num_args(1)
|
||||
.value_name("N")
|
||||
.help(
|
||||
"Sets the number of octets per group to be displayed. \
|
||||
The possible options are 1, 2, 4, 8. The default is 1",
|
||||
),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("base")
|
||||
.short('b')
|
||||
.long("base")
|
||||
.num_args(1)
|
||||
.value_name("B")
|
||||
.help(
|
||||
"Sets the base used for the bytes. The possible options are \
|
||||
binary, octal, decimal, and hexadecimal. The default base \
|
||||
is hexadecimal."
|
||||
)
|
||||
)
|
||||
.arg(
|
||||
Arg::new("terminal_width")
|
||||
.long("terminal-width")
|
||||
.num_args(1)
|
||||
.value_name("N")
|
||||
.conflicts_with("panels")
|
||||
.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 panels that can fit in the requested \
|
||||
width but still leave some space to the right.\nCannot be used with other \
|
||||
width-setting options.",
|
||||
),
|
||||
);
|
||||
|
||||
let matches = command.get_matches();
|
||||
|
||||
let stdin = io::stdin();
|
||||
|
||||
let mut reader = match matches.get_one::<String>("FILE") {
|
||||
Some(filename) => Input::File(File::open(filename)?),
|
||||
None => Input::Stdin(stdin.lock()),
|
||||
};
|
||||
|
||||
let block_size = matches
|
||||
.get_one::<String>("block_size")
|
||||
.map(|bs| {
|
||||
if let Some(hex_number) = try_parse_as_hex_number(bs) {
|
||||
return hex_number.map_err(|e| anyhow!(e)).and_then(|x| {
|
||||
PositiveI64::new(x)
|
||||
.ok_or_else(|| anyhow!("block size argument must be positive"))
|
||||
});
|
||||
}
|
||||
let (num, unit) = extract_num_and_unit_from(bs)?;
|
||||
if let Unit::Block { custom_size: _ } = unit {
|
||||
return Err(anyhow!(
|
||||
"can not use 'block(s)' as a unit to specify block size"
|
||||
));
|
||||
};
|
||||
num.checked_mul(unit.get_multiplier())
|
||||
.ok_or_else(|| anyhow!(ByteOffsetParseError::UnitMultiplicationOverflow))
|
||||
.and_then(|x| {
|
||||
PositiveI64::new(x)
|
||||
.ok_or_else(|| anyhow!("block size argument must be positive"))
|
||||
})
|
||||
})
|
||||
.transpose()?
|
||||
.unwrap_or_else(|| PositiveI64::new(DEFAULT_BLOCK_SIZE).unwrap());
|
||||
|
||||
let skip_arg = matches
|
||||
.get_one::<String>("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> {
|
||||
Ok(parse_byte_offset(s, block_size)?
|
||||
.assume_forward_offset_from_start()?
|
||||
.into())
|
||||
};
|
||||
|
||||
let mut reader = if let Some(length) = matches
|
||||
.get_one::<String>("length")
|
||||
.or_else(|| matches.get_one::<String>("bytes"))
|
||||
.or_else(|| matches.get_one::<String>("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.get_one::<String>("color").map(String::as_ref) {
|
||||
Some("never") => false,
|
||||
Some("auto") => std::io::stdout().is_terminal(),
|
||||
_ => true,
|
||||
};
|
||||
|
||||
let border_style = match matches.get_one::<String>("border").map(String::as_ref) {
|
||||
Some("unicode") => BorderStyle::Unicode,
|
||||
Some("ascii") => BorderStyle::Ascii,
|
||||
_ => BorderStyle::None,
|
||||
};
|
||||
|
||||
let &squeeze = matches.get_one::<bool>("nosqueezing").unwrap_or(&true);
|
||||
|
||||
let show_char_panel = *matches.get_one::<bool>("no_chars").unwrap_or(&true)
|
||||
&& !matches.get_one::<bool>("plain").unwrap_or(&false);
|
||||
|
||||
let show_position_panel = *matches.get_one::<bool>("no_position").unwrap_or(&true)
|
||||
&& !matches.get_one::<bool>("plain").unwrap_or(&false);
|
||||
|
||||
let display_offset: u64 = matches
|
||||
.get_one::<String>("display_offset")
|
||||
.map(|s| {
|
||||
parse_byte_count(s).context(anyhow!(
|
||||
"failed to parse `--display-offset` arg {:?} as byte count",
|
||||
s
|
||||
))
|
||||
})
|
||||
.transpose()?
|
||||
.unwrap_or(0);
|
||||
|
||||
let max_panels_fn = |terminal_width: u64, base_digits: u64, group_bytes: u64| {
|
||||
let offset = if show_position_panel { 10 } else { 1 };
|
||||
let col_width = if show_char_panel {
|
||||
((8 / group_bytes) * (base_digits * group_bytes + 1)) + 2 + 8
|
||||
} else {
|
||||
((8 / group_bytes) * (base_digits * group_bytes + 1)) + 2
|
||||
};
|
||||
if (terminal_width - offset) / col_width < 1 {
|
||||
1
|
||||
} else {
|
||||
(terminal_width - offset) / col_width
|
||||
}
|
||||
};
|
||||
|
||||
let base = if let Some(base) = matches.get_one::<String>("base")
|
||||
.map(|s| {
|
||||
if let Ok(base_num) = s.parse::<u8>() {
|
||||
match base_num {
|
||||
2 => Ok(Base::Binary),
|
||||
8 => Ok(Base::Octal),
|
||||
10 => Ok(Base::Decimal),
|
||||
16 => Ok(Base::Hexadecimal),
|
||||
_ => Err(anyhow!("The number provided is not a valid base. Valid bases are 2, 8, 10, and 16.")),
|
||||
}
|
||||
} else {
|
||||
match s.as_str() {
|
||||
"b" | "bin" | "binary" => Ok(Base::Binary),
|
||||
"o" | "oct" | "octal" => Ok(Base::Octal),
|
||||
"d" | "dec" | "decimal" => Ok(Base::Decimal),
|
||||
"x" | "hex" | "hexadecimal" => Ok(Base::Hexadecimal),
|
||||
_ => Err(anyhow!("The base provided is not valid. Valid bases are \"b\", \"o\", \"d\", and \"x\"."))
|
||||
}
|
||||
}
|
||||
}).transpose()? {
|
||||
base
|
||||
} else {
|
||||
Base::Hexadecimal
|
||||
};
|
||||
|
||||
let base_digits = match base {
|
||||
Base::Binary => 8,
|
||||
Base::Octal => 3,
|
||||
Base::Decimal => 3,
|
||||
Base::Hexadecimal => 2,
|
||||
};
|
||||
|
||||
let group_bytes = if let Some(group_bytes) = matches
|
||||
.get_one::<String>("group_bytes")
|
||||
.map(|s| {
|
||||
s.parse::<NonZeroU8>().map(u8::from).context(anyhow!(
|
||||
"failed to parse `--group-bytes`/`-g` arg {:?} as unsigned nonzero integer",
|
||||
s
|
||||
))
|
||||
})
|
||||
.transpose()?
|
||||
{
|
||||
if (group_bytes <= 8) && ((group_bytes & (group_bytes - 1)) == 0) {
|
||||
group_bytes
|
||||
} else {
|
||||
return Err(anyhow!(
|
||||
"Possible options for the `--group-bytes`/`-g` option are 1, 2, 4 or 8. "
|
||||
));
|
||||
}
|
||||
} else {
|
||||
1
|
||||
};
|
||||
|
||||
let panels = if matches.get_one::<String>("panels").map(String::as_ref) == Some("auto") {
|
||||
max_panels_fn(
|
||||
terminal_size().ok_or_else(|| anyhow!("not a TTY"))?.0 .0 as u64,
|
||||
base_digits,
|
||||
group_bytes.into(),
|
||||
)
|
||||
} else if let Some(panels) = matches
|
||||
.get_one::<String>("panels")
|
||||
.map(|s| {
|
||||
s.parse::<NonZeroU64>().map(u64::from).context(anyhow!(
|
||||
"failed to parse `--panels` arg {:?} as unsigned nonzero integer",
|
||||
s
|
||||
))
|
||||
})
|
||||
.transpose()?
|
||||
{
|
||||
panels
|
||||
} else if let Some(terminal_width) = matches
|
||||
.get_one::<String>("terminal_width")
|
||||
.map(|s| {
|
||||
s.parse::<NonZeroU64>().map(u64::from).context(anyhow!(
|
||||
"failed to parse `--terminal-width` arg {:?} as unsigned nonzero integer",
|
||||
s
|
||||
))
|
||||
})
|
||||
.transpose()?
|
||||
{
|
||||
max_panels_fn(terminal_width, base_digits, group_bytes.into())
|
||||
} else {
|
||||
2
|
||||
};
|
||||
|
||||
let stdout = io::stdout();
|
||||
let mut stdout_lock = BufWriter::new(stdout.lock());
|
||||
|
||||
let mut printer = PrinterBuilder::new(&mut stdout_lock)
|
||||
.show_color(show_color)
|
||||
.show_char_panel(show_char_panel)
|
||||
.show_position_panel(show_position_panel)
|
||||
.with_border_style(border_style)
|
||||
.enable_squeezing(squeeze)
|
||||
.num_panels(panels)
|
||||
.num_group_bytes(group_bytes)
|
||||
.with_base(base)
|
||||
.build();
|
||||
printer.display_offset(skip_offset + display_offset);
|
||||
printer.print_all(&mut reader).map_err(|e| anyhow!(e))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let result = run();
|
||||
|
||||
if let Err(err) = result {
|
||||
if let Some(io_error) = err.downcast_ref::<io::Error>() {
|
||||
if io_error.kind() == ::std::io::ErrorKind::BrokenPipe {
|
||||
std::process::exit(0);
|
||||
}
|
||||
}
|
||||
eprintln!("Error: {:?}", err);
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default, Hash, Eq, Ord, PartialEq, PartialOrd)]
|
||||
pub struct NonNegativeI64(i64);
|
||||
|
||||
impl NonNegativeI64 {
|
||||
pub fn new(x: i64) -> Option<Self> {
|
||||
if x.is_negative() {
|
||||
None
|
||||
} else {
|
||||
Some(Self(x))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn into_inner(self) -> i64 {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl From<NonNegativeI64> for u64 {
|
||||
fn from(x: NonNegativeI64) -> u64 {
|
||||
u64::try_from(x.0)
|
||||
.expect("invariant broken: NonNegativeI64 should contain a non-negative i64 value")
|
||||
}
|
||||
}
|
||||
|
||||
#[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,
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -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),
|
||||
}
|
||||
}
|
||||
}
|
||||
+875
@@ -0,0 +1,875 @@
|
||||
pub(crate) mod input;
|
||||
|
||||
pub use input::*;
|
||||
|
||||
use std::io::{self, BufReader, Read, Write};
|
||||
|
||||
use anstyle::{AnsiColor, Reset};
|
||||
|
||||
pub enum Base {
|
||||
Binary,
|
||||
Octal,
|
||||
Decimal,
|
||||
Hexadecimal,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq)]
|
||||
#[repr(usize)]
|
||||
enum ByteColor {
|
||||
Null = 0,
|
||||
Offset = 1,
|
||||
AsciiPrintable = 2,
|
||||
AsciiWhitespace = 3,
|
||||
AsciiOther = 4,
|
||||
NonAscii = 5,
|
||||
Reset = 6,
|
||||
}
|
||||
|
||||
const COLOR_NULL: AnsiColor = AnsiColor::BrightBlack;
|
||||
const COLOR_OFFSET: AnsiColor = AnsiColor::BrightBlack;
|
||||
const COLOR_ASCII_PRINTABLE: AnsiColor = AnsiColor::Cyan;
|
||||
const COLOR_ASCII_WHITESPACE: AnsiColor = AnsiColor::Green;
|
||||
const COLOR_ASCII_OTHER: AnsiColor = AnsiColor::Magenta;
|
||||
const COLOR_NONASCII: AnsiColor = AnsiColor::Yellow;
|
||||
|
||||
pub enum ByteCategory {
|
||||
Null,
|
||||
AsciiPrintable,
|
||||
AsciiWhitespace,
|
||||
AsciiOther,
|
||||
NonAscii,
|
||||
}
|
||||
|
||||
#[derive(PartialEq)]
|
||||
enum Squeezer {
|
||||
Print,
|
||||
Delete,
|
||||
Ignore,
|
||||
Disabled,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
struct Byte(u8);
|
||||
|
||||
impl Byte {
|
||||
fn category(self) -> ByteCategory {
|
||||
if self.0 == 0x00 {
|
||||
ByteCategory::Null
|
||||
} else if self.0.is_ascii_graphic() {
|
||||
ByteCategory::AsciiPrintable
|
||||
} else if self.0.is_ascii_whitespace() {
|
||||
ByteCategory::AsciiWhitespace
|
||||
} else if self.0.is_ascii() {
|
||||
ByteCategory::AsciiOther
|
||||
} else {
|
||||
ByteCategory::NonAscii
|
||||
}
|
||||
}
|
||||
|
||||
fn color(self) -> ByteColor {
|
||||
use crate::ByteCategory::*;
|
||||
|
||||
match self.category() {
|
||||
Null => ByteColor::Null,
|
||||
AsciiPrintable => ByteColor::AsciiPrintable,
|
||||
AsciiWhitespace => ByteColor::AsciiWhitespace,
|
||||
AsciiOther => ByteColor::AsciiOther,
|
||||
NonAscii => ByteColor::NonAscii,
|
||||
}
|
||||
}
|
||||
|
||||
fn as_char(self) -> char {
|
||||
use crate::ByteCategory::*;
|
||||
|
||||
match self.category() {
|
||||
Null => '⋄',
|
||||
AsciiPrintable => self.0 as char,
|
||||
AsciiWhitespace if self.0 == 0x20 => ' ',
|
||||
AsciiWhitespace => '_',
|
||||
AsciiOther => '•',
|
||||
NonAscii => '×',
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct BorderElements {
|
||||
left_corner: char,
|
||||
horizontal_line: char,
|
||||
column_separator: char,
|
||||
right_corner: char,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum BorderStyle {
|
||||
Unicode,
|
||||
Ascii,
|
||||
None,
|
||||
}
|
||||
|
||||
impl BorderStyle {
|
||||
fn header_elems(&self) -> Option<BorderElements> {
|
||||
match self {
|
||||
BorderStyle::Unicode => Some(BorderElements {
|
||||
left_corner: '┌',
|
||||
horizontal_line: '─',
|
||||
column_separator: '┬',
|
||||
right_corner: '┐',
|
||||
}),
|
||||
BorderStyle::Ascii => Some(BorderElements {
|
||||
left_corner: '+',
|
||||
horizontal_line: '-',
|
||||
column_separator: '+',
|
||||
right_corner: '+',
|
||||
}),
|
||||
BorderStyle::None => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn footer_elems(&self) -> Option<BorderElements> {
|
||||
match self {
|
||||
BorderStyle::Unicode => Some(BorderElements {
|
||||
left_corner: '└',
|
||||
horizontal_line: '─',
|
||||
column_separator: '┴',
|
||||
right_corner: '┘',
|
||||
}),
|
||||
BorderStyle::Ascii => Some(BorderElements {
|
||||
left_corner: '+',
|
||||
horizontal_line: '-',
|
||||
column_separator: '+',
|
||||
right_corner: '+',
|
||||
}),
|
||||
BorderStyle::None => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn outer_sep(&self) -> char {
|
||||
match self {
|
||||
BorderStyle::Unicode => '│',
|
||||
BorderStyle::Ascii => '|',
|
||||
BorderStyle::None => ' ',
|
||||
}
|
||||
}
|
||||
|
||||
fn inner_sep(&self) -> char {
|
||||
match self {
|
||||
BorderStyle::Unicode => '┊',
|
||||
BorderStyle::Ascii => '|',
|
||||
BorderStyle::None => ' ',
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PrinterBuilder<'a, Writer: Write> {
|
||||
writer: &'a mut Writer,
|
||||
show_color: bool,
|
||||
show_char_panel: bool,
|
||||
show_position_panel: bool,
|
||||
border_style: BorderStyle,
|
||||
use_squeeze: bool,
|
||||
panels: u64,
|
||||
group_bytes: u8,
|
||||
base: Base,
|
||||
}
|
||||
|
||||
impl<'a, Writer: Write> PrinterBuilder<'a, Writer> {
|
||||
pub fn new(writer: &'a mut Writer) -> Self {
|
||||
PrinterBuilder {
|
||||
writer,
|
||||
show_color: true,
|
||||
show_char_panel: true,
|
||||
show_position_panel: true,
|
||||
border_style: BorderStyle::Unicode,
|
||||
use_squeeze: true,
|
||||
panels: 2,
|
||||
group_bytes: 1,
|
||||
base: Base::Hexadecimal,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn show_color(mut self, show_color: bool) -> Self {
|
||||
self.show_color = show_color;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn show_char_panel(mut self, show_char_panel: bool) -> Self {
|
||||
self.show_char_panel = show_char_panel;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn show_position_panel(mut self, show_position_panel: bool) -> Self {
|
||||
self.show_position_panel = show_position_panel;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_border_style(mut self, border_style: BorderStyle) -> Self {
|
||||
self.border_style = border_style;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn enable_squeezing(mut self, enable: bool) -> Self {
|
||||
self.use_squeeze = enable;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn num_panels(mut self, num: u64) -> Self {
|
||||
self.panels = num;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn num_group_bytes(mut self, num: u8) -> Self {
|
||||
self.group_bytes = num;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_base(mut self, base: Base) -> Self {
|
||||
self.base = base;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn build(self) -> Printer<'a, Writer> {
|
||||
Printer::new(
|
||||
self.writer,
|
||||
self.show_color,
|
||||
self.show_char_panel,
|
||||
self.show_position_panel,
|
||||
self.border_style,
|
||||
self.use_squeeze,
|
||||
self.panels,
|
||||
self.group_bytes,
|
||||
self.base,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Printer<'a, Writer: Write> {
|
||||
idx: u64,
|
||||
/// the buffer containing all the bytes in a line for character printing
|
||||
line_buf: Vec<u8>,
|
||||
writer: &'a mut Writer,
|
||||
show_char_panel: bool,
|
||||
show_position_panel: bool,
|
||||
show_color: bool,
|
||||
colors: Vec<String>,
|
||||
curr_color: Option<ByteColor>,
|
||||
border_style: BorderStyle,
|
||||
byte_hex_panel: Vec<String>,
|
||||
byte_char_panel: Vec<String>,
|
||||
// same as previous but in Fixed(242) gray color, for position panel
|
||||
byte_hex_panel_g: Vec<String>,
|
||||
byte_char_panel_g: Vec<String>,
|
||||
squeezer: Squeezer,
|
||||
display_offset: u64,
|
||||
/// The number of panels to draw.
|
||||
panels: u64,
|
||||
squeeze_byte: usize,
|
||||
/// The number of octets per group.
|
||||
group_bytes: u8,
|
||||
/// The number of digits used to write the base.
|
||||
base_digits: u8,
|
||||
}
|
||||
|
||||
impl<'a, Writer: Write> Printer<'a, Writer> {
|
||||
fn new(
|
||||
writer: &'a mut Writer,
|
||||
show_color: bool,
|
||||
show_char_panel: bool,
|
||||
show_position_panel: bool,
|
||||
border_style: BorderStyle,
|
||||
use_squeeze: bool,
|
||||
panels: u64,
|
||||
group_bytes: u8,
|
||||
base: Base,
|
||||
) -> Printer<'a, Writer> {
|
||||
Printer {
|
||||
idx: 0,
|
||||
line_buf: vec![0x0; 8 * panels as usize],
|
||||
writer,
|
||||
show_char_panel,
|
||||
show_position_panel,
|
||||
show_color,
|
||||
curr_color: None,
|
||||
colors: vec![
|
||||
COLOR_NULL.render_fg().to_string(),
|
||||
COLOR_OFFSET.render_fg().to_string(),
|
||||
COLOR_ASCII_PRINTABLE.render_fg().to_string(),
|
||||
COLOR_ASCII_WHITESPACE.render_fg().to_string(),
|
||||
COLOR_ASCII_OTHER.render_fg().to_string(),
|
||||
COLOR_NONASCII.render_fg().to_string(),
|
||||
Reset.render().to_string(),
|
||||
],
|
||||
border_style,
|
||||
byte_hex_panel: (0u8..=u8::MAX)
|
||||
.map(|i| match base {
|
||||
Base::Binary => format!("{i:08b}"),
|
||||
Base::Octal => format!("{i:03o}"),
|
||||
Base::Decimal => format!("{i:03}"),
|
||||
Base::Hexadecimal => format!("{i:02x}"),
|
||||
})
|
||||
.collect(),
|
||||
byte_char_panel: (0u8..=u8::MAX)
|
||||
.map(|i| format!("{}", Byte(i).as_char()))
|
||||
.collect(),
|
||||
byte_hex_panel_g: (0u8..=u8::MAX).map(|i| format!("{i:02x}")).collect(),
|
||||
byte_char_panel_g: (0u8..=u8::MAX)
|
||||
.map(|i| format!("{}", Byte(i).as_char()))
|
||||
.collect(),
|
||||
squeezer: if use_squeeze {
|
||||
Squeezer::Ignore
|
||||
} else {
|
||||
Squeezer::Disabled
|
||||
},
|
||||
display_offset: 0,
|
||||
panels,
|
||||
squeeze_byte: 0x00,
|
||||
group_bytes,
|
||||
base_digits: match base {
|
||||
Base::Binary => 8,
|
||||
Base::Octal => 3,
|
||||
Base::Decimal => 3,
|
||||
Base::Hexadecimal => 2,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn display_offset(&mut self, display_offset: u64) -> &mut Self {
|
||||
self.display_offset = display_offset;
|
||||
self
|
||||
}
|
||||
|
||||
fn panel_sz(&self) -> usize {
|
||||
// add one to include the trailing space of a group
|
||||
let group_sz = self.base_digits as usize * self.group_bytes as usize + 1;
|
||||
let group_per_panel = 8 / self.group_bytes as usize;
|
||||
// add one to include the leading space
|
||||
1 + group_sz * group_per_panel
|
||||
}
|
||||
|
||||
fn write_border(&mut self, border_elements: BorderElements) -> io::Result<()> {
|
||||
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 h_repeat = h.to_string().repeat(self.panel_sz());
|
||||
|
||||
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.panels - 1 {
|
||||
write!(self.writer, "{h_repeat}{c}", h_repeat = h_repeat, c = c).ok();
|
||||
}
|
||||
if self.show_char_panel {
|
||||
write!(self.writer, "{h_repeat}{c}", h_repeat = h_repeat, c = c).ok();
|
||||
} else {
|
||||
write!(self.writer, "{h_repeat}", h_repeat = h_repeat).ok();
|
||||
}
|
||||
|
||||
if self.show_char_panel {
|
||||
for _ in 0..self.panels - 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();
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn print_header(&mut self) -> io::Result<()> {
|
||||
if let Some(e) = self.border_style.header_elems() {
|
||||
self.write_border(e)?
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn print_footer(&mut self) -> io::Result<()> {
|
||||
if let Some(e) = self.border_style.footer_elems() {
|
||||
self.write_border(e)?
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn print_position_panel(&mut self) -> io::Result<()> {
|
||||
self.writer.write_all(
|
||||
self.border_style
|
||||
.outer_sep()
|
||||
.encode_utf8(&mut [0; 4])
|
||||
.as_bytes(),
|
||||
)?;
|
||||
if self.show_color {
|
||||
self.writer
|
||||
.write_all(self.colors[ByteColor::Offset as usize].as_bytes())?;
|
||||
}
|
||||
if self.show_position_panel {
|
||||
match self.squeezer {
|
||||
Squeezer::Print => {
|
||||
self.writer
|
||||
.write_all(self.byte_char_panel_g[b'*' as usize].as_bytes())?;
|
||||
if self.show_color {
|
||||
self.writer
|
||||
.write_all(self.colors[ByteColor::Reset as usize].as_bytes())?;
|
||||
}
|
||||
self.writer.write_all(b" ")?;
|
||||
}
|
||||
Squeezer::Ignore | Squeezer::Disabled | Squeezer::Delete => {
|
||||
let byte_index: [u8; 8] = (self.idx + self.display_offset).to_be_bytes();
|
||||
let mut i = 0;
|
||||
while byte_index[i] == 0x0 && i < 4 {
|
||||
i += 1;
|
||||
}
|
||||
for &byte in byte_index.iter().skip(i) {
|
||||
self.writer
|
||||
.write_all(self.byte_hex_panel_g[byte as usize].as_bytes())?;
|
||||
}
|
||||
if self.show_color {
|
||||
self.writer
|
||||
.write_all(self.colors[ByteColor::Reset as usize].as_bytes())?;
|
||||
}
|
||||
}
|
||||
}
|
||||
self.writer.write_all(
|
||||
self.border_style
|
||||
.outer_sep()
|
||||
.encode_utf8(&mut [0; 4])
|
||||
.as_bytes(),
|
||||
)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn print_char(&mut self, i: u64) -> io::Result<()> {
|
||||
match self.squeezer {
|
||||
Squeezer::Print | Squeezer::Delete => self.writer.write_all(b" ")?,
|
||||
Squeezer::Ignore | Squeezer::Disabled => {
|
||||
if let Some(&b) = self.line_buf.get(i as usize) {
|
||||
if self.show_color && self.curr_color != Some(Byte(b).color()) {
|
||||
self.writer
|
||||
.write_all(self.colors[Byte(b).color() as usize].as_bytes())?;
|
||||
self.curr_color = Some(Byte(b).color());
|
||||
}
|
||||
self.writer
|
||||
.write_all(self.byte_char_panel[b as usize].as_bytes())?;
|
||||
} else {
|
||||
self.squeezer = Squeezer::Print;
|
||||
}
|
||||
}
|
||||
}
|
||||
if i == 8 * self.panels - 1 {
|
||||
if self.show_color {
|
||||
self.writer
|
||||
.write_all(self.colors[ByteColor::Reset as usize].as_bytes())?;
|
||||
self.curr_color = None;
|
||||
}
|
||||
self.writer.write_all(
|
||||
self.border_style
|
||||
.outer_sep()
|
||||
.encode_utf8(&mut [0; 4])
|
||||
.as_bytes(),
|
||||
)?;
|
||||
} else if i % 8 == 7 {
|
||||
if self.show_color {
|
||||
self.writer
|
||||
.write_all(self.colors[ByteColor::Reset as usize].as_bytes())?;
|
||||
self.curr_color = None;
|
||||
}
|
||||
self.writer.write_all(
|
||||
self.border_style
|
||||
.inner_sep()
|
||||
.encode_utf8(&mut [0; 4])
|
||||
.as_bytes(),
|
||||
)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn print_char_panel(&mut self) -> io::Result<()> {
|
||||
for i in 0..self.line_buf.len() {
|
||||
self.print_char(i as u64)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn print_byte(&mut self, i: usize, b: u8) -> io::Result<()> {
|
||||
match self.squeezer {
|
||||
Squeezer::Print => {
|
||||
if !self.show_position_panel && i == 0 {
|
||||
if self.show_color {
|
||||
self.writer
|
||||
.write_all(self.colors[ByteColor::Offset as usize].as_bytes())?;
|
||||
}
|
||||
self.writer
|
||||
.write_all(self.byte_char_panel_g[b'*' as usize].as_bytes())?;
|
||||
if self.show_color {
|
||||
self.writer
|
||||
.write_all(self.colors[ByteColor::Reset as usize].as_bytes())?;
|
||||
}
|
||||
} else {
|
||||
if i % (self.group_bytes as usize) == 0 {
|
||||
self.writer.write_all(b" ")?;
|
||||
}
|
||||
}
|
||||
for _ in 0..self.base_digits {
|
||||
self.writer.write_all(b" ")?;
|
||||
}
|
||||
}
|
||||
Squeezer::Delete => self.writer.write_all(b" ")?,
|
||||
Squeezer::Ignore | Squeezer::Disabled => {
|
||||
if i % (self.group_bytes as usize) == 0 {
|
||||
self.writer.write_all(b" ")?;
|
||||
}
|
||||
if self.show_color && self.curr_color != Some(Byte(b).color()) {
|
||||
self.writer
|
||||
.write_all(self.colors[Byte(b).color() as usize].as_bytes())?;
|
||||
self.curr_color = Some(Byte(b).color());
|
||||
}
|
||||
self.writer
|
||||
.write_all(self.byte_hex_panel[b as usize].as_bytes())?;
|
||||
}
|
||||
}
|
||||
// byte is last in panel
|
||||
if i % 8 == 7 {
|
||||
if self.show_color {
|
||||
self.curr_color = None;
|
||||
self.writer
|
||||
.write_all(self.colors[ByteColor::Reset as usize].as_bytes())?;
|
||||
}
|
||||
self.writer.write_all(b" ")?;
|
||||
// byte is last in last panel
|
||||
if i as u64 % (8 * self.panels) == 8 * self.panels - 1 {
|
||||
self.writer.write_all(
|
||||
self.border_style
|
||||
.outer_sep()
|
||||
.encode_utf8(&mut [0; 4])
|
||||
.as_bytes(),
|
||||
)?;
|
||||
} else {
|
||||
self.writer.write_all(
|
||||
self.border_style
|
||||
.inner_sep()
|
||||
.encode_utf8(&mut [0; 4])
|
||||
.as_bytes(),
|
||||
)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn print_bytes(&mut self) -> io::Result<()> {
|
||||
for (i, &b) in self.line_buf.clone().iter().enumerate() {
|
||||
self.print_byte(i, b)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Loop through the given `Reader`, printing until the `Reader` buffer
|
||||
/// is exhausted.
|
||||
pub fn print_all<Reader: Read>(&mut self, reader: Reader) -> io::Result<()> {
|
||||
let mut is_empty = true;
|
||||
|
||||
let mut buf = BufReader::new(reader);
|
||||
|
||||
let leftover = loop {
|
||||
// read a maximum of 8 * self.panels bytes from the reader
|
||||
if let Ok(n) = buf.read(&mut self.line_buf) {
|
||||
if n > 0 && n < 8 * self.panels as usize {
|
||||
// if less are read, that indicates end of file after
|
||||
if is_empty {
|
||||
self.print_header()?;
|
||||
is_empty = false;
|
||||
}
|
||||
|
||||
// perform second check on read
|
||||
if let Ok(0) = buf.read(&mut self.line_buf[n..]) {
|
||||
self.line_buf.resize(n, 0);
|
||||
break Some(n);
|
||||
};
|
||||
} else if n == 0 {
|
||||
// if no bytes are read, that indicates end of file
|
||||
if self.squeezer == Squeezer::Delete {
|
||||
// empty the last line when ending is squeezed
|
||||
self.line_buf.clear();
|
||||
break Some(0);
|
||||
}
|
||||
break None;
|
||||
}
|
||||
}
|
||||
if is_empty {
|
||||
self.print_header()?;
|
||||
is_empty = false;
|
||||
}
|
||||
|
||||
// squeeze is active, check if the line is the same
|
||||
// skip print if still squeezed, otherwise print and deactivate squeeze
|
||||
if matches!(self.squeezer, Squeezer::Print | Squeezer::Delete) {
|
||||
if self
|
||||
.line_buf
|
||||
.chunks_exact(std::mem::size_of::<usize>())
|
||||
.all(|w| usize::from_ne_bytes(w.try_into().unwrap()) == self.squeeze_byte)
|
||||
{
|
||||
if self.squeezer == Squeezer::Delete {
|
||||
self.idx += 8 * self.panels;
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
self.squeezer = Squeezer::Ignore;
|
||||
}
|
||||
}
|
||||
|
||||
// print the line
|
||||
self.print_position_panel()?;
|
||||
self.print_bytes()?;
|
||||
if self.show_char_panel {
|
||||
self.print_char_panel()?;
|
||||
}
|
||||
self.writer.write_all(b"\n")?;
|
||||
|
||||
// increment index to next line
|
||||
self.idx += 8 * self.panels;
|
||||
|
||||
// change from print to delete if squeeze is still active
|
||||
if self.squeezer == Squeezer::Print {
|
||||
self.squeezer = Squeezer::Delete;
|
||||
}
|
||||
|
||||
// repeat the first byte in the line until it's a usize
|
||||
// compare that usize with each usize chunk in the line
|
||||
// if they are all the same, change squeezer to print
|
||||
let repeat_byte = (self.line_buf[0] as usize) * (usize::MAX / 255);
|
||||
if !matches!(self.squeezer, Squeezer::Disabled | Squeezer::Delete)
|
||||
&& self
|
||||
.line_buf
|
||||
.chunks_exact(std::mem::size_of::<usize>())
|
||||
.all(|w| usize::from_ne_bytes(w.try_into().unwrap()) == repeat_byte)
|
||||
{
|
||||
self.squeezer = Squeezer::Print;
|
||||
self.squeeze_byte = repeat_byte;
|
||||
};
|
||||
};
|
||||
|
||||
// special ending
|
||||
|
||||
if is_empty {
|
||||
self.base_digits = 2;
|
||||
self.print_header()?;
|
||||
if self.show_position_panel {
|
||||
write!(self.writer, "{0:9}", "│")?;
|
||||
}
|
||||
write!(
|
||||
self.writer,
|
||||
"{0:2}{1:2$}{0}{0:>3$}",
|
||||
"│",
|
||||
"No content",
|
||||
self.panel_sz() - 1,
|
||||
self.panel_sz() + 1,
|
||||
)?;
|
||||
if self.show_char_panel {
|
||||
write!(self.writer, "{0:>9}{0:>9}", "│")?;
|
||||
}
|
||||
writeln!(self.writer)?;
|
||||
} else if let Some(n) = leftover {
|
||||
// last line is incomplete
|
||||
self.print_position_panel()?;
|
||||
self.squeezer = Squeezer::Ignore;
|
||||
self.print_bytes()?;
|
||||
self.squeezer = Squeezer::Print;
|
||||
for i in n..8 * self.panels as usize {
|
||||
self.print_byte(i, 0)?;
|
||||
}
|
||||
if self.show_char_panel {
|
||||
self.squeezer = Squeezer::Ignore;
|
||||
self.print_char_panel()?;
|
||||
self.squeezer = Squeezer::Print;
|
||||
for i in n..8 * self.panels as usize {
|
||||
self.print_char(i as u64)?;
|
||||
}
|
||||
}
|
||||
self.writer.write_all(b"\n")?;
|
||||
}
|
||||
|
||||
self.print_footer()?;
|
||||
|
||||
self.writer.flush()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::io;
|
||||
use std::str;
|
||||
|
||||
use super::*;
|
||||
|
||||
fn assert_print_all_output<Reader: Read>(input: Reader, expected_string: String) {
|
||||
let mut output = vec![];
|
||||
let mut printer = Printer::new(
|
||||
&mut output,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
BorderStyle::Unicode,
|
||||
true,
|
||||
2,
|
||||
1,
|
||||
Base::Hexadecimal,
|
||||
);
|
||||
|
||||
printer.print_all(input).unwrap();
|
||||
|
||||
let actual_string: &str = str::from_utf8(&output).unwrap();
|
||||
assert_eq!(actual_string, expected_string,)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty_file_passes() {
|
||||
let input = io::empty();
|
||||
let expected_string = "\
|
||||
┌────────┬─────────────────────────┬─────────────────────────┬────────┬────────┐
|
||||
│ │ No content │ │ │ │
|
||||
└────────┴─────────────────────────┴─────────────────────────┴────────┴────────┘
|
||||
"
|
||||
.to_owned();
|
||||
assert_print_all_output(input, expected_string);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn short_input_passes() {
|
||||
let input = io::Cursor::new(b"spam");
|
||||
let expected_string = "\
|
||||
┌────────┬─────────────────────────┬─────────────────────────┬────────┬────────┐
|
||||
│00000000│ 73 70 61 6d ┊ │spam ┊ │
|
||||
└────────┴─────────────────────────┴─────────────────────────┴────────┴────────┘
|
||||
"
|
||||
.to_owned();
|
||||
assert_print_all_output(input, expected_string);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn display_offset() {
|
||||
let input = io::Cursor::new(b"spamspamspamspamspam");
|
||||
let expected_string = "\
|
||||
┌────────┬─────────────────────────┬─────────────────────────┬────────┬────────┐
|
||||
│deadbeef│ 73 70 61 6d 73 70 61 6d ┊ 73 70 61 6d 73 70 61 6d │spamspam┊spamspam│
|
||||
│deadbeff│ 73 70 61 6d ┊ │spam ┊ │
|
||||
└────────┴─────────────────────────┴─────────────────────────┴────────┴────────┘
|
||||
"
|
||||
.to_owned();
|
||||
|
||||
let mut output = vec![];
|
||||
let mut printer: Printer<Vec<u8>> = Printer::new(
|
||||
&mut output,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
BorderStyle::Unicode,
|
||||
true,
|
||||
2,
|
||||
1,
|
||||
Base::Hexadecimal,
|
||||
);
|
||||
printer.display_offset(0xdeadbeef);
|
||||
|
||||
printer.print_all(input).unwrap();
|
||||
|
||||
let actual_string: &str = str::from_utf8(&output).unwrap();
|
||||
assert_eq!(actual_string, expected_string)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multiple_panels() {
|
||||
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,
|
||||
1,
|
||||
Base::Hexadecimal,
|
||||
);
|
||||
|
||||
printer.print_all(input).unwrap();
|
||||
|
||||
let actual_string: &str = str::from_utf8(&output).unwrap();
|
||||
assert_eq!(actual_string, expected_string)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn squeeze_works() {
|
||||
let input = io::Cursor::new(b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00");
|
||||
let expected_string = "\
|
||||
┌────────┬─────────────────────────┬─────────────────────────┬────────┬────────┐
|
||||
│00000000│ 00 00 00 00 00 00 00 00 ┊ 00 00 00 00 00 00 00 00 │⋄⋄⋄⋄⋄⋄⋄⋄┊⋄⋄⋄⋄⋄⋄⋄⋄│
|
||||
│* │ ┊ │ ┊ │
|
||||
│00000020│ 00 ┊ │⋄ ┊ │
|
||||
└────────┴─────────────────────────┴─────────────────────────┴────────┴────────┘
|
||||
"
|
||||
.to_owned();
|
||||
assert_print_all_output(input, expected_string);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn squeeze_nonzero() {
|
||||
let input = io::Cursor::new(b"000000000000000000000000000000000");
|
||||
let expected_string = "\
|
||||
┌────────┬─────────────────────────┬─────────────────────────┬────────┬────────┐
|
||||
│00000000│ 30 30 30 30 30 30 30 30 ┊ 30 30 30 30 30 30 30 30 │00000000┊00000000│
|
||||
│* │ ┊ │ ┊ │
|
||||
│00000020│ 30 ┊ │0 ┊ │
|
||||
└────────┴─────────────────────────┴─────────────────────────┴────────┴────────┘
|
||||
"
|
||||
.to_owned();
|
||||
assert_print_all_output(input, expected_string);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn squeeze_multiple_panels() {
|
||||
let input = io::Cursor::new(b"0000000000000000000000000000000000000000000000000");
|
||||
let expected_string = "\
|
||||
┌────────┬─────────────────────────┬─────────────────────────┬─────────────────────────┬────────┬────────┬────────┐
|
||||
│00000000│ 30 30 30 30 30 30 30 30 ┊ 30 30 30 30 30 30 30 30 ┊ 30 30 30 30 30 30 30 30 │00000000┊00000000┊00000000│
|
||||
│* │ ┊ ┊ │ ┊ ┊ │
|
||||
│00000030│ 30 ┊ ┊ │0 ┊ ┊ │
|
||||
└────────┴─────────────────────────┴─────────────────────────┴─────────────────────────┴────────┴────────┴────────┘
|
||||
"
|
||||
.to_owned();
|
||||
|
||||
let mut output = vec![];
|
||||
let mut printer: Printer<Vec<u8>> = Printer::new(
|
||||
&mut output,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
BorderStyle::Unicode,
|
||||
true,
|
||||
3,
|
||||
1,
|
||||
Base::Hexadecimal,
|
||||
);
|
||||
|
||||
printer.print_all(input).unwrap();
|
||||
|
||||
let actual_string: &str = str::from_utf8(&output).unwrap();
|
||||
assert_eq!(actual_string, expected_string)
|
||||
}
|
||||
}
|
||||
-261
@@ -1,261 +0,0 @@
|
||||
#[macro_use]
|
||||
extern crate clap;
|
||||
extern crate ansi_term;
|
||||
|
||||
use std::fs::File;
|
||||
use std::io::{self, prelude::*, StdoutLock};
|
||||
|
||||
use clap::{App, AppSettings, Arg};
|
||||
|
||||
use ansi_term::Colour;
|
||||
use ansi_term::Colour::RGB;
|
||||
|
||||
const BUFFER_SIZE: usize = 64;
|
||||
|
||||
const COLOR_NULL: Colour = RGB(117, 113, 94); // grey
|
||||
const COLOR_OFFSET: Colour = RGB(117, 113, 94); // grey
|
||||
const COLOR_ASCII_PRINTABLE: Colour = RGB(102, 217, 239); // cyan
|
||||
const COLOR_ASCII_WHITESPACE: Colour = RGB(166, 226, 46); // green
|
||||
const COLOR_ASCII_OTHER: Colour = RGB(249, 38, 114); // magenta
|
||||
const COLOR_NONASCII: Colour = RGB(253, 151, 31); // orange
|
||||
|
||||
enum ByteCategory {
|
||||
Null,
|
||||
AsciiPrintable,
|
||||
AsciiWhitespace,
|
||||
AsciiOther,
|
||||
NonAscii,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
struct Byte(u8);
|
||||
|
||||
impl Byte {
|
||||
fn category(self) -> ByteCategory {
|
||||
if self.0 == 0x00 {
|
||||
ByteCategory::Null
|
||||
} else if self.0.is_ascii_alphanumeric()
|
||||
|| self.0.is_ascii_punctuation()
|
||||
|| self.0.is_ascii_graphic()
|
||||
{
|
||||
ByteCategory::AsciiPrintable
|
||||
} else if self.0.is_ascii_whitespace() {
|
||||
ByteCategory::AsciiWhitespace
|
||||
} else if self.0.is_ascii() {
|
||||
ByteCategory::AsciiOther
|
||||
} else {
|
||||
ByteCategory::NonAscii
|
||||
}
|
||||
}
|
||||
|
||||
fn color(self) -> &'static Colour {
|
||||
use ByteCategory::*;
|
||||
|
||||
match self.category() {
|
||||
Null => &COLOR_NULL,
|
||||
AsciiPrintable => &COLOR_ASCII_PRINTABLE,
|
||||
AsciiWhitespace => &COLOR_ASCII_WHITESPACE,
|
||||
AsciiOther => &COLOR_ASCII_OTHER,
|
||||
NonAscii => &COLOR_NONASCII,
|
||||
}
|
||||
}
|
||||
|
||||
fn as_char(self) -> char {
|
||||
use ByteCategory::*;
|
||||
|
||||
match self.category() {
|
||||
Null => '0',
|
||||
AsciiPrintable => self.0 as char,
|
||||
AsciiWhitespace if self.0 == 0x20 => ' ',
|
||||
AsciiWhitespace => '_',
|
||||
AsciiOther => '•',
|
||||
NonAscii => '×',
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct Printer<'a> {
|
||||
idx: usize,
|
||||
line: Vec<u8>,
|
||||
stdout: StdoutLock<'a>,
|
||||
}
|
||||
|
||||
impl<'a> Printer<'a> {
|
||||
fn new(stdout: StdoutLock) -> Printer {
|
||||
Printer {
|
||||
idx: 1,
|
||||
line: vec![],
|
||||
stdout,
|
||||
}
|
||||
}
|
||||
|
||||
fn header(&mut self) {
|
||||
writeln!(
|
||||
self.stdout,
|
||||
"┌{}┬{}┬{}┬{}┬{}┐",
|
||||
"─".repeat(8),
|
||||
"─".repeat(3 * 8 + 1),
|
||||
"─".repeat(3 * 8 + 1),
|
||||
"─".repeat(8),
|
||||
"─".repeat(8)
|
||||
);
|
||||
}
|
||||
|
||||
fn footer(&mut self) {
|
||||
writeln!(
|
||||
self.stdout,
|
||||
"└{}┴{}┴{}┴{}┴{}┘",
|
||||
"─".repeat(8),
|
||||
"─".repeat(3 * 8 + 1),
|
||||
"─".repeat(3 * 8 + 1),
|
||||
"─".repeat(8),
|
||||
"─".repeat(8)
|
||||
);
|
||||
}
|
||||
|
||||
fn print_byte(&mut self, b: u8) -> io::Result<()> {
|
||||
if self.idx % 16 == 1 {
|
||||
let offset_str = format!("{:08x}", self.idx - 1);
|
||||
write!(self.stdout, "│{}│ ", COLOR_OFFSET.paint(offset_str))?;
|
||||
}
|
||||
|
||||
let byte_str = format!("{:02x} ", b);
|
||||
write!(self.stdout, "{}", Byte(b).color().paint(byte_str))?;
|
||||
self.line.push(b);
|
||||
|
||||
match self.idx % 16 {
|
||||
8 => write!(self.stdout, "┊ ")?,
|
||||
0 => {
|
||||
self.print_textline()?;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
self.idx += 1;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn print_textline(&mut self) -> io::Result<()> {
|
||||
let len = self.line.len();
|
||||
|
||||
if len == 0 {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if len < 8 {
|
||||
write!(
|
||||
self.stdout,
|
||||
"{}┊{}│",
|
||||
" ".repeat(3 * (8 - len)),
|
||||
" ".repeat(1 + 3 * 8)
|
||||
)?;
|
||||
} else {
|
||||
write!(self.stdout, "{}│", " ".repeat(3 * (16 - len)))?;
|
||||
}
|
||||
|
||||
let mut idx = 1;
|
||||
for b in self.line.iter().map(|b| Byte(*b)) {
|
||||
let chr = format!("{}", b.as_char());
|
||||
write!(self.stdout, "{}", b.color().paint(chr)).ok();
|
||||
|
||||
if idx == 8 {
|
||||
write!(self.stdout, "┊");
|
||||
}
|
||||
|
||||
idx += 1;
|
||||
}
|
||||
|
||||
if len < 8 {
|
||||
writeln!(
|
||||
self.stdout,
|
||||
"{}┊{}│ ",
|
||||
" ".repeat(8 - len),
|
||||
" ".repeat(8)
|
||||
)?;
|
||||
} else {
|
||||
writeln!(self.stdout, "{}│", " ".repeat(16 - len))?;
|
||||
}
|
||||
|
||||
self.line.clear();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn run() -> io::Result<()> {
|
||||
let app = App::new(crate_name!())
|
||||
.setting(AppSettings::ColorAuto)
|
||||
.setting(AppSettings::ColoredHelp)
|
||||
.setting(AppSettings::DeriveDisplayOrder)
|
||||
.setting(AppSettings::UnifiedHelpMessage)
|
||||
.version(crate_version!())
|
||||
.about(crate_description!())
|
||||
.arg(Arg::with_name("file").help("File to display"))
|
||||
.arg(
|
||||
Arg::with_name("length")
|
||||
.short("n")
|
||||
.long("length")
|
||||
.takes_value(true)
|
||||
.value_name("N")
|
||||
.help("read only N bytes from the input"),
|
||||
);
|
||||
|
||||
let matches = app.get_matches();
|
||||
|
||||
let stdin = io::stdin();
|
||||
|
||||
let mut reader: Box<dyn Read> = match matches.value_of("file") {
|
||||
Some(filename) => Box::new(File::open(filename)?),
|
||||
None => Box::new(stdin.lock()),
|
||||
};
|
||||
|
||||
if let Some(length) = matches
|
||||
.value_of("length")
|
||||
.and_then(|n| n.parse::<u64>().ok())
|
||||
{
|
||||
reader = Box::new(reader.take(length));
|
||||
}
|
||||
|
||||
let stdout = io::stdout();
|
||||
let mut printer = Printer::new(stdout.lock());
|
||||
printer.header();
|
||||
|
||||
let mut buffer = [0; BUFFER_SIZE];
|
||||
loop {
|
||||
let size = reader.read(&mut buffer)?;
|
||||
if size == 0 {
|
||||
break;
|
||||
}
|
||||
|
||||
for b in &buffer[..size] {
|
||||
let res = printer.print_byte(*b);
|
||||
|
||||
if res.is_err() {
|
||||
// Broken pipe
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Finish last line
|
||||
printer.print_textline().ok();
|
||||
printer.footer();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn main() {
|
||||
// Enable ANSI support for Windows
|
||||
#[cfg(windows)]
|
||||
let _ = ansi_term::enable_ansi_support();
|
||||
|
||||
let result = run();
|
||||
match result {
|
||||
Err(err) => {
|
||||
eprintln!("Error: {}", err);
|
||||
std::process::exit(1);
|
||||
}
|
||||
Ok(()) => {}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
ascii text eol=lf
|
||||
@@ -0,0 +1 @@
|
||||
0123456789abcde
|
||||
Whitespace-only changes.
Executable
BIN
Binary file not shown.
@@ -0,0 +1,571 @@
|
||||
use assert_cmd::Command;
|
||||
|
||||
fn hexyl() -> Command {
|
||||
let mut cmd = Command::cargo_bin("hexyl").unwrap();
|
||||
cmd.current_dir("tests/examples");
|
||||
cmd
|
||||
}
|
||||
trait PrettyAssert<S>
|
||||
where
|
||||
S: AsRef<str>,
|
||||
{
|
||||
fn pretty_stdout(self, other: S);
|
||||
}
|
||||
|
||||
// https://github.com/assert-rs/assert_cmd/issues/121#issuecomment-849937376
|
||||
//
|
||||
impl<S> PrettyAssert<S> for assert_cmd::assert::Assert
|
||||
where
|
||||
S: AsRef<str>,
|
||||
{
|
||||
fn pretty_stdout(self, other: S) {
|
||||
println!("{}", other.as_ref().len());
|
||||
let self_str = String::from_utf8(self.get_output().stdout.clone()).unwrap();
|
||||
println!("{}", self_str.len());
|
||||
pretty_assertions::assert_eq!(self_str, other.as_ref());
|
||||
}
|
||||
}
|
||||
|
||||
mod basic {
|
||||
use super::hexyl;
|
||||
|
||||
#[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 │ │ │ │\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•••⋄┊⋄⋄⋄⋄⋄⋄⋄⋄│\n\
|
||||
│00000010│ 02 00 3e 00 01 00 00 00 ┊ 00 10 40 00 00 00 00 00 │•⋄>⋄•⋄⋄⋄┊⋄•@⋄⋄⋄⋄⋄│\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 │ │ │ │\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 │•⋄>⋄•⋄⋄⋄┊⋄•@⋄⋄⋄⋄⋄│\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",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
mod group {
|
||||
use super::hexyl;
|
||||
use super::PrettyAssert;
|
||||
|
||||
#[test]
|
||||
fn group_2_bytes() {
|
||||
hexyl()
|
||||
.arg("ascii")
|
||||
.arg("--color=never")
|
||||
.arg("--group-bytes=2")
|
||||
.assert()
|
||||
.success()
|
||||
.stdout(
|
||||
"┌────────┬─────────────────────┬─────────────────────┬────────┬────────┐\n\
|
||||
│00000000│ 3031 3233 3435 3637 ┊ 3839 6162 6364 650a │01234567┊89abcde_│\n\
|
||||
└────────┴─────────────────────┴─────────────────────┴────────┴────────┘\n",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn group_4_bytes() {
|
||||
hexyl()
|
||||
.arg("ascii")
|
||||
.arg("--color=never")
|
||||
.arg("--group-bytes=4")
|
||||
.assert()
|
||||
.success()
|
||||
.stdout(
|
||||
"┌────────┬───────────────────┬───────────────────┬────────┬────────┐\n\
|
||||
│00000000│ 30313233 34353637 ┊ 38396162 6364650a │01234567┊89abcde_│\n\
|
||||
└────────┴───────────────────┴───────────────────┴────────┴────────┘\n",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn group_8_bytes() {
|
||||
hexyl()
|
||||
.arg("ascii")
|
||||
.arg("--color=never")
|
||||
.arg("--group-bytes=8")
|
||||
.assert()
|
||||
.success()
|
||||
.stdout(
|
||||
"┌────────┬──────────────────┬──────────────────┬────────┬────────┐\n\
|
||||
│00000000│ 3031323334353637 ┊ 383961626364650a │01234567┊89abcde_│\n\
|
||||
└────────┴──────────────────┴──────────────────┴────────┴────────┘\n",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn group_bytes_plain() {
|
||||
hexyl()
|
||||
.arg("ascii")
|
||||
.arg("--color=never")
|
||||
.arg("--plain")
|
||||
.arg("--group-bytes=2")
|
||||
.assert()
|
||||
.success()
|
||||
.stdout(" 3031 3233 3435 3637 3839 6162 6364 650a \n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn group_bytes_fill_space() {
|
||||
hexyl()
|
||||
.arg("--color=never")
|
||||
.arg("--group-bytes=2")
|
||||
.write_stdin("abc")
|
||||
.assert()
|
||||
.success()
|
||||
.stdout(
|
||||
"┌────────┬─────────────────────┬─────────────────────┬────────┬────────┐\n\
|
||||
│00000000│ 6162 63 ┊ │abc ┊ │\n\
|
||||
└────────┴─────────────────────┴─────────────────────┴────────┴────────┘\n",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn group_bytes_invalid() {
|
||||
hexyl()
|
||||
.arg("ascii")
|
||||
.arg("--color=never")
|
||||
.arg("--plain")
|
||||
.arg("--group-bytes=3")
|
||||
.assert()
|
||||
.failure();
|
||||
}
|
||||
#[test]
|
||||
fn squeeze_no_chars() {
|
||||
hexyl()
|
||||
.arg("hello_world_elf64")
|
||||
.arg("--color=never")
|
||||
.arg("--skip=1024")
|
||||
.arg("--length=4096")
|
||||
.arg("--no-characters")
|
||||
.assert()
|
||||
.success()
|
||||
.pretty_stdout(
|
||||
"\
|
||||
┌────────┬─────────────────────────┬─────────────────────────┐
|
||||
│00000400│ 00 00 00 00 00 00 00 00 ┊ 00 00 00 00 00 00 00 00 │
|
||||
│* │ ┊ │
|
||||
│00001000│ ba 0e 00 00 00 b9 00 20 ┊ 40 00 bb 01 00 00 00 b8 │
|
||||
│00001010│ 04 00 00 00 cd 80 b8 01 ┊ 00 00 00 cd 80 00 00 00 │
|
||||
│00001020│ 00 00 00 00 00 00 00 00 ┊ 00 00 00 00 00 00 00 00 │
|
||||
│* │ ┊ │
|
||||
│00001400│ ┊ │
|
||||
└────────┴─────────────────────────┴─────────────────────────┘
|
||||
",
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn squeeze_no_chars_one_panel() {
|
||||
hexyl()
|
||||
.arg("hello_world_elf64")
|
||||
.arg("--color=never")
|
||||
.arg("--skip=1024")
|
||||
.arg("--length=4096")
|
||||
.arg("--no-characters")
|
||||
.arg("--panels=1")
|
||||
.assert()
|
||||
.success()
|
||||
.pretty_stdout(
|
||||
"\
|
||||
┌────────┬─────────────────────────┐
|
||||
│00000400│ 00 00 00 00 00 00 00 00 │
|
||||
│* │ │
|
||||
│00001000│ ba 0e 00 00 00 b9 00 20 │
|
||||
│00001008│ 40 00 bb 01 00 00 00 b8 │
|
||||
│00001010│ 04 00 00 00 cd 80 b8 01 │
|
||||
│00001018│ 00 00 00 cd 80 00 00 00 │
|
||||
│00001020│ 00 00 00 00 00 00 00 00 │
|
||||
│* │ │
|
||||
│00001400│ │
|
||||
└────────┴─────────────────────────┘
|
||||
",
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn squeeze_no_position() {
|
||||
hexyl()
|
||||
.arg("hello_world_elf64")
|
||||
.arg("--color=never")
|
||||
.arg("--skip=1024")
|
||||
.arg("--length=4096")
|
||||
.arg("--no-position")
|
||||
.assert()
|
||||
.success()
|
||||
.pretty_stdout(
|
||||
"\
|
||||
┌─────────────────────────┬─────────────────────────┬────────┬────────┐
|
||||
│ 00 00 00 00 00 00 00 00 ┊ 00 00 00 00 00 00 00 00 │⋄⋄⋄⋄⋄⋄⋄⋄┊⋄⋄⋄⋄⋄⋄⋄⋄│
|
||||
│* ┊ │ ┊ │
|
||||
│ ba 0e 00 00 00 b9 00 20 ┊ 40 00 bb 01 00 00 00 b8 │ו⋄⋄⋄×⋄ ┊@⋄ו⋄⋄⋄×│
|
||||
│ 04 00 00 00 cd 80 b8 01 ┊ 00 00 00 cd 80 00 00 00 │•⋄⋄⋄××ו┊⋄⋄⋄××⋄⋄⋄│
|
||||
│ 00 00 00 00 00 00 00 00 ┊ 00 00 00 00 00 00 00 00 │⋄⋄⋄⋄⋄⋄⋄⋄┊⋄⋄⋄⋄⋄⋄⋄⋄│
|
||||
│* ┊ │ ┊ │
|
||||
│* ┊ │ ┊ │
|
||||
└─────────────────────────┴─────────────────────────┴────────┴────────┘
|
||||
",
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn squeeze_no_position_one_panel() {
|
||||
hexyl()
|
||||
.arg("hello_world_elf64")
|
||||
.arg("--color=never")
|
||||
.arg("--skip=1024")
|
||||
.arg("--length=4096")
|
||||
.arg("--no-position")
|
||||
.arg("--panels=1")
|
||||
.assert()
|
||||
.success()
|
||||
.pretty_stdout(
|
||||
"\
|
||||
┌─────────────────────────┬────────┐
|
||||
│ 00 00 00 00 00 00 00 00 │⋄⋄⋄⋄⋄⋄⋄⋄│
|
||||
│* │ │
|
||||
│ ba 0e 00 00 00 b9 00 20 │ו⋄⋄⋄×⋄ │
|
||||
│ 40 00 bb 01 00 00 00 b8 │@⋄ו⋄⋄⋄×│
|
||||
│ 04 00 00 00 cd 80 b8 01 │•⋄⋄⋄××ו│
|
||||
│ 00 00 00 cd 80 00 00 00 │⋄⋄⋄××⋄⋄⋄│
|
||||
│ 00 00 00 00 00 00 00 00 │⋄⋄⋄⋄⋄⋄⋄⋄│
|
||||
│* │ │
|
||||
│* │ │
|
||||
└─────────────────────────┴────────┘
|
||||
",
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn squeeze_odd_panels_remainder_bytes() {
|
||||
hexyl()
|
||||
.arg("hello_world_elf64")
|
||||
.arg("--color=never")
|
||||
.arg("--skip=1024")
|
||||
.arg("--length=4092") // 4 byte remainder
|
||||
.arg("--panels=3")
|
||||
.assert()
|
||||
.success()
|
||||
.pretty_stdout(
|
||||
"\
|
||||
┌────────┬─────────────────────────┬─────────────────────────┬─────────────────────────┬────────┬────────┬────────┐
|
||||
│00000400│ 00 00 00 00 00 00 00 00 ┊ 00 00 00 00 00 00 00 00 ┊ 00 00 00 00 00 00 00 00 │⋄⋄⋄⋄⋄⋄⋄⋄┊⋄⋄⋄⋄⋄⋄⋄⋄┊⋄⋄⋄⋄⋄⋄⋄⋄│
|
||||
│* │ ┊ ┊ │ ┊ ┊ │
|
||||
│00001000│ ba 0e 00 00 00 b9 00 20 ┊ 40 00 bb 01 00 00 00 b8 ┊ 04 00 00 00 cd 80 b8 01 │ו⋄⋄⋄×⋄ ┊@⋄ו⋄⋄⋄×┊•⋄⋄⋄××ו│
|
||||
│00001018│ 00 00 00 cd 80 00 00 00 ┊ 00 00 00 00 00 00 00 00 ┊ 00 00 00 00 00 00 00 00 │⋄⋄⋄××⋄⋄⋄┊⋄⋄⋄⋄⋄⋄⋄⋄┊⋄⋄⋄⋄⋄⋄⋄⋄│
|
||||
│00001030│ 00 00 00 00 00 00 00 00 ┊ 00 00 00 00 00 00 00 00 ┊ 00 00 00 00 00 00 00 00 │⋄⋄⋄⋄⋄⋄⋄⋄┊⋄⋄⋄⋄⋄⋄⋄⋄┊⋄⋄⋄⋄⋄⋄⋄⋄│
|
||||
│* │ ┊ ┊ │ ┊ ┊ │
|
||||
│000013f0│ 00 00 00 00 00 00 00 00 ┊ 00 00 00 00 ┊ │⋄⋄⋄⋄⋄⋄⋄⋄┊⋄⋄⋄⋄ ┊ │
|
||||
└────────┴─────────────────────────┴─────────────────────────┴─────────────────────────┴────────┴────────┴────────┘
|
||||
",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn squeeze_plain() {
|
||||
hexyl()
|
||||
.arg("hello_world_elf64")
|
||||
.arg("--color=never")
|
||||
.arg("--skip=1024")
|
||||
.arg("--length=4096")
|
||||
.arg("--plain")
|
||||
.assert()
|
||||
.success()
|
||||
.pretty_stdout(
|
||||
" \
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
*
|
||||
ba 0e 00 00 00 b9 00 20 40 00 bb 01 00 00 00 b8
|
||||
04 00 00 00 cd 80 b8 01 00 00 00 cd 80 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
*
|
||||
*
|
||||
",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn squeeze_plain_remainder() {
|
||||
hexyl()
|
||||
.arg("hello_world_elf64")
|
||||
.arg("--color=never")
|
||||
.arg("--skip=1024")
|
||||
.arg("--length=4092") // 4 byte remainder
|
||||
.arg("--plain")
|
||||
.assert()
|
||||
.success()
|
||||
.pretty_stdout(
|
||||
" \
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
*
|
||||
ba 0e 00 00 00 b9 00 20 40 00 bb 01 00 00 00 b8
|
||||
04 00 00 00 cd 80 b8 01 00 00 00 cd 80 00 00 00
|
||||
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
*
|
||||
00 00 00 00 00 00 00 00 00 00 00 00
|
||||
",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
mod base {
|
||||
use super::hexyl;
|
||||
use super::PrettyAssert;
|
||||
|
||||
#[test]
|
||||
fn base2() {
|
||||
hexyl()
|
||||
.arg("ascii")
|
||||
.arg("--plain")
|
||||
.arg("--base=binary")
|
||||
.assert()
|
||||
.success()
|
||||
.pretty_stdout(
|
||||
" 00110000 00110001 00110010 00110011 00110100 00110101 00110110 00110111 00111000 00111001 01100001 01100010 01100011 01100100 01100101 00001010 \n"
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user