hexyl-0.6.0/.github/FUNDING.yml010064400017500001735000000000201354714170200142430ustar0000000000000000github: sharkdp hexyl-0.6.0/.gitignore010064400017500001735000000000231337011776200130640ustar0000000000000000/target **/*.rs.bk hexyl-0.6.0/.travis.yml010064400017500001735000000053451354715267300132270ustar0000000000000000language: 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 hexyl-0.6.0/Cargo.toml.orig010064400017500001735000000010161354715307000137640ustar0000000000000000[package] authors = ["David Peter "] categories = ["command-line-utilities"] description = "A command-line hex viewer" homepage = "https://github.com/sharkdp/hexyl" license = "MIT/Apache-2.0" name = "hexyl" readme = "README.md" repository = "https://github.com/sharkdp/hexyl" version = "0.6.0" edition = "2018" [dependencies] ansi_term = "0.12" atty = "0.2" ctrlc = "3.1" [dependencies.clap] version = "2" features = ["suggestions", "color", "wrap_help"] [profile.release] lto = true codegen-units = 1 hexyl-0.6.0/Cargo.toml0000644000000021210000000000000102210ustar00# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies # # If you believe there's an error in this file please file an # issue against the rust-lang/cargo repository. If you're # editing this file be aware that the upstream Cargo.toml # will likely look very different (and much more reasonable) [package] edition = "2018" name = "hexyl" version = "0.6.0" authors = ["David Peter "] description = "A command-line hex viewer" homepage = "https://github.com/sharkdp/hexyl" readme = "README.md" categories = ["command-line-utilities"] license = "MIT/Apache-2.0" repository = "https://github.com/sharkdp/hexyl" [profile.release] lto = true codegen-units = 1 [dependencies.ansi_term] version = "0.12" [dependencies.atty] version = "0.2" [dependencies.clap] version = "2" features = ["suggestions", "color", "wrap_help"] [dependencies.ctrlc] version = "3.1" hexyl-0.6.0/LICENSE-APACHE010064400017500001735000000261351337011776200130340ustar0000000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. hexyl-0.6.0/LICENSE-MIT010064400017500001735000000017771337733734500125610ustar0000000000000000Permission 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. hexyl-0.6.0/README.md010064400017500001735000000034611354715302500123620ustar0000000000000000![](doc/logo.svg) [![Build Status](https://travis-ci.org/sharkdp/hexyl.svg?branch=master)](https://travis-ci.org/sharkdp/hexyl) [![](https://img.shields.io/crates/l/hexyl.svg?colorB=22ba4c)](https://crates.io/crates/hexyl) ![](https://img.shields.io/crates/v/hexyl.svg?colorB=00aa88) `hexyl` is a simple hex viewer for the terminal. It uses a colored output to distinguish different categories of bytes (NULL bytes, printable ASCII characters, ASCII whitespace characters, other ASCII characters and non-ASCII). ## Preview ![](https://i.imgur.com/MWO9uSL.png) ![](https://i.imgur.com/Dp7Wncz.png) ![](https://i.imgur.com/ln3TniI.png) ![](https://i.imgur.com/f8nm8g6.png) ## Installation ### On Debian-based systems ``` bash wget "https://github.com/sharkdp/hexyl/releases/download/v0.6.0/hexyl_0.6.0_amd64.deb" sudo dpkg -i hexyl_0.6.0_amd64.deb ``` ### On Arch Linux You can install `hexyl` from [the official package repository](https://www.archlinux.org/packages/community/x86_64/hexyl/): ``` pacman -S hexyl ``` ### On macOS ``` brew install hexyl ``` ### On FreeBSD ``` pkg install hexyl ``` ### Via Nix ``` nix-env -i hexyl ``` ### On other distributions Check out the [release page](https://github.com/sharkdp/hexyl/releases) for binary builds. ### On Windows For now, you will have to install from source via `cargo` (see below). Make sure that you use a terminal that supports ANSI escape sequences (like PowerShell on Windows 10). ### Via cargo If you have Rust 1.31 or higher, you can install `hexyl` from source via `cargo`: ``` cargo install hexyl ``` ## License Licensed under either of * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) at your option. hexyl-0.6.0/ci/.gitattributes010064400017500001735000000000311337037362500143630ustar0000000000000000*.bash linguist-vendored hexyl-0.6.0/ci/before_deploy.bash010075500017500001735000000045751337037360300151650ustar0000000000000000#!/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" < 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 hexyl-0.6.0/doc/logo.svg010064400017500001735000000267651337110413700133400ustar0000000000000000 image/svg+xml hexyl-0.6.0/src/bin/hexyl.rs010064400017500001735000000117531354714170200141430ustar0000000000000000#[macro_use] extern crate clap; use atty; use ctrlc; use std::fs::File; use std::io::{self, prelude::*}; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; use clap::{App, AppSettings, Arg}; use atty::Stream; use hexyl::{BorderStyle, Printer}; fn run() -> Result<(), Box> { let app = App::new(crate_name!()) .setting(AppSettings::ColorAuto) .setting(AppSettings::ColoredHelp) .setting(AppSettings::DeriveDisplayOrder) .setting(AppSettings::UnifiedHelpMessage) .version(crate_version!()) .about(crate_description!()) .arg(Arg::with_name("file").help("File to display")) .arg( Arg::with_name("length") .short("n") .long("length") .takes_value(true) .value_name("N") .help("Read only N bytes from the input"), ) .arg( Arg::with_name("bytes") .short("c") .long("bytes") .takes_value(true) .value_name("N") .help("An alias for -n/--length"), ) .arg( Arg::with_name("nosqueezing") .short("v") .long("no-squeezing") .help( "Displays all input data. Otherwise any number of groups of output \ lines which would be identical to the preceding group of lines, are \ replaced with a line comprised of a single asterisk.", ), ) .arg( Arg::with_name("color") .long("color") .takes_value(true) .value_name("when") .possible_values(&["always", "auto", "never"]) .default_value("always") .help( "When to use colors. The auto-mode only displays colors if the output \ goes to an interactive terminal", ), ) .arg( Arg::with_name("border") .long("border") .takes_value(true) .possible_values(&["unicode", "ascii", "none"]) .default_value("unicode") .help("Whether to draw a border with unicode or ASCII characters, or none at all"), ) .arg( Arg::with_name("display_offset") .short("o") .long("display-offset") .takes_value(true) .value_name("OFFSET") .help("Add OFFSET to the displayed file position."), ); let matches = app.get_matches_safe()?; let stdin = io::stdin(); let mut reader: Box = match matches.value_of("file") { Some(filename) => Box::new(File::open(filename)?), None => Box::new(stdin.lock()), }; let length_arg = matches.value_of("length").or(matches.value_of("bytes")); if let Some(length) = length_arg.and_then(parse_hex_or_int) { reader = Box::new(reader.take(length)); } let show_color = match matches.value_of("color") { Some("never") => false, Some("auto") => atty::is(Stream::Stdout), _ => true, }; let border_style = match matches.value_of("border") { Some("unicode") => BorderStyle::Unicode, Some("ascii") => BorderStyle::Ascii, _ => BorderStyle::None, }; let squeeze = !matches.is_present("nosqueezing"); let display_offset = matches .value_of("display_offset") .and_then(parse_hex_or_int) .unwrap_or(0); // Set up Ctrl-C handler let cancelled = Arc::new(AtomicBool::new(false)); let c = cancelled.clone(); ctrlc::set_handler(move || { c.store(true, Ordering::SeqCst); }) .expect("Error setting Ctrl-C handler"); let stdout = io::stdout(); let mut stdout_lock = stdout.lock(); let mut printer = Printer::new(&mut stdout_lock, show_color, border_style, squeeze); printer.display_offset(display_offset as usize); printer.print_all(&mut reader, Some(cancelled))?; Ok(()) } fn main() { // Enable ANSI support for Windows #[cfg(windows)] let _ = ansi_term::enable_ansi_support(); let result = run(); if let Err(err) = result { if let Some(clap_err) = err.downcast_ref::() { eprint!("{}", clap_err); // Clap errors already have newlines match clap_err.kind { // The exit code should not indicate an error for --help / --version clap::ErrorKind::HelpDisplayed | clap::ErrorKind::VersionDisplayed => { std::process::exit(0) } _ => (), } } else { eprintln!("Error: {}", err); } std::process::exit(1); } } fn parse_hex_or_int(n: &str) -> Option { if n.starts_with("0x") { u64::from_str_radix(n.trim_start_matches("0x"), 16).ok() } else { n.parse::().ok() } } hexyl-0.6.0/src/lib.rs010064400017500001735000000373051354715032100130060ustar0000000000000000pub mod squeezer; use std::io::{self, Read, Write}; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; use ansi_term::Color; use ansi_term::Color::Fixed; use crate::squeezer::{SqueezeAction, Squeezer}; type Cancelled = Arc; const BUFFER_SIZE: usize = 256; const COLOR_NULL: Color = Fixed(242); // grey const COLOR_OFFSET: Color = Fixed(242); // grey const COLOR_ASCII_PRINTABLE: Color = Color::Cyan; const COLOR_ASCII_WHITESPACE: Color = Color::Green; const COLOR_ASCII_OTHER: Color = Color::Purple; const COLOR_NONASCII: Color = Color::Yellow; pub enum ByteCategory { Null, AsciiPrintable, AsciiWhitespace, AsciiOther, NonAscii, } #[derive(Copy, Clone)] struct Byte(u8); impl Byte { fn category(self) -> ByteCategory { if self.0 == 0x00 { ByteCategory::Null } else if self.0.is_ascii_alphanumeric() || self.0.is_ascii_punctuation() || self.0.is_ascii_graphic() { ByteCategory::AsciiPrintable } else if self.0.is_ascii_whitespace() { ByteCategory::AsciiWhitespace } else if self.0.is_ascii() { ByteCategory::AsciiOther } else { ByteCategory::NonAscii } } fn color(self) -> &'static Color { use crate::ByteCategory::*; match self.category() { Null => &COLOR_NULL, AsciiPrintable => &COLOR_ASCII_PRINTABLE, AsciiWhitespace => &COLOR_ASCII_WHITESPACE, AsciiOther => &COLOR_ASCII_OTHER, NonAscii => &COLOR_NONASCII, } } fn as_char(self) -> char { use crate::ByteCategory::*; match self.category() { Null => '0', AsciiPrintable => self.0 as char, AsciiWhitespace if self.0 == 0x20 => ' ', AsciiWhitespace => '_', AsciiOther => '•', NonAscii => '×', } } } struct BorderElements { left_corner: char, horizontal_line: char, column_separator: char, right_corner: char, } pub enum BorderStyle { Unicode, Ascii, None, } impl BorderStyle { fn header_elems(&self) -> Option { 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 { match self { BorderStyle::Unicode => Some(BorderElements { left_corner: '└', horizontal_line: '─', column_separator: '┴', right_corner: '┘', }), BorderStyle::Ascii => Some(BorderElements { left_corner: '+', horizontal_line: '-', column_separator: '+', right_corner: '+', }), BorderStyle::None => None, } } fn outer_sep(&self) -> char { match self { BorderStyle::Unicode => '│', BorderStyle::Ascii => '|', BorderStyle::None => ' ', } } fn inner_sep(&self) -> char { match self { BorderStyle::Unicode => '┊', BorderStyle::Ascii => '|', BorderStyle::None => ' ', } } } pub struct Printer<'a, Writer: Write> { idx: usize, /// The raw bytes used as input for the current line. raw_line: Vec, /// The buffered line built with each byte, ready to print to writer. buffer_line: Vec, writer: &'a mut Writer, show_color: bool, border_style: BorderStyle, header_was_printed: bool, byte_hex_table: Vec, byte_char_table: Vec, squeezer: Squeezer, display_offset: usize, } impl<'a, Writer: Write> Printer<'a, Writer> { pub fn new( writer: &'a mut Writer, show_color: bool, border_style: BorderStyle, use_squeeze: bool, ) -> Printer<'a, Writer> { Printer { idx: 1, raw_line: vec![], buffer_line: vec![], writer, show_color, border_style, header_was_printed: false, byte_hex_table: (0u8..=u8::max_value()) .map(|i| { let byte_hex = format!("{:02x} ", i); if show_color { Byte(i).color().paint(byte_hex).to_string() } else { byte_hex } }) .collect(), byte_char_table: (0u8..=u8::max_value()) .map(|i| { let byte_char = format!("{}", Byte(i).as_char()); if show_color { Byte(i).color().paint(byte_char).to_string() } else { byte_char } }) .collect(), squeezer: Squeezer::new(use_squeeze), display_offset: 0, } } pub fn display_offset(&mut self, display_offset: usize) -> &mut Self { self.display_offset = display_offset; self } pub fn header(&mut self) { if let Some(border_elements) = self.border_style.header_elems() { let h = border_elements.horizontal_line; let h8 = h.to_string().repeat(8); let h25 = h.to_string().repeat(25); writeln!( self.writer, "{l}{h8}{c}{h25}{c}{h25}{c}{h8}{c}{h8}{r}", l = border_elements.left_corner, c = border_elements.column_separator, r = border_elements.right_corner, h8 = h8, h25 = h25 ) .ok(); } } pub fn footer(&mut self) { if let Some(border_elements) = self.border_style.footer_elems() { let h = border_elements.horizontal_line; let h8 = h.to_string().repeat(8); let h25 = h.to_string().repeat(25); writeln!( self.writer, "{l}{h8}{c}{h25}{c}{h25}{c}{h8}{c}{h8}{r}", l = border_elements.left_corner, c = border_elements.column_separator, r = border_elements.right_corner, h8 = h8, h25 = h25 ) .ok(); } } fn print_position_indicator(&mut self) { if !self.header_was_printed { self.header(); self.header_was_printed = true; } let style = COLOR_OFFSET.normal(); let byte_index = format!("{:08x}", (self.idx - 1) + self.display_offset); let formatted_string = if self.show_color { format!("{}", style.paint(byte_index)) } else { byte_index }; let _ = write!( &mut self.buffer_line, "{}{}{} ", self.border_style.outer_sep(), formatted_string, self.border_style.outer_sep() ); } pub fn print_byte(&mut self, b: u8) -> io::Result<()> { if self.idx % 16 == 1 { self.print_position_indicator(); } write!(&mut self.buffer_line, "{}", self.byte_hex_table[b as usize])?; self.raw_line.push(b); self.squeezer.process(b, self.idx); match self.idx % 16 { 8 => { let _ = write!(&mut self.buffer_line, "{} ", self.border_style.inner_sep()); } 0 => { self.print_textline()?; } _ => {} } self.idx += 1; Ok(()) } pub fn print_textline(&mut self) -> io::Result<()> { let len = self.raw_line.len(); if len == 0 { if self.squeezer.active() { self.print_position_indicator(); let _ = writeln!( &mut self.buffer_line, "{0:1$}{4}{0:2$}{5}{0:3$}{4}{0:3$}{5}", "", 24, 25, 8, self.border_style.inner_sep(), self.border_style.outer_sep(), ); self.writer.write_all(&self.buffer_line)?; } return Ok(()); } let squeeze_action = self.squeezer.action(); if squeeze_action != SqueezeAction::Delete { if len < 8 { let _ = write!( &mut self.buffer_line, "{0:1$}{3}{0:2$}{4}", "", 3 * (8 - len), 1 + 3 * 8, self.border_style.inner_sep(), self.border_style.outer_sep(), ); } else { let _ = write!( &mut self.buffer_line, "{0:1$}{2}", "", 3 * (16 - len), self.border_style.outer_sep() ); } let mut idx = 1; for &b in self.raw_line.iter() { let _ = write!( &mut self.buffer_line, "{}", self.byte_char_table[b as usize] ); if idx == 8 { let _ = write!(&mut self.buffer_line, "{}", self.border_style.inner_sep()); } idx += 1; } if len < 8 { let _ = writeln!( &mut self.buffer_line, "{0:1$}{3}{0:2$}{4}", "", 8 - len, 8, self.border_style.inner_sep(), self.border_style.outer_sep(), ); } else { let _ = writeln!( &mut self.buffer_line, "{0:1$}{2}", "", 16 - len, self.border_style.outer_sep() ); } } match squeeze_action { SqueezeAction::Print => { self.buffer_line.clear(); let style = COLOR_OFFSET.normal(); let asterisk = if self.show_color { format!("{}", style.paint("*")) } else { String::from("*") }; let _ = writeln!( &mut self.buffer_line, "{5}{0}{1:2$}{5}{1:3$}{6}{1:3$}{5}{1:4$}{6}{1:4$}{5}", asterisk, "", 7, 25, 8, self.border_style.outer_sep(), self.border_style.inner_sep(), ); } SqueezeAction::Delete => self.buffer_line.clear(), SqueezeAction::Ignore => (), } self.writer.write_all(&self.buffer_line)?; self.raw_line.clear(); self.buffer_line.clear(); self.squeezer.advance(); Ok(()) } pub fn header_was_printed(&self) -> bool { self.header_was_printed } /// Loop through the given `Reader`, printing until the `Reader` buffer /// is exhausted, or the optional `cancelled` bool is set to true. pub fn print_all( &mut self, mut reader: Reader, canceller: Option, ) -> Result<(), Box> { let mut buffer = [0; BUFFER_SIZE]; 'mainloop: loop { let size = reader.read(&mut buffer)?; if size == 0 { break; } if let Some(cancelled) = &canceller { if cancelled.load(Ordering::SeqCst) { eprintln!("hexyl has been cancelled."); std::process::exit(130); // Set exit code to 128 + SIGINT } } for b in &buffer[..size] { let res = self.print_byte(*b); if res.is_err() { // Broken pipe break 'mainloop; } } } // Finish last line self.print_textline().ok(); if !self.header_was_printed() { self.header(); } self.footer(); Ok(()) } } #[cfg(test)] mod tests { use std::io; use std::str; use super::*; fn assert_print_all_output(input: Reader, expected_string: String) -> () { let mut output = vec![]; let mut printer = Printer::new(&mut output, false, BorderStyle::Unicode, true); printer.print_all(input, None).unwrap(); let actual_string: &str = str::from_utf8(&output).unwrap(); assert_eq!(actual_string, expected_string) } #[test] fn empty_file_passes() { let input = io::empty(); let expected_string = "┌────────┬─────────────────────────┬─────────────────────────┬────────┬────────┐ └────────┴─────────────────────────┴─────────────────────────┴────────┴────────┘ " .to_owned(); assert_print_all_output(input, expected_string); } #[test] fn short_input_passes() { let input = io::Cursor::new(b"spam"); let expected_string = "┌────────┬─────────────────────────┬─────────────────────────┬────────┬────────┐ │00000000│ 73 70 61 6d ┊ │spam ┊ │ └────────┴─────────────────────────┴─────────────────────────┴────────┴────────┘ " .to_owned(); assert_print_all_output(input, expected_string); } #[test] fn display_offset() { let input = io::Cursor::new(b"spamspamspamspamspam"); let expected_string = "┌────────┬─────────────────────────┬─────────────────────────┬────────┬────────┐ │deadbeef│ 73 70 61 6d 73 70 61 6d ┊ 73 70 61 6d 73 70 61 6d │spamspam┊spamspam│ │deadbeff│ 73 70 61 6d ┊ │spam ┊ │ └────────┴─────────────────────────┴─────────────────────────┴────────┴────────┘ " .to_owned(); let mut output = vec![]; let mut printer: Printer> = Printer::new(&mut output, false, BorderStyle::Unicode, true); printer.display_offset(0xdeadbeef); printer.print_all(input, None).unwrap(); let actual_string: &str = str::from_utf8(&output).unwrap(); assert_eq!(actual_string, expected_string) } } hexyl-0.6.0/src/squeezer.rs010064400017500001735000000270551354715032100141040ustar0000000000000000#[derive(Debug, PartialEq)] enum SqueezeState { /// not enabled Disabled, /// Will be set from all states if equal condition can't be hold up. /// Set if previous byte is not equal the current processed byte. NoSqueeze, /// Valid for a whole line to identify if it is candidate for squeezing Probe, /// Squeeze line parsing is active, but EOL is not reached yet SqueezeActive, /// Squeeze line, EOL is reached, will influence the action Squeeze, /// same as Squeeze, however this is only for the first line after /// the squeeze candidate has been set. SqueezeFirstLine, /// same as SqueezeActive, however this is only for the first line after /// the squeeze candidate has been set. SqueezeActiveFirstLine, } pub struct Squeezer { state: SqueezeState, byte: u8, } #[derive(Debug, PartialEq)] pub enum SqueezeAction { Ignore, Print, Delete, } /// line size const LSIZE: usize = 16; impl Squeezer { pub fn new(enabled: bool) -> Squeezer { Squeezer { state: if enabled { SqueezeState::Probe } else { SqueezeState::Disabled }, byte: 0, } } pub fn process(&mut self, b: u8, i: usize) { use self::SqueezeState::*; if self.state == Disabled { return; } let eq = b == self.byte; if i % LSIZE == 0 { if !eq { self.state = Probe; } else { self.state = match self.state { NoSqueeze => Probe, Probe => SqueezeActiveFirstLine, SqueezeActiveFirstLine => SqueezeFirstLine, SqueezeFirstLine => SqueezeActive, SqueezeActive => Squeeze, Squeeze => SqueezeActive, Disabled => Disabled, }; } } else if !eq { if i % LSIZE == 1 { self.state = Probe; } else if i % LSIZE != 1 { self.state = NoSqueeze; } } self.byte = b; } pub fn active(&self) -> bool { use self::SqueezeState::*; match self.state { Squeeze | SqueezeActive | SqueezeFirstLine | SqueezeActiveFirstLine => true, _ => false, } } pub fn action(&self) -> SqueezeAction { match self.state { SqueezeState::SqueezeFirstLine => SqueezeAction::Print, SqueezeState::Squeeze => SqueezeAction::Delete, _ => SqueezeAction::Ignore, } } pub fn advance(&mut self) { match self.state { SqueezeState::SqueezeFirstLine => { self.state = SqueezeState::SqueezeActive; } SqueezeState::Squeeze => { self.state = SqueezeState::SqueezeActive; } _ => {} } } } #[cfg(test)] mod tests { use super::*; #[test] fn three_same_lines() { const LINES: usize = 3; let v = vec![0u8; LINES * LSIZE]; let mut s = Squeezer::new(true); // just initialized assert_eq!(s.action(), SqueezeAction::Ignore); s.advance(); let exp = vec![ SqueezeAction::Ignore, // first line, print as is SqueezeAction::Print, // print squeeze symbol SqueezeAction::Delete, // delete reoccurring line ]; let mut line = 0; let mut idx = 1; for z in v.chunks(LSIZE) { for i in z { s.process(*i, idx); idx += 1; } let action = s.action(); s.advance(); assert_eq!(action, exp[line]); line += 1; } } #[test] fn incomplete_while_squeeze() { // fourth line only has 12 bytes and should be printed let v = vec![0u8; 3 * LSIZE + 12]; let mut s = Squeezer::new(true); // just initialized assert_eq!(s.action(), SqueezeAction::Ignore); s.advance(); let exp = vec![ SqueezeAction::Ignore, // first line, print as is SqueezeAction::Print, // print squeeze symbol SqueezeAction::Delete, // delete reoccurring line SqueezeAction::Ignore, // last line only 12 bytes, print it ]; let mut line = 0; let mut idx = 1; for z in v.chunks(LSIZE) { for i in z { s.process(*i, idx); idx += 1; } assert_eq!(s.action(), exp[line]); s.advance(); line += 1; } } #[test] /// all three lines are different, print all fn three_different_lines() { let mut v: Vec = vec![]; v.extend(vec![0u8; 16]); v.extend(vec![1u8; 16]); v.extend(vec![2u8; 16]); let mut s = Squeezer::new(true); // just initialized assert_eq!(s.action(), SqueezeAction::Ignore); s.advance(); let exp = vec![ SqueezeAction::Ignore, // first line, print as is SqueezeAction::Ignore, // different SqueezeAction::Ignore, // different ]; let mut line = 0; let mut idx = 1; for z in v.chunks(LSIZE) { for i in z { s.process(*i, idx); idx += 1; } let action = s.action(); assert_eq!(action, exp[line]); s.advance(); line += 1; } } #[test] /// first two lines same, hence squeeze symbol, third line diff, hence /// print fn one_squeeze_no_delete() { const LINES: usize = 3; let mut v = vec![0u8; (LINES - 1) * LSIZE]; v.extend(vec![1u8; 16]); let mut s = Squeezer::new(true); // just initialized assert_eq!(s.action(), SqueezeAction::Ignore); s.advance(); let exp = vec![ SqueezeAction::Ignore, // first line, print as is SqueezeAction::Print, // print squeeze symbol SqueezeAction::Ignore, // different lines, print again ]; let mut line = 0; let mut idx = 1; for z in v.chunks(LSIZE) { for i in z { s.process(*i, idx); idx += 1; } let action = s.action(); s.advance(); assert_eq!(action, exp[line]); line += 1; } } #[test] /// First line all eq, 2nd half eq with first line, then change fn second_line_different() { const LINES: usize = 2; let mut v = vec![0u8; (LINES - 1) * LSIZE]; v.extend(vec![0u8; 8]); v.extend(vec![1u8; 8]); let mut s = Squeezer::new(true); // just initialized assert_eq!(s.action(), SqueezeAction::Ignore); s.advance(); let exp = vec![ SqueezeAction::Ignore, // first line, print as is SqueezeAction::Ignore, // print squeeze symbol ]; let mut line = 0; let mut idx = 1; for z in v.chunks(LSIZE) { for i in z { s.process(*i, idx); idx += 1; } let action = s.action(); s.advance(); assert_eq!(action, exp[line]); line += 1; } } #[test] /// all three lines never become squeeze candidate (diff within line) fn never_squeeze_candidate() { let mut v = vec![]; v.extend(vec![0u8; 8]); v.extend(vec![1u8; 8]); v.extend(vec![0u8; 8]); v.extend(vec![1u8; 8]); v.extend(vec![0u8; 8]); v.extend(vec![1u8; 8]); let mut s = Squeezer::new(true); // just initialized assert_eq!(s.action(), SqueezeAction::Ignore); s.advance(); let exp = vec![ SqueezeAction::Ignore, // first line, print as is SqueezeAction::Ignore, // print squeeze symbol SqueezeAction::Ignore, // print squeeze symbol ]; let mut line = 0; let mut idx = 1; for z in v.chunks(LSIZE) { for i in z { s.process(*i, idx); idx += 1; } let action = s.action(); s.advance(); assert_eq!(action, exp[line]); line += 1; } } #[test] fn mix_everything() { let mut v = vec![]; v.extend(vec![10u8; 16]); // print v.extend(vec![20u8; 16]); // print v.extend(vec![0u8; 16]); // print v.extend(vec![0u8; 16]); // * v.extend(vec![10u8; 16]); // print v.extend(vec![20u8; 16]); // print v.extend(vec![0u8; 16]); // print v.extend(vec![0u8; 16]); // * v.extend(vec![0u8; 16]); // delete v.extend(vec![0u8; 16]); // delete* v.extend(vec![20u8; 16]); // print v.extend(vec![0u8; 12]); // print, only 12 bytes let mut s = Squeezer::new(true); // just initialized assert_eq!(s.action(), SqueezeAction::Ignore); s.advance(); let exp = vec![ SqueezeAction::Ignore, SqueezeAction::Ignore, SqueezeAction::Ignore, SqueezeAction::Print, SqueezeAction::Ignore, SqueezeAction::Ignore, SqueezeAction::Ignore, SqueezeAction::Print, SqueezeAction::Delete, SqueezeAction::Delete, SqueezeAction::Ignore, SqueezeAction::Ignore, ]; let mut line = 0; let mut idx = 1; for z in v.chunks(LSIZE) { for i in z { s.process(*i, idx); idx += 1; } let action = s.action(); s.advance(); assert_eq!(action, exp[line]); line += 1; } } #[test] fn last_char_diff() { // see issue #62 let mut v = vec![]; v.extend(vec![20u8; 16]); v.extend(vec![20u8; 15]); v.push(61); v.extend(vec![20u8; 16]); v.extend(vec![20u8; 16]); let mut s = Squeezer::new(true); // just initialized assert_eq!(s.action(), SqueezeAction::Ignore); s.advance(); let exp = vec![ SqueezeAction::Ignore, // print as is SqueezeAction::Ignore, // print as is SqueezeAction::Ignore, // print as is SqueezeAction::Print, // print '*' char ]; let mut line = 0; let mut idx = 1; for z in v.chunks(LSIZE) { for i in z { s.process(*i, idx); idx += 1; } assert_eq!(s.action(), exp[line]); s.advance(); line += 1; } } #[test] fn first_char_diff() { // see issue #62 let mut v = vec![]; v.extend(vec![20u8; 16]); v.push(61); v.extend(vec![20u8; 15]); v.extend(vec![20u8; 16]); let mut s = Squeezer::new(true); // just initialized assert_eq!(s.action(), SqueezeAction::Ignore); s.advance(); let exp = vec![ SqueezeAction::Ignore, // print as is SqueezeAction::Ignore, // print as is SqueezeAction::Ignore, // print as is ]; let mut line = 0; let mut idx = 1; for z in v.chunks(LSIZE) { for i in z { s.process(*i, idx); idx += 1; } assert_eq!(s.action(), exp[line]); s.advance(); line += 1; } } } hexyl-0.6.0/.cargo_vcs_info.json0000644000000001120000000000000122210ustar00{ "git": { "sha1": "c75a8820815c4374bab5b20157c6fe4c577fcb96" } } hexyl-0.6.0/Cargo.lock0000644000000207100000000000000102020ustar00# This file is automatically @generated by Cargo. # It is not intended for manual editing. [[package]] name = "ansi_term" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "ansi_term" version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "atty" version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" 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)", ] [[package]] name = "bitflags" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "cc" version = "1.0.45" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "cfg-if" version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "clap" version = "2.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "atty 0.2.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)", ] [[package]] name = "ctrlc" version = "3.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" 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)", ] [[package]] name = "hexyl" version = "0.6.0" dependencies = [ "ansi_term 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)", "atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)", "clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)", "ctrlc 3.1.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "kernel32-sys" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" 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)", ] [[package]] name = "libc" version = "0.2.62" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "nix" version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" 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)", ] [[package]] name = "strsim" version = "0.8.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.62 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "textwrap" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "term_size 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-width 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "unicode-width" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "vec_map" version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "void" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "winapi" version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "winapi" version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "winapi-build" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[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"