-
Notifications
You must be signed in to change notification settings - Fork 18
/
Copy pathmain.rs
111 lines (92 loc) · 3.5 KB
/
main.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
//! The HEx Helper is a cross-platform terminal UI used for modifying file data in hex or ASCII.
//! It aims to replicate some of the look of [hexyl](https://github.com/sharkdp/hexyl) while
//! functionaly acting like a terminal UI version of [GHex](https://wiki.gnome.org/Apps/Ghex).
//!
//! **heh is currently in alpha** - it's not ready to be used in any production manner. It lacks a
//! variety of quality of life features and does not store backups if killed or crashing.
use std::{error::Error, fs::OpenOptions, io, process};
use clap::{Parser, ValueEnum};
use ratatui::crossterm::tty::IsTty;
use heh::app::Application;
use heh::decoder::Encoding;
const ABOUT: &str = "
A HEx Helper to edit bytes by the nibble.
Do --help for more information.";
const LONG_ABOUT: &str = "
The HEx Helper is a terminal tool used for modifying binaries by
the nibble. It aims to replicate some of the look of hexyl while
functionally acting like a terminal UI version of GHex.
Note that the octal and hexadecimal labels are slightly
different in heh; they interpret the stream as if 0s were filled
to the end of the byte (i.e. stream length 9 on FF FF would
produce octal 377 200 and hexadecimal FF 80).
Like GHex, you cannot create files with heh, only modify them.
Terminal UI Commands:
ALT= Increase the stream length by 1
ALT- Decrease the stream length by 1
CNTRLs Save
CNTRLq Quit
CNTRLj Jump to Byte
CNTRLe Switch Endianness
CNTRLd Page Down
CNTRLu Page Up
CNTRLf or / Search
CNTRLn or Enter Next Search Match
CNTRLp Prev Search Match
Left-clicking on a label will copy the contents to the clipboard.
Left-clicking on the ASCII or hex table will focus it.
Zooming in and out will change the size of the components.";
#[derive(Parser)]
#[command(version, about = ABOUT, long_about = LONG_ABOUT)]
struct Cli {
#[arg(
value_enum,
short = 'e',
long = "encoding",
default_value = "ascii",
help = "Encoding used for text editor"
)]
encoding: EncodingOption,
#[arg(
value_parser = parse_hex_or_dec,
long = "offset",
default_value = "0",
help="Read file at offset (indicated by a decimal or hexadecimal number)"
)]
offset: usize,
// Positional argument.
#[arg(help = "File to open")]
file: String,
}
/// Opens the specified file, creates a new application and runs it!
fn main() -> Result<(), Box<dyn Error>> {
if !io::stdout().is_tty() {
eprintln!("Stdout is not a TTY device.");
process::exit(1);
}
let cli = Cli::parse();
let file = OpenOptions::new().read(true).write(true).open(cli.file)?;
let mut app = Application::new(file, cli.encoding.into(), cli.offset)?;
app.run()?;
Ok(())
}
#[derive(ValueEnum, Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum EncodingOption {
Ascii,
Utf8,
}
impl From<EncodingOption> for Encoding {
fn from(encoding: EncodingOption) -> Self {
match encoding {
EncodingOption::Ascii => Encoding::Ascii,
EncodingOption::Utf8 => Encoding::Utf8,
}
}
}
fn parse_hex_or_dec(arg: &str) -> Result<usize, String> {
if let Some(stripped) = arg.strip_prefix("0x") {
usize::from_str_radix(stripped, 16).map_err(|e| format!("Invalid hexadecimal number: {e}"))
} else {
arg.parse().map_err(|e| format!("Invalid decimal number: {e}"))
}
}