81 Commits
Author SHA1 Message Date
David Peter c3e9cb9518 Bump MSRV 2021-07-11 20:09:28 +02:00
David Peter 3489998c67 cargo fmt 2021-07-11 20:09:28 +02:00
David Peter 4594d29f73 Bump version to 0.9.0 2021-07-11 20:09:28 +02:00
David Peter cc73f01c34 Update dependencies 2021-07-11 20:09:28 +02:00
David Peter 6da15cd1e9 Added alias notice in help text 2021-07-11 19:57:55 +02:00
David Peter c82ccd5a59 Add CHANGELOG entry 2021-07-11 19:57:55 +02:00
TheDoctor314 19d2805729 Add alias '-l' for '-n/--length'
Set `.hidden(true)` option

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

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

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

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

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

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

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

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

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

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

No files matched your search

-1
View File
@@ -1 +0,0 @@
github: sharkdp
+315
View File
@@ -0,0 +1,315 @@
name: CICD
env:
MIN_SUPPORTED_RUST_VERSION: "1.40.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)
- name: Run tests
uses: actions-rs/cargo@v1
with:
command: test
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: 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"
# 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 }}"
# 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: command-line tool to generate, analyze, convert and manipulate colors
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 }}
-79
View File
@@ -1,79 +0,0 @@
language: rust
cache: cargo
matrix:
include:
# Stable channel.
- os: linux
rust: stable
env: TARGET=x86_64-unknown-linux-gnu
- os: linux
rust: stable
env: TARGET=x86_64-unknown-linux-musl
- os: osx
rust: stable
env: TARGET=x86_64-apple-darwin
- os: windows
rust: stable
env: TARGET=x86_64-pc-windows-msvc
# Minimum Rust supported channel.
- os: linux
rust: 1.31.0
env: TARGET=x86_64-unknown-linux-gnu
addons:
apt:
packages:
# needed to build deb packages
- fakeroot
env:
global:
# Default target on travis-ci.
# Used as conditional check in the install stage
- HOST=x86_64-unknown-linux-gnu
# Used on the deployment script
- PROJECT_NAME=hexyl
install:
# prevent target re-add error from rustup
- if [[ $TRAVIS_OS_NAME = linux && $HOST != $TARGET ]]; then rustup target add $TARGET; fi
script:
# Incorporate TARGET env var to the build and test process
- cargo build --target $TARGET --verbose
- cargo test --target $TARGET --verbose
before_deploy:
- bash ci/before_deploy.bash
deploy:
provider: releases
# NOTE updating the `api_key.secure`
# - go to: https://github.com/settings/tokens/new
# - generate new token using `public_repo` scope
# - encrypt it using: `travis encrypt API_KEY_HERE`
# - paste the output below
api_key:
secure: "XHpyp21JUWI6GkV7APTwPZiJQJkRJ13E/9VALuWvDrNYF00OE8m+T0ZbG+N1WCuwewa2QQChqd5yBDlwJn+LlDPqstie69xjCt7PEhvI5ERZlbT8KUtMLI4bFIudsaa6LS6phWi/6aKb4cfn0nEYmWhpqfVfsn4Hh4F9OyOFSRmMSSXziU3kxLdIBBO4jOfoDYz+4KUX48CtIhmFy6dSb4G22jzVXL3ixGq895lVFlUzfiGJhHXoS+6KsOx1q0eIEMPC6V0VTT1loBNxKpjBPQ1b0KysCim/jRnr9mB3ko/4yWcMtIvCaZxTaQXWsQn9wAEN62x3ruKugZh3LJbtaxSvnH9PMntXZliz1HJw3Hpmn+tGLxAOm+uwHRz2C/T2ibemgmw6F8KjsjdNv7Nc2//ErIbmMvV4OlJjJI2Q3LWjnYa2gqI0L0sMqUz9HAfiY/BXLSxp19LgSvZPuHNVzMfh9ovF+Rdk1I3TyoByS8ZUi2ngnra8bSjmjtWHmkntOksjWNG6iobpjz9UxdWXPWxNJHQbRfEyTJSISpClykcSZWj+E2+tfuB3YvuRUaVOIhA3THdOSsqEesl3jkUeIhQqgRhE+Uq/XqB7MdfZ9ZTX8ZgfGFL0gjygviuWznTb0q5i4t1SgEJ2ie+ykjVyqQWa0dePtaQp8yx2PtzchJs="
# for uploading multiple files
file_glob: true
# NOTE explanation on each env variable
# - PROJECT_NAME: name of the project, set on the `env.global` above
# - TRAVIS_TAG: tag name that the build is being deployed for, usually the version number
# - TARGET: target triple of the build
file:
- $PROJECT_NAME-$TRAVIS_TAG-$TARGET.*
- $PROJECT_NAME*.deb
# don't delete artifacts from previous stage
skip_cleanup: true
on:
# deploy only if we push a tag
tags: true
# deploy only on stable channel that has TARGET env variable sets
condition: $TRAVIS_RUST_VERSION = stable && $TARGET != "x86_64-pc-windows-msvc"
notifications:
email:
on_success: never
+151
View File
@@ -0,0 +1,151 @@
# unreleased
## Changes
## Features
## Bugfixes
## Other
## Packaging
# 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 reoccuring lines are squashed together and visualized with an asterisk. A new `-v`/`--no-squeezing` option can be used to disable the feature. For details, see #59 (@awidegreen)
- Added a new `--border` option with support for various styles (Unicode, ASCII, None), see #54 (@dmke)
- The `--length`/`-n` argument can be passed as a hexadecimal number (`hexyl -n 0xff /dev/urandom`), see #45 (@Qyriad)
- Added `--bytes`/`-c` as an alias for `--length`/`-n`, see #48 (@selfup)
## Changes
- Print header immediately before the first line, see #51 (@mziter)
# v0.4.0
## Features
- Added a new `--color=always/auto/never` option which can be used
to control `hexyl`s color output, see #30 (@bennetthardwick)
- Use 16 colors instead of 256, see #38
## Changes
- Various speed improvements, see #33 (@kballard)
## Bugfixes
- Proper Ctrl-C handling, see #35
- Proper handling of broken pipes (`hexyl … | head`)
# v0.3.1
- Various (huge) performance improvements, see #23 and #24 (@kballard)
- Replaced 24-bit truecolor ANSI codes by 8-bit codes to support
more terminal emulators, fixes #9
# v0.3.0
Windows support
# v0.2.0
Initial release
+29
View File
@@ -0,0 +1,29 @@
# Contributing
Thank you for considering to contribute to `hexyl`!
## Add an entry to the changelog
If your contribution changes the behavior of `hexyl` (as opposed to a typo-fix
in the documentation), please update the [`CHANGELOG.md`](CHANGELOG.md) file
and describe your changes. This makes the release proess 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
+269 -96
View File
@@ -1,193 +1,366 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
[[package]]
name = "aho-corasick"
version = "0.7.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f"
dependencies = [
"memchr",
]
[[package]]
name = "ansi_term"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
dependencies = [
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi",
]
[[package]]
name = "ansi_term"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
dependencies = [
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi",
]
[[package]]
name = "anyhow"
version = "1.0.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "595d3cfa7a60d4555cb5067b99f07142a08ea778de5cf993f7b75c7d8fabc486"
[[package]]
name = "assert_cmd"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d20831bd004dda4c7c372c19cdabff369f794a95e955b3f13fe460e3e1ae95f"
dependencies = [
"bstr",
"doc-comment",
"predicates",
"predicates-core",
"predicates-tree",
"wait-timeout",
]
[[package]]
name = "atty"
version = "0.2.13"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
dependencies = [
"libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
"hermit-abi",
"libc",
"winapi",
]
[[package]]
name = "autocfg"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
[[package]]
name = "bitflags"
version = "1.2.0"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
[[package]]
name = "cc"
version = "1.0.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "cfg-if"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "clap"
version = "2.33.0"
name = "bstr"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90682c8d613ad3373e66de8c6411e0ae2ab2571e879d2efbf73558cc66f21279"
dependencies = [
"ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
"atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)",
"bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
"term_size 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-width 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
"vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static",
"memchr",
"regex-automata",
]
[[package]]
name = "ctrlc"
version = "3.1.3"
name = "clap"
version = "2.33.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002"
dependencies = [
"nix 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
"ansi_term 0.11.0",
"atty",
"bitflags",
"strsim",
"term_size",
"textwrap",
"unicode-width",
]
[[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.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
[[package]]
name = "float-cmp"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e1267f4ac4f343772758f7b1bdcbe767c218bbab93bb432acbf5162bbf85a6c4"
dependencies = [
"num-traits",
]
[[package]]
name = "hermit-abi"
version = "0.1.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
dependencies = [
"libc",
]
[[package]]
name = "hexyl"
version = "0.6.0"
version = "0.9.0"
dependencies = [
"ansi_term 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)",
"atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)",
"clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
"ctrlc 3.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
"ansi_term 0.12.1",
"anyhow",
"assert_cmd",
"atty",
"clap",
"libc",
"predicates",
"thiserror",
]
[[package]]
name = "kernel32-sys"
version = "0.2.2"
name = "itertools"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69ddb889f9d0d08a67338271fa9b62996bc788c7796a5c18cf057420aaed5eaf"
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)",
"either",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.62"
version = "0.2.98"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "320cfe77175da3a483efed4bc0adc1968ca050b098ce4f2f1c13a56626128790"
[[package]]
name = "nix"
version = "0.14.1"
name = "memchr"
version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b16bd47d9e329435e309c58469fe0791c2d0d1ba96ec0954152a5ae2b04387dc"
[[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.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290"
dependencies = [
"bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)",
"cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
"void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"autocfg",
]
[[package]]
name = "predicates"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c6e46ca79eb4e21e2ec14430340c71250ab69332abf85521c95d3a8bc336aa76"
dependencies = [
"difflib",
"float-cmp",
"itertools",
"normalize-line-endings",
"predicates-core",
"regex",
]
[[package]]
name = "predicates-core"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57e35a3326b75e49aa85f5dc6ec15b41108cf5aee58eabb1f274dd18b73c2451"
[[package]]
name = "predicates-tree"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "15f553275e5721409451eb85e15fd9a860a6e5ab4496eb215987502b5f5391f2"
dependencies = [
"predicates-core",
"treeline",
]
[[package]]
name = "proc-macro2"
version = "1.0.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0d8caf72986c1a598726adc988bb5984792ef84f5ee5aa50209145ee8077038"
dependencies = [
"unicode-xid",
]
[[package]]
name = "quote"
version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7"
dependencies = [
"proc-macro2",
]
[[package]]
name = "regex"
version = "1.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461"
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.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
[[package]]
name = "strsim"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
[[package]]
name = "syn"
version = "1.0.73"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f71489ff30030d2ae598524f61326b902466f72a0fb1a8564c001cc63425bcc7"
dependencies = [
"proc-macro2",
"quote",
"unicode-xid",
]
[[package]]
name = "term_size"
version = "0.3.1"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e4129646ca0ed8f45d09b929036bafad5377103edd06e50bf574b353d2b08d9"
dependencies = [
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
"libc",
"winapi",
]
[[package]]
name = "textwrap"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
dependencies = [
"term_size 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-width 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
"term_size",
"unicode-width",
]
[[package]]
name = "thiserror"
version = "1.0.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93119e4feac1cbe6c798c34d3a53ea0026b0b1de6a120deef895137c0529bfe2"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "060d69a0afe7796bf42e9e2ff91f5ee691fb15c53d38b4b62a9a53eb23164745"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "treeline"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7f741b240f1a48843f9b8e0444fb55fb2a4ff67293b50a9179dfd5ea67f8d41"
[[package]]
name = "unicode-width"
version = "0.1.6"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3"
[[package]]
name = "vec_map"
version = "0.8.1"
name = "unicode-xid"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
[[package]]
name = "void"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "winapi"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "winapi"
version = "0.3.8"
name = "wait-timeout"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6"
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)",
"libc",
]
[[package]]
name = "winapi-build"
version = "0.1.1"
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-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[metadata]
"checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
"checksum ansi_term 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
"checksum atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)" = "1803c647a3ec87095e7ae7acfca019e98de5ec9a7d01343f611cf3152ed71a90"
"checksum bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8a606a02debe2813760609f57a64a2ffd27d9fdf5b2f133eaca0b248dd92cdd2"
"checksum cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)" = "4fc9a35e1f4290eb9e5fc54ba6cf40671ed2a2514c3eeb2b2a908dda2ea5a1be"
"checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
"checksum clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9"
"checksum ctrlc 3.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c7dfd2d8b4c82121dfdff120f818e09fc4380b0b7e17a742081a89b94853e87f"
"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
"checksum libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)" = "34fcd2c08d2f832f376f4173a231990fa5aef4e99fb569867318a227ef4c06ba"
"checksum nix 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6c722bee1037d430d0f8e687bbdbf222f27cc6e4e68d5caf630857bb2b6dbdce"
"checksum strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
"checksum term_size 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9e5b9a66db815dcfd2da92db471106457082577c3c278d4138ab3e3b4e189327"
"checksum textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
"checksum unicode-width 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "7007dbd421b92cc6e28410fe7362e2e0a2503394908f417b68ec8d1c364c4e20"
"checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a"
"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
"checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6"
"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"
"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+9 -2
View File
@@ -7,18 +7,25 @@ license = "MIT/Apache-2.0"
name = "hexyl"
readme = "README.md"
repository = "https://github.com/sharkdp/hexyl"
version = "0.6.0"
version = "0.9.0"
edition = "2018"
[dependencies]
ansi_term = "0.12"
anyhow = "1.0.31"
atty = "0.2"
ctrlc = "3.1"
libc = "0.2"
thiserror = "1.0.19"
[dependencies.clap]
version = "2"
default-features = false
features = ["suggestions", "color", "wrap_help"]
[dev-dependencies]
assert_cmd = "1.0"
predicates = "2.0"
[profile.release]
lto = true
codegen-units = 1
+74 -7
View File
@@ -1,6 +1,6 @@
![](doc/logo.svg)
[![Build Status](https://travis-ci.org/sharkdp/hexyl.svg?branch=master)](https://travis-ci.org/sharkdp/hexyl)
[![CICD](https://github.com/sharkdp/hexyl/actions/workflows/CICD.yml/badge.svg)](https://github.com/sharkdp/hexyl/actions/workflows/CICD.yml)
[![](https://img.shields.io/crates/l/hexyl.svg?colorB=22ba4c)](https://crates.io/crates/hexyl)
![](https://img.shields.io/crates/v/hexyl.svg?colorB=00aa88)
@@ -19,13 +19,31 @@ 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/eoan/hexyl):
```bash
sudo apt install hexyl
```
If you use an older version of Ubuntu, you can download
the latest `.deb` package from the release page and install it via:
``` bash
wget "https://github.com/sharkdp/hexyl/releases/download/v0.6.0/hexyl_0.6.0_amd64.deb"
sudo dpkg -i hexyl_0.6.0_amd64.deb
sudo dpkg -i hexyl_0.9.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/buster/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 Arch Linux
You can install `hexyl` from [the official package repository](https://www.archlinux.org/packages/community/x86_64/hexyl/):
@@ -34,40 +52,89 @@ You can install `hexyl` from [the official package repository](https://www.archl
pacman -S hexyl
```
### On Void Linux
```
xbps-install hexyl
```
### On Gentoo Linux
Available in [dm9pZCAq overlay](https://github.com/gentoo-mirror/dm9pZCAq)
```
sudo eselect repository enable dm9pZCAq
sudo emerge --sync dm9pZCAq
sudo emerge sys-apps/hexyl::dm9pZCAq
```
### On macOS
Via [Homebrew](https://brew.sh):
```
brew install hexyl
```
...or via [MacPorts](https://www.macports.org):
```
sudo port install hexyl
```
### On FreeBSD
```
pkg install hexyl
```
### on Termux
```
pkg install hexyl
```
or
```
apt install hexyl
```
### Via Nix
```
nix-env -i hexyl
```
### Via Guix
```
guix package -i hexyl
```
Or add the `hexyl` package in the list of packages to be installed in your system configuration (e.g., `/etc/config.scm`).
### On other distributions
Check out the [release page](https://github.com/sharkdp/hexyl/releases) for binary builds.
### On Windows
For now, you will have to install from source via `cargo` (see below). Make sure that
you use a terminal that supports ANSI escape sequences (like PowerShell on Windows 10).
For now, you will have to install from source via `cargo` (see below). Make sure that you
use a terminal that supports ANSI escape sequences (like ConHost v2 since Windows 10 1703
or Windows Terminal since Windows 10 1903).
### Via cargo
If you have Rust 1.31 or higher, you can install `hexyl` from source via `cargo`:
If you have Rust 1.40 or higher, you can install `hexyl` from source via `cargo`:
```
cargo install hexyl
```
### Via snap package
```
sudo snap install hexyl
```
[Get it from the Snap Store](https://snapcraft.io/hexyl)
## License
Licensed under either of
+20
View File
@@ -0,0 +1,20 @@
use std::io;
use hexyl::{BorderStyle, Printer};
fn main() {
let input = vec![
0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44,
0x52, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x44, 0x08, 0x02, 0x00, 0x00, 0x00,
];
let stdout = io::stdout();
let mut handle = stdout.lock();
let show_color = true;
let use_squeezing = false;
let border_style = BorderStyle::Unicode;
let mut printer = Printer::new(&mut handle, show_color, border_style, use_squeezing);
printer.print_all(&input[..]).unwrap();
}
+438 -44
View File
@@ -1,36 +1,45 @@
#[macro_use]
extern crate clap;
use atty;
use ctrlc;
use std::convert::TryFrom;
use std::fs::File;
use std::io::{self, prelude::*};
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::io::{self, prelude::*, SeekFrom};
use clap::{App, AppSettings, Arg};
use atty::Stream;
use hexyl::{BorderStyle, Printer};
use anyhow::{anyhow, Context, Error as AnyhowError};
fn run() -> Result<(), Box<dyn std::error::Error>> {
use thiserror::Error as ThisError;
use hexyl::{BorderStyle, Input, Printer};
fn run() -> Result<(), AnyhowError> {
let app = App::new(crate_name!())
.setting(AppSettings::ColorAuto)
.setting(AppSettings::ColoredHelp)
.setting(AppSettings::DeriveDisplayOrder)
.setting(AppSettings::UnifiedHelpMessage)
.max_term_width(90)
.version(crate_version!())
.about(crate_description!())
.arg(Arg::with_name("file").help("File to display"))
.arg(
Arg::with_name("FILE")
.help("The file to display. If no FILE argument is given, read from STDIN."),
)
.arg(
Arg::with_name("length")
.short("n")
.long("length")
.takes_value(true)
.value_name("N")
.help("Read only N bytes from the input"),
.help(
"Only read N bytes from the input. The N argument can also include a \
unit with a decimal prefix (kB, MB, ..) or binary prefix (kiB, MiB, ..). \
The short option '-l' can be used as an alias.\n\
Examples: --length=64, --length=4KiB",
),
)
.arg(
Arg::with_name("bytes")
@@ -38,8 +47,39 @@ fn run() -> Result<(), Box<dyn std::error::Error>> {
.long("bytes")
.takes_value(true)
.value_name("N")
.conflicts_with("length")
.help("An alias for -n/--length"),
)
.arg(
Arg::with_name("count")
.short("l")
.takes_value(true)
.value_name("N")
.hidden(true)
.help("Yet another alias for -n/--length"),
)
.arg(
Arg::with_name("skip")
.short("s")
.long("skip")
.takes_value(true)
.value_name("N")
.help(
"Skip the first N bytes of the input. The N argument can also include \
a unit (see `--length` for details)\n\
A negative value is valid and will seek from the end of the file.",
),
)
.arg(
Arg::with_name("block_size")
.long("block-size")
.takes_value(true)
.value_name("SIZE")
.help(
"Sets the size of the `block` unit to SIZE.\n\
Examples: --block-size=1024, --block-size=4kB",
),
)
.arg(
Arg::with_name("nosqueezing")
.short("v")
@@ -54,7 +94,7 @@ fn run() -> Result<(), Box<dyn std::error::Error>> {
Arg::with_name("color")
.long("color")
.takes_value(true)
.value_name("when")
.value_name("WHEN")
.possible_values(&["always", "auto", "never"])
.default_value("always")
.help(
@@ -66,33 +106,99 @@ fn run() -> Result<(), Box<dyn std::error::Error>> {
Arg::with_name("border")
.long("border")
.takes_value(true)
.value_name("STYLE")
.possible_values(&["unicode", "ascii", "none"])
.default_value("unicode")
.help("Whether to draw a border with unicode or ASCII characters, or none at all"),
.help(
"Whether to draw a border with Unicode characters, ASCII characters, \
or none at all",
),
)
.arg(
Arg::with_name("display_offset")
.short("o")
.long("display-offset")
.takes_value(true)
.value_name("OFFSET")
.help("Add OFFSET to the displayed file position."),
.value_name("N")
.help(
"Add N bytes to the displayed file position. The N argument can also \
include a unit (see `--length` for details)\n\
A negative value is valid and calculates an offset relative to the \
end of the file.",
),
);
let matches = app.get_matches_safe()?;
let stdin = io::stdin();
let mut reader: Box<dyn Read> = match matches.value_of("file") {
Some(filename) => Box::new(File::open(filename)?),
None => Box::new(stdin.lock()),
let mut reader = match matches.value_of("FILE") {
Some(filename) => Input::File(File::open(filename)?),
None => Input::Stdin(stdin.lock()),
};
let length_arg = matches.value_of("length").or(matches.value_of("bytes"));
let block_size = matches
.value_of("block_size")
.map(|bs| {
bs.parse::<i64>().map_err(|e| anyhow!(e)).and_then(|x| {
PositiveI64::new(x).ok_or_else(|| anyhow!("block size argument must be positive"))
})
})
.transpose()?
.unwrap_or_else(|| PositiveI64::new(512).unwrap());
if let Some(length) = length_arg.and_then(parse_hex_or_int) {
reader = Box::new(reader.take(length));
}
let skip_arg = matches
.value_of("skip")
.map(|s| {
parse_byte_offset(s, block_size).context(anyhow!(
"failed to parse `--skip` arg {:?} as byte count",
s
))
})
.transpose()?;
let skip_offset = if let Some(ByteOffset { kind, value }) = skip_arg {
let value = value.into_inner();
reader
.seek(match kind {
ByteOffsetKind::ForwardFromBeginning | ByteOffsetKind::ForwardFromLastOffset => {
SeekFrom::Current(value)
}
ByteOffsetKind::BackwardFromEnd => SeekFrom::End(value.checked_neg().unwrap()),
})
.map_err(|_| {
anyhow!(
"Failed to jump to the desired input position. \
This could be caused by a negative offset that is too large or by \
an input that is not seek-able (e.g. if the input comes from a pipe)."
)
})?
} else {
0
};
let parse_byte_count = |s| -> Result<u64, AnyhowError> {
Ok(parse_byte_offset(s, block_size)?
.assume_forward_offset_from_start()?
.into())
};
let mut reader = if let Some(length) = matches
.value_of("length")
.or_else(|| matches.value_of("bytes"))
.or_else(|| matches.value_of("count"))
.map(|s| {
parse_byte_count(s).context(anyhow!(
"failed to parse `--length` arg {:?} as byte count",
s
))
})
.transpose()?
{
Box::new(reader.take(length))
} else {
reader.into_inner()
};
let show_color = match matches.value_of("color") {
Some("never") => false,
@@ -108,26 +214,24 @@ fn run() -> Result<(), Box<dyn std::error::Error>> {
let squeeze = !matches.is_present("nosqueezing");
let display_offset = matches
let display_offset: u64 = matches
.value_of("display_offset")
.and_then(parse_hex_or_int)
.unwrap_or(0);
// Set up Ctrl-C handler
let cancelled = Arc::new(AtomicBool::new(false));
let c = cancelled.clone();
ctrlc::set_handler(move || {
c.store(true, Ordering::SeqCst);
})
.expect("Error setting Ctrl-C handler");
.map(|s| {
parse_byte_count(s).context(anyhow!(
"failed to parse `--display-offset` arg {:?} as byte count",
s
))
})
.transpose()?
.unwrap_or(0)
.into();
let stdout = io::stdout();
let mut stdout_lock = stdout.lock();
let mut printer = Printer::new(&mut stdout_lock, show_color, border_style, squeeze);
printer.display_offset(display_offset as usize);
printer.print_all(&mut reader, Some(cancelled))?;
printer.display_offset(skip_offset + display_offset);
printer.print_all(&mut reader).map_err(|e| anyhow!(e))?;
Ok(())
}
@@ -141,26 +245,316 @@ fn main() {
if let Err(err) = result {
if let Some(clap_err) = err.downcast_ref::<clap::Error>() {
eprint!("{}", clap_err); // Clap errors already have newlines
match clap_err.kind {
// The exit code should not indicate an error for --help / --version
clap::ErrorKind::HelpDisplayed | clap::ErrorKind::VersionDisplayed => {
clap::ErrorKind::HelpDisplayed => {
eprint!("{}", clap_err); // Clap errors already have newlines
std::process::exit(0)
}
clap::ErrorKind::VersionDisplayed => {
// Version output in clap 2.33.1 (dep as of now) doesn't have a newline
// and the fix is not included even in the latest stable release
println!("");
std::process::exit(0)
}
_ => (),
}
} else {
eprintln!("Error: {}", err);
eprintln!("Error: {:?}", err);
}
std::process::exit(1);
}
}
fn parse_hex_or_int(n: &str) -> Option<u64> {
if n.starts_with("0x") {
u64::from_str_radix(n.trim_start_matches("0x"), 16).ok()
} else {
n.parse::<u64>().ok()
#[derive(Clone, Copy, Debug, Default, Hash, Eq, Ord, PartialEq, PartialOrd)]
pub struct NonNegativeI64(i64);
impl NonNegativeI64 {
pub fn new(x: i64) -> Option<Self> {
if x.is_negative() {
None
} else {
Some(Self(x))
}
}
pub fn into_inner(self) -> i64 {
self.0
}
}
impl Into<u64> for NonNegativeI64 {
fn into(self) -> u64 {
u64::try_from(self.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 Into<u64> for PositiveI64 {
fn into(self) -> u64 {
u64::try_from(self.0)
.expect("invariant broken: PositiveI64 should contain a positive i64 value")
}
}
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 multipled 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) = {
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('+') => (
check_empty_after_sign()?,
ByteOffsetKind::ForwardFromLastOffset,
),
Some('-') => (check_empty_after_sign()?, ByteOffsetKind::BackwardFromEnd),
None => return Err(Empty),
_ => (n, ByteOffsetKind::ForwardFromBeginning),
}
};
let into_byte_offset = |value| {
Ok(ByteOffset {
value: NonNegativeI64::new(value).unwrap(),
kind,
})
};
if n.starts_with(HEX_PREFIX) {
let n = &n[HEX_PREFIX.len()..];
let mut chars = n.chars();
match chars.next() {
Some(c @ '+') | Some(c @ '-') => {
return if chars.next().is_none() {
Err(EmptyAfterSign)
} else {
Err(SignFoundAfterHexPrefix(c))
}
}
_ => (),
}
return i64::from_str_radix(n, 16)
.map(into_byte_offset)
.map_err(ParseNum)?;
}
let (num, unit) = match n.chars().position(|c| !c.is_ascii_digit()) {
Some(unit_begin_idx) => {
let (n, raw_unit) = n.split_at(unit_begin_idx);
let raw_unit_lower = raw_unit.to_lowercase();
let multiplier = [
("b", 1),
("kb", 1000i64.pow(1)),
("mb", 1000i64.pow(2)),
("gb", 1000i64.pow(3)),
("tb", 1000i64.pow(4)),
("kib", 1024i64.pow(1)),
("mib", 1024i64.pow(2)),
("gib", 1024i64.pow(3)),
("tib", 1024i64.pow(4)),
("block", block_size.into_inner()),
]
.iter()
.cloned()
.find_map(|(unit, multiplier)| {
if unit == raw_unit_lower {
Some(multiplier)
} else {
None
}
})
.ok_or_else(|| InvalidUnit(raw_unit.to_owned()));
(n, multiplier.map(|m| (Some(raw_unit), m)))
}
None => (n, Ok((None, 1))),
};
match (num.parse::<i64>(), unit) {
(Ok(num), Ok((_raw_unit, unit_multiplier))) => num
.checked_mul(unit_multiplier)
.ok_or_else(|| UnitMultiplicationOverflow)
.and_then(into_byte_offset),
(Ok(_), Err(e)) => Err(e),
(Err(e), Ok((raw_unit, _unit_multiplier))) => match raw_unit {
Some(raw_unit) if num.is_empty() => Err(EmptyWithUnit(raw_unit.to_owned())),
_ => Err(ParseNum(e)),
},
(Err(_), Err(_)) => Err(InvalidNumAndUnit(n.to_owned())),
}
}
#[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: 512)
};
($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(512).unwrap()),
Err($expected_err),
);
};
}
success!("0", ForwardFromBeginning 0);
success!("1", ForwardFromBeginning 1);
success!("1", ForwardFromBeginning 1);
success!("100", ForwardFromBeginning 100);
success!("+100", ForwardFromLastOffset 100);
success!("0x0", ForwardFromBeginning 0);
success!("0xf", ForwardFromBeginning 15);
success!("0xdeadbeef", ForwardFromBeginning 3_735_928_559);
success!("1KB", ForwardFromBeginning 1000);
success!("2MB", ForwardFromBeginning 2000000);
success!("3GB", ForwardFromBeginning 3000000000);
success!("4TB", ForwardFromBeginning 4000000000000);
success!("+4TB", ForwardFromLastOffset 4000000000000);
success!("1GiB", ForwardFromBeginning 1073741824);
success!("2TiB", ForwardFromBeginning 2199023255552);
success!("+2TiB", ForwardFromLastOffset 2199023255552);
success!("0xff", ForwardFromBeginning 255);
success!("0xEE", ForwardFromBeginning 238);
success!("+0xFF", ForwardFromLastOffset 255);
success!("1block", ForwardFromBeginning 512; block_size: 512);
success!("2block", ForwardFromBeginning 1024; block_size: 512);
success!("1block", ForwardFromBeginning 4; block_size: 4);
success!("2block", ForwardFromBeginning 8; block_size: 4);
// empty string is invalid
error!("", Empty);
// These are also bad.
error!("+", EmptyAfterSign);
error!("-", EmptyAfterSign);
error!("K", InvalidNumAndUnit("K".to_owned()));
error!("k", InvalidNumAndUnit("k".to_owned()));
error!("m", InvalidNumAndUnit("m".to_owned()));
error!("block", EmptyWithUnit("block".to_owned()));
// leading/trailing space is invalid
error!(" 0", InvalidNumAndUnit(" 0".to_owned()));
error!("0 ", InvalidUnit(" ".to_owned()));
// Signs after the hex prefix make no sense
error!("0x-12", SignFoundAfterHexPrefix('-'));
// This was previously accepted but shouldn't be.
error!("0x+12", SignFoundAfterHexPrefix('+'));
// invalid suffix
error!("1234asdf", InvalidUnit("asdf".to_owned()));
// bad numbers
error!("asdf1234", InvalidNumAndUnit("asdf1234".to_owned()));
error!("a1s2d3f4", InvalidNumAndUnit("a1s2d3f4".to_owned()));
// multiplication overflows u64
error!("20000000TiB", UnitMultiplicationOverflow);
assert!(
match parse_byte_offset("99999999999999999999", PositiveI64::new(512).unwrap()) {
// We can't check against the kind of the `ParseIntError`, so we'll just make sure it's the
// same as trying to do the parse directly.
Err(ParseNum(e)) => e == "99999999999999999999".parse::<i64>().unwrap_err(),
_ => false,
}
);
}
+65
View File
@@ -0,0 +1,65 @@
use std::convert::TryFrom;
use std::fs;
use std::io::{self, copy, sink, Read, Seek, SeekFrom};
pub enum Input<'a> {
File(fs::File),
Stdin(io::StdinLock<'a>),
}
impl<'a> Read for Input<'a> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
match *self {
Input::File(ref mut file) => file.read(buf),
Input::Stdin(ref mut stdin) => stdin.read(buf),
}
}
}
impl<'a> Seek for Input<'a> {
fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
fn try_skip<R>(reader: R, pos: SeekFrom, err_desc: &'static str) -> io::Result<u64>
where
R: Read,
{
let cant_seek_abs_err = || Err(io::Error::new(io::ErrorKind::Other, err_desc));
let offset = match pos {
SeekFrom::Current(o) => u64::try_from(o).or_else(|_e| cant_seek_abs_err())?,
SeekFrom::Start(_) | SeekFrom::End(_) => cant_seek_abs_err()?,
};
copy(&mut reader.take(offset), &mut sink())
}
match *self {
Input::File(ref mut file) => {
let seek_res = file.seek(pos);
if let Err(Some(libc::ESPIPE)) = seek_res.as_ref().map_err(|err| err.raw_os_error())
{
try_skip(
file,
pos,
"Pipes only support seeking forward with a relative offset",
)
} else {
seek_res
}
}
Input::Stdin(ref mut stdin) => try_skip(
stdin,
pos,
"STDIN only supports seeking forward with a relative offset",
),
}
}
}
impl<'a> Input<'a> {
pub fn into_inner(self) -> Box<dyn Read + 'a> {
match self {
Input::File(file) => Box::new(file),
Input::Stdin(stdin) => Box::new(stdin),
}
}
}
+28 -33
View File
@@ -1,16 +1,15 @@
pub(crate) mod input;
pub mod squeezer;
pub use input::*;
use std::io::{self, Read, Write};
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use ansi_term::Color;
use ansi_term::Color::Fixed;
use crate::squeezer::{SqueezeAction, Squeezer};
type Cancelled = Arc<AtomicBool>;
const BUFFER_SIZE: usize = 256;
const COLOR_NULL: Color = Fixed(242); // grey
@@ -35,10 +34,7 @@ impl Byte {
fn category(self) -> ByteCategory {
if self.0 == 0x00 {
ByteCategory::Null
} else if self.0.is_ascii_alphanumeric()
|| self.0.is_ascii_punctuation()
|| self.0.is_ascii_graphic()
{
} else if self.0.is_ascii_graphic() {
ByteCategory::AsciiPrintable
} else if self.0.is_ascii_whitespace() {
ByteCategory::AsciiWhitespace
@@ -143,7 +139,7 @@ impl BorderStyle {
}
pub struct Printer<'a, Writer: Write> {
idx: usize,
idx: u64,
/// The raw bytes used as input for the current line.
raw_line: Vec<u8>,
/// The buffered line built with each byte, ready to print to writer.
@@ -155,7 +151,7 @@ pub struct Printer<'a, Writer: Write> {
byte_hex_table: Vec<String>,
byte_char_table: Vec<String>,
squeezer: Squeezer,
display_offset: usize,
display_offset: u64,
}
impl<'a, Writer: Write> Printer<'a, Writer> {
@@ -198,7 +194,7 @@ impl<'a, Writer: Write> Printer<'a, Writer> {
}
}
pub fn display_offset(&mut self, display_offset: usize) -> &mut Self {
pub fn display_offset(&mut self, display_offset: u64) -> &mut Self {
self.display_offset = display_offset;
self
}
@@ -248,7 +244,7 @@ impl<'a, Writer: Write> Printer<'a, Writer> {
}
let style = COLOR_OFFSET.normal();
let byte_index = format!("{:08x}", (self.idx - 1) + self.display_offset);
let byte_index = format!("{:08x}", self.idx - 1 + self.display_offset);
let formatted_string = if self.show_color {
format!("{}", style.paint(byte_index))
} else {
@@ -408,12 +404,11 @@ impl<'a, Writer: Write> Printer<'a, Writer> {
}
/// Loop through the given `Reader`, printing until the `Reader` buffer
/// is exhausted, or the optional `cancelled` bool is set to true.
/// is exhausted.
pub fn print_all<Reader: Read>(
&mut self,
mut reader: Reader,
canceller: Option<Cancelled>,
) -> Result<(), Box<dyn std::error::Error>> {
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let mut buffer = [0; BUFFER_SIZE];
'mainloop: loop {
let size = reader.read(&mut buffer)?;
@@ -421,13 +416,6 @@ impl<'a, Writer: Write> Printer<'a, Writer> {
break;
}
if let Some(cancelled) = &canceller {
if cancelled.load(Ordering::SeqCst) {
eprintln!("hexyl has been cancelled.");
std::process::exit(130); // Set exit code to 128 + SIGINT
}
}
for b in &buffer[..size] {
let res = self.print_byte(*b);
@@ -440,8 +428,14 @@ impl<'a, Writer: Write> Printer<'a, Writer> {
// Finish last line
self.print_textline().ok();
if !self.header_was_printed() {
self.header();
writeln!(
self.writer,
"│ │ No content to print │ │ │ │"
)
.ok();
}
self.footer();
@@ -460,7 +454,7 @@ mod tests {
let mut output = vec![];
let mut printer = Printer::new(&mut output, false, BorderStyle::Unicode, true);
printer.print_all(input, None).unwrap();
printer.print_all(input).unwrap();
let actual_string: &str = str::from_utf8(&output).unwrap();
assert_eq!(actual_string, expected_string)
@@ -469,43 +463,44 @@ mod tests {
#[test]
fn empty_file_passes() {
let input = io::empty();
let expected_string =
"┌────────┬─────────────────────────┬─────────────────────────┬────────┬────────┐
let expected_string = "\
┌────────┬─────────────────────────┬─────────────────────────┬────────┬────────┐
│ │ No content to print │ │ │ │
└────────┴─────────────────────────┴─────────────────────────┴────────┴────────┘
"
.to_owned();
.to_owned();
assert_print_all_output(input, expected_string);
}
#[test]
fn short_input_passes() {
let input = io::Cursor::new(b"spam");
let expected_string =
"┌────────┬─────────────────────────┬─────────────────────────┬────────┬────────┐
let expected_string = "\
┌────────┬─────────────────────────┬─────────────────────────┬────────┬────────┐
│00000000│ 73 70 61 6d ┊ │spam ┊ │
└────────┴─────────────────────────┴─────────────────────────┴────────┴────────┘
"
.to_owned();
.to_owned();
assert_print_all_output(input, expected_string);
}
#[test]
fn display_offset() {
let input = io::Cursor::new(b"spamspamspamspamspam");
let expected_string =
"┌────────┬─────────────────────────┬─────────────────────────┬────────┬────────┐
let expected_string = "\
┌────────┬─────────────────────────┬─────────────────────────┬────────┬────────┐
│deadbeef│ 73 70 61 6d 73 70 61 6d ┊ 73 70 61 6d 73 70 61 6d │spamspam┊spamspam│
│deadbeff│ 73 70 61 6d ┊ │spam ┊ │
└────────┴─────────────────────────┴─────────────────────────┴────────┴────────┘
"
.to_owned();
.to_owned();
let mut output = vec![];
let mut printer: Printer<Vec<u8>> =
Printer::new(&mut output, false, BorderStyle::Unicode, true);
printer.display_offset(0xdeadbeef);
printer.print_all(input, None).unwrap();
printer.print_all(input).unwrap();
let actual_string: &str = str::from_utf8(&output).unwrap();
assert_eq!(actual_string, expected_string)
+17 -15
View File
@@ -32,7 +32,7 @@ pub enum SqueezeAction {
}
/// line size
const LSIZE: usize = 16;
const LSIZE: u64 = 16;
impl Squeezer {
pub fn new(enabled: bool) -> Squeezer {
@@ -46,7 +46,7 @@ impl Squeezer {
}
}
pub fn process(&mut self, b: u8, i: usize) {
pub fn process(&mut self, b: u8, i: u64) {
use self::SqueezeState::*;
if self.state == Disabled {
return;
@@ -111,10 +111,12 @@ impl Squeezer {
mod tests {
use super::*;
const LSIZE_USIZE: usize = LSIZE as usize;
#[test]
fn three_same_lines() {
const LINES: usize = 3;
let v = vec![0u8; LINES * LSIZE];
let v = vec![0u8; LINES * LSIZE_USIZE];
let mut s = Squeezer::new(true);
// just initialized
assert_eq!(s.action(), SqueezeAction::Ignore);
@@ -128,7 +130,7 @@ mod tests {
let mut line = 0;
let mut idx = 1;
for z in v.chunks(LSIZE) {
for z in v.chunks(LSIZE_USIZE) {
for i in z {
s.process(*i, idx);
idx += 1;
@@ -143,7 +145,7 @@ mod tests {
#[test]
fn incomplete_while_squeeze() {
// fourth line only has 12 bytes and should be printed
let v = vec![0u8; 3 * LSIZE + 12];
let v = vec![0u8; 3 * LSIZE_USIZE + 12];
let mut s = Squeezer::new(true);
// just initialized
assert_eq!(s.action(), SqueezeAction::Ignore);
@@ -158,7 +160,7 @@ mod tests {
let mut line = 0;
let mut idx = 1;
for z in v.chunks(LSIZE) {
for z in v.chunks(LSIZE_USIZE) {
for i in z {
s.process(*i, idx);
idx += 1;
@@ -190,7 +192,7 @@ mod tests {
let mut line = 0;
let mut idx = 1;
for z in v.chunks(LSIZE) {
for z in v.chunks(LSIZE_USIZE) {
for i in z {
s.process(*i, idx);
idx += 1;
@@ -207,7 +209,7 @@ mod tests {
/// print
fn one_squeeze_no_delete() {
const LINES: usize = 3;
let mut v = vec![0u8; (LINES - 1) * LSIZE];
let mut v = vec![0u8; (LINES - 1) * LSIZE_USIZE];
v.extend(vec![1u8; 16]);
let mut s = Squeezer::new(true);
@@ -223,7 +225,7 @@ mod tests {
let mut line = 0;
let mut idx = 1;
for z in v.chunks(LSIZE) {
for z in v.chunks(LSIZE_USIZE) {
for i in z {
s.process(*i, idx);
idx += 1;
@@ -239,7 +241,7 @@ mod tests {
/// First line all eq, 2nd half eq with first line, then change
fn second_line_different() {
const LINES: usize = 2;
let mut v = vec![0u8; (LINES - 1) * LSIZE];
let mut v = vec![0u8; (LINES - 1) * LSIZE_USIZE];
v.extend(vec![0u8; 8]);
v.extend(vec![1u8; 8]);
@@ -255,7 +257,7 @@ mod tests {
let mut line = 0;
let mut idx = 1;
for z in v.chunks(LSIZE) {
for z in v.chunks(LSIZE_USIZE) {
for i in z {
s.process(*i, idx);
idx += 1;
@@ -291,7 +293,7 @@ mod tests {
let mut line = 0;
let mut idx = 1;
for z in v.chunks(LSIZE) {
for z in v.chunks(LSIZE_USIZE) {
for i in z {
s.process(*i, idx);
idx += 1;
@@ -341,7 +343,7 @@ mod tests {
let mut line = 0;
let mut idx = 1;
for z in v.chunks(LSIZE) {
for z in v.chunks(LSIZE_USIZE) {
for i in z {
s.process(*i, idx);
idx += 1;
@@ -377,7 +379,7 @@ mod tests {
let mut line = 0;
let mut idx = 1;
for z in v.chunks(LSIZE) {
for z in v.chunks(LSIZE_USIZE) {
for i in z {
s.process(*i, idx);
idx += 1;
@@ -410,7 +412,7 @@ mod tests {
let mut line = 0;
let mut idx = 1;
for z in v.chunks(LSIZE) {
for z in v.chunks(LSIZE_USIZE) {
for i in z {
s.process(*i, idx);
idx += 1;
+1
View File
@@ -0,0 +1 @@
ascii text eol=lf
+1
View File
@@ -0,0 +1 @@
0123456789abcde
View File
Whitespace-only changes.
BIN
View File
Binary file not shown.
+205
View File
@@ -0,0 +1,205 @@
use assert_cmd::Command;
fn hexyl() -> Command {
let mut cmd = Command::cargo_bin("hexyl").unwrap();
cmd.current_dir("tests/examples");
cmd
}
mod basic {
use super::hexyl;
#[test]
fn can_print_simple_ascii_file() {
hexyl()
.arg("ascii")
.arg("--color=never")
.assert()
.success()
.stdout(
"┌────────┬─────────────────────────┬─────────────────────────┬────────┬────────┐\n\
│00000000│ 30 31 32 33 34 35 36 37 ┊ 38 39 61 62 63 64 65 0a │01234567┊89abcde_│\n\
└────────┴─────────────────────────┴─────────────────────────┴────────┴────────┘\n",
);
}
#[test]
fn can_read_input_from_stdin() {
hexyl()
.arg("--color=never")
.write_stdin("abc")
.assert()
.success()
.stdout(
"┌────────┬─────────────────────────┬─────────────────────────┬────────┬────────┐\n\
│00000000│ 61 62 63 ┊ │abc ┊ │\n\
└────────┴─────────────────────────┴─────────────────────────┴────────┴────────┘\n",
);
}
#[test]
fn fails_on_non_existing_input() {
hexyl().arg("non-existing").assert().failure();
}
#[test]
fn prints_warning_on_empty_content() {
hexyl()
.arg("empty")
.arg("--color=never")
.assert()
.success()
.stdout(
"┌────────┬─────────────────────────┬─────────────────────────┬────────┬────────┐\n\
│ │ No content to print │ │ │ │\n\
└────────┴─────────────────────────┴─────────────────────────┴────────┴────────┘\n",
);
}
}
mod length {
use super::hexyl;
#[test]
fn length_restricts_output_size() {
hexyl()
.arg("hello_world_elf64")
.arg("--color=never")
.arg("--length=32")
.assert()
.success()
.stdout(
"┌────────┬─────────────────────────┬─────────────────────────┬────────┬────────┐\n\
│00000000│ 7f 45 4c 46 02 01 01 00 ┊ 00 00 00 00 00 00 00 00 │•ELF•••0┊00000000│\n\
│00000010│ 02 00 3e 00 01 00 00 00 ┊ 00 10 40 00 00 00 00 00 │•0>0•000┊0•@00000│\n\
└────────┴─────────────────────────┴─────────────────────────┴────────┴────────┘\n",
);
}
#[test]
fn fail_if_length_and_bytes_options_are_used_simultaneously() {
hexyl()
.arg("hello_world_elf64")
.arg("--length=32")
.arg("--bytes=10")
.assert()
.failure();
}
}
mod skip {
use super::hexyl;
#[test]
fn basic() {
hexyl()
.arg("ascii")
.arg("--color=never")
.arg("--skip=2")
.arg("--length=4")
.assert()
.success()
.stdout(
"┌────────┬─────────────────────────┬─────────────────────────┬────────┬────────┐\n\
│00000002│ 32 33 34 35 ┊ │2345 ┊ │\n\
└────────┴─────────────────────────┴─────────────────────────┴────────┴────────┘\n",
);
}
#[test]
fn prints_warning_when_skipping_past_the_end() {
hexyl()
.arg("ascii")
.arg("--color=never")
.arg("--skip=1000")
.assert()
.success()
.stdout(
"┌────────┬─────────────────────────┬─────────────────────────┬────────┬────────┐\n\
│ │ No content to print │ │ │ │\n\
└────────┴─────────────────────────┴─────────────────────────┴────────┴────────┘\n",
);
}
#[test]
fn negative_offset() {
hexyl()
.arg("ascii")
.arg("--color=never")
.arg("--skip=-4")
.arg("--length=3")
.assert()
.success()
.stdout(
"┌────────┬─────────────────────────┬─────────────────────────┬────────┬────────┐\n\
│0000000c│ 63 64 65 ┊ │cde ┊ │\n\
└────────┴─────────────────────────┴─────────────────────────┴────────┴────────┘\n",
);
}
#[test]
fn fails_if_negative_offset_is_too_large() {
hexyl()
.arg("ascii")
.arg("--color=never")
.arg("--skip=-1MiB")
.assert()
.failure()
.stderr(predicates::str::contains("Failed to jump"));
}
}
mod display_offset {
use super::hexyl;
#[test]
fn basic() {
hexyl()
.arg("ascii")
.arg("--color=never")
.arg("--display-offset=0xc0ffee")
.assert()
.success()
.stdout(
"┌────────┬─────────────────────────┬─────────────────────────┬────────┬────────┐\n\
│00c0ffee│ 30 31 32 33 34 35 36 37 ┊ 38 39 61 62 63 64 65 0a │01234567┊89abcde_│\n\
└────────┴─────────────────────────┴─────────────────────────┴────────┴────────┘\n",
);
}
#[test]
fn display_offset_and_skip() {
hexyl()
.arg("hello_world_elf64")
.arg("--color=never")
.arg("--display-offset=0x20")
.arg("--skip=0x10")
.arg("--length=0x10")
.assert()
.success()
.stdout(
"┌────────┬─────────────────────────┬─────────────────────────┬────────┬────────┐\n\
│00000030│ 02 00 3e 00 01 00 00 00 ┊ 00 10 40 00 00 00 00 00 │•0>0•000┊0•@00000│\n\
└────────┴─────────────────────────┴─────────────────────────┴────────┴────────┘\n",
);
}
}
mod blocksize {
use super::hexyl;
#[test]
fn fails_for_zero_or_negative_blocksize() {
hexyl()
.arg("ascii")
.arg("--block-size=0")
.assert()
.failure();
hexyl()
.arg("ascii")
.arg("--block-size=-16")
.assert()
.failure();
}
}