nxxd 1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE +52 -0
- data/README.md +106 -0
- data/bin/nxxd +219 -0
- data/lib/nxxd/nvim.rb +116 -0
- data/lib/nxxd/version.rb +12 -0
- data/lib/nxxd.rb +201 -0
- data/nxxd.gemspec +37 -0
- metadata +51 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 5235dca5d1f46e13930f0d67076555a589a8966f4e83abf933a805ca722abbfe
|
|
4
|
+
data.tar.gz: 16ee0e67f7b9b8d765639470d7aff99c8e4aa71a5668493808589fcd42089ccd
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: b9b88cd360d0af386401df8eada33e03718790136ce709d4f5d74ea4f54311d2a22df54e7f6d157d6a8ed53a5d073ccf1958f40f582aa8d2cae3b4d7f74dc175
|
|
7
|
+
data.tar.gz: 3e6a4f1b49aeef9867e80b0b900b99951a6b37d53d4b8f1ee82d37f9d58f773355f2a72d651e5df734022d942d7673d0c7af76f384f5a8cd564eb69ff2b2dadc
|
data/LICENSE
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# BSD-2-clause license, extended by language use conditions
|
|
2
|
+
|
|
3
|
+
Copyright (C) 2025, Bertram Scharpf <software@bertram-scharpf.de>.
|
|
4
|
+
All rights reserved.
|
|
5
|
+
|
|
6
|
+
Redistribution and use in source and binary forms, with or without
|
|
7
|
+
modification, are permitted provided that the following conditions are
|
|
8
|
+
met:
|
|
9
|
+
|
|
10
|
+
* Redistributions of source code must retain the above copyright
|
|
11
|
+
notice, this list of conditions and the following disclaimer.
|
|
12
|
+
|
|
13
|
+
* Redistributions in binary form must reproduce the above copyright
|
|
14
|
+
notice, this list of conditions and the following disclaimer in
|
|
15
|
+
the documentation and/or other materials provided with the
|
|
16
|
+
distribution.
|
|
17
|
+
|
|
18
|
+
* Redistributions must not contain any clauses about anticipated
|
|
19
|
+
harassment or discrimination, nor must they be held in a so-called
|
|
20
|
+
"inclusive language". As far as German language is used, the
|
|
21
|
+
conditions mentioned below additionally apply.
|
|
22
|
+
|
|
23
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
|
24
|
+
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
|
25
|
+
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
|
26
|
+
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
|
27
|
+
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
28
|
+
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
29
|
+
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
|
30
|
+
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
|
31
|
+
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
32
|
+
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
33
|
+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
## Use of the German Language
|
|
37
|
+
|
|
38
|
+
Beim Gebrauch deutscher Sprache sind Weiterentwicklungen und
|
|
39
|
+
-verbreitungen nur gestattet unter Einhaltung sowie abermaligen
|
|
40
|
+
Einforderns folgender zusätzlicher Bedingungen:
|
|
41
|
+
|
|
42
|
+
* Keine Verwendung von sogenannter „geschlechtergerechter Sprache“,
|
|
43
|
+
also Anfügen von weiblichen Endungen mit Binnen-I, Sternchen,
|
|
44
|
+
Doppelpunkt, Unterstrich oder ähnlichem, oder Konstruktionen, die
|
|
45
|
+
den Sachverhalt falsch wiedergeben („Radfahrende“, „Studierende“).
|
|
46
|
+
|
|
47
|
+
* Keine Verwendung der „reformierten Rechtschreibung“ von 1996,
|
|
48
|
+
insbesondere Doppel-S am Silbenende, „plazieren“ mit T, sowie
|
|
49
|
+
Großschreibung von Wendungen wie „des weiteren“.
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
<!-- vim:set ft=markdown : -->
|
data/README.md
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# Nxxd Hex Dump tool
|
|
2
|
+
|
|
3
|
+
Yet another Xxd reimplementation.
|
|
4
|
+
|
|
5
|
+
The original Xxd is part of the Vim editor (<https://www.vim.org>).
|
|
6
|
+
This one is written in plain Ruby.
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
sudo gem install nxxd
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
## Command line execution
|
|
17
|
+
|
|
18
|
+
Plain output:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
echo hello | grep --color=yes -nH ll | nxxd
|
|
22
|
+
dd if=/dev/urandom bs=16 count=4 status=none | nxxd
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
The filename will be added as a comment, unless you explicitly ask to refrain
|
|
26
|
+
from that.
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
nxxd /bin/sleep 2>/dev/null | head
|
|
30
|
+
nxxd -p /bin/sleep 2>/dev/null | head
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Repeated lines will be squeezed by default.
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
dd if=/dev/zero bs=16 count=4 status=none | nxxd
|
|
37
|
+
dd if=/dev/zero bs=16 count=4 status=none | nxxd -a
|
|
38
|
+
dd if=/dev/zero bs=16 count=4 status=none | nxxd -f
|
|
39
|
+
ruby -e 'print "xyz="*16' | nxxd
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Reverse operation.
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
echo '42617a696e6761210a' | nxxd -r
|
|
46
|
+
echo hello | grep --color=yes -nH ll | nxxd | nxxd -r
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
C source code output.
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
nxxd -i someimage.png
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Get help.
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
nxxd -h
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
## Ruby classes and methods
|
|
63
|
+
|
|
64
|
+
Here's an example:
|
|
65
|
+
|
|
66
|
+
```ruby
|
|
67
|
+
require "nxxd"
|
|
68
|
+
|
|
69
|
+
data = " !\"\#$%&'()*+,-./0123456789:;<=>?"
|
|
70
|
+
Nxxd::Dump.new.run data do |l| puts l end
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Reverse operation allows free address jumping.
|
|
74
|
+
Both string and file output can be done.
|
|
75
|
+
|
|
76
|
+
```ruby
|
|
77
|
+
require "nxxd"
|
|
78
|
+
|
|
79
|
+
x = <<~EOT
|
|
80
|
+
0004: 204d 696c 6b79 2047 7265 656e 0a Milky Green
|
|
81
|
+
0000: 436f 6465 Code
|
|
82
|
+
EOT
|
|
83
|
+
|
|
84
|
+
s = Nxxd::Dump.reverse x
|
|
85
|
+
puts s.encoding, s.length, s.inspect
|
|
86
|
+
|
|
87
|
+
File.open "status", "w" do |f|
|
|
88
|
+
Nxxd::Dump.reverse x, f
|
|
89
|
+
end
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
## Colorization
|
|
94
|
+
|
|
95
|
+
There is no and there will be no color support. Pipe the output to your
|
|
96
|
+
favourite editor and use the syntax highlighting there.
|
|
97
|
+
|
|
98
|
+
If you are using Vim/Neovim, you might like the more elaborate syntax
|
|
99
|
+
highlighting included in this package [xxd.vim](./vim/syntax/xxd.vim).
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
## Copyright
|
|
103
|
+
|
|
104
|
+
* (C) 2025 Bertram Scharpf <software@bertram-scharpf.de>
|
|
105
|
+
* License: [BSD-2-Clause+](./LICENSE)
|
|
106
|
+
|
data/bin/nxxd
ADDED
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
#
|
|
4
|
+
# nxxd -- Hex dump tool
|
|
5
|
+
#
|
|
6
|
+
|
|
7
|
+
require "nxxd"
|
|
8
|
+
require "nxxd/version"
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
module Nxxd
|
|
12
|
+
|
|
13
|
+
class Application
|
|
14
|
+
|
|
15
|
+
NAME = "nxxd"
|
|
16
|
+
|
|
17
|
+
OPTIONS = {
|
|
18
|
+
"r" => [ :reverse, true, "Reverse operation, pack"],
|
|
19
|
+
"o" => [ :output, :NAME, "Output file (- = stdout)"],
|
|
20
|
+
"output" => "o",
|
|
21
|
+
"f" => [ :full, true, "Full output (no squeeze)"],
|
|
22
|
+
"full" => "f",
|
|
23
|
+
"a" => [ :full, false, "Abbreviate (squeeze duplicate lines)"],
|
|
24
|
+
"p" => [ :plain, true, "Plain output (no comments, no C variables)"],
|
|
25
|
+
"u" => [ :upper, true, "Upper case A-F"],
|
|
26
|
+
"upper" => "u",
|
|
27
|
+
"l" => [ :line_size, :NUM, "Line size"],
|
|
28
|
+
"line" => "l",
|
|
29
|
+
"d" => [ :addr_len, :NUM, "Address length"],
|
|
30
|
+
"addrs" => "d",
|
|
31
|
+
"i" => [ :cnums, true, "C-style number literals"],
|
|
32
|
+
"include" => "i",
|
|
33
|
+
"C" => [ :capitals, true, "Capitalize C variable names with -i"],
|
|
34
|
+
"capitalize" => "C",
|
|
35
|
+
"h" => [ :help, :help, "Print this help message"],
|
|
36
|
+
"help" => "h",
|
|
37
|
+
"v" => [ :version, :version, "Version information"],
|
|
38
|
+
"version" => "v",
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
OPT_ENV = "NXXD"
|
|
42
|
+
|
|
43
|
+
class Options
|
|
44
|
+
|
|
45
|
+
attr_reader :opts, :args
|
|
46
|
+
|
|
47
|
+
def initialize params
|
|
48
|
+
@opts, @args = {}, []
|
|
49
|
+
env = ENV[ OPT_ENV]
|
|
50
|
+
if env then
|
|
51
|
+
@params = env.split
|
|
52
|
+
until @params.empty? do
|
|
53
|
+
p = @params.shift
|
|
54
|
+
case p
|
|
55
|
+
when /^--/ then $'.empty? or set $'
|
|
56
|
+
when /^-/ then set_short $'
|
|
57
|
+
else set p
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
@params = params
|
|
62
|
+
until @params.empty? do
|
|
63
|
+
p = @params.shift
|
|
64
|
+
case p
|
|
65
|
+
when /^--/ then $'.empty? and break ; set $'
|
|
66
|
+
when /^-/ then set_short $'
|
|
67
|
+
else @args.push p
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
@args.concat @params
|
|
71
|
+
@params = nil
|
|
72
|
+
rescue StandardError
|
|
73
|
+
$stderr.puts $!
|
|
74
|
+
exit 1
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
private
|
|
78
|
+
|
|
79
|
+
def set_short cont
|
|
80
|
+
@cont = cont
|
|
81
|
+
until @cont.empty? do
|
|
82
|
+
c = @cont.slice! 0
|
|
83
|
+
set c
|
|
84
|
+
end
|
|
85
|
+
ensure
|
|
86
|
+
@cont = nil
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def set o
|
|
90
|
+
opt = loop do
|
|
91
|
+
d = OPTIONS[ o]
|
|
92
|
+
raise "Unknown option: #{o}" unless d
|
|
93
|
+
break d if Array === d
|
|
94
|
+
o = d
|
|
95
|
+
end
|
|
96
|
+
key, t, = opt
|
|
97
|
+
if Symbol === t then
|
|
98
|
+
type = type_repr t
|
|
99
|
+
if type then
|
|
100
|
+
arg = @cont.slice! 0..nil if @cont
|
|
101
|
+
arg = @params.shift if not arg or arg.empty?
|
|
102
|
+
arg = Integer arg if %i(NUM INT).include? type
|
|
103
|
+
else
|
|
104
|
+
send t
|
|
105
|
+
exit 0
|
|
106
|
+
end
|
|
107
|
+
else
|
|
108
|
+
arg = t
|
|
109
|
+
end
|
|
110
|
+
@opts[ key] = arg
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def version
|
|
114
|
+
puts "#{NAME} -- #{VERSION}"
|
|
115
|
+
puts AUTHOR
|
|
116
|
+
puts "License: #{LICENSE}"
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def help
|
|
120
|
+
version
|
|
121
|
+
puts
|
|
122
|
+
puts "Usage: #$0 [options] [ inputfile+ [ outputfile ] ]"
|
|
123
|
+
puts
|
|
124
|
+
OPTIONS.each { |opt,defn|
|
|
125
|
+
case defn
|
|
126
|
+
when String then
|
|
127
|
+
text = "= #{opt_repr defn}"
|
|
128
|
+
else
|
|
129
|
+
_, arg, text = *defn
|
|
130
|
+
type = type_repr arg if Symbol === arg
|
|
131
|
+
end
|
|
132
|
+
puts " %-10s %-10s %s" % [(opt_repr opt), type, text]
|
|
133
|
+
}
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def type_repr type
|
|
137
|
+
type if type =~ /\A[A-Z_]+\z/
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def opt_repr o
|
|
141
|
+
o.length == 1 ? "-#{o}" : "--#{o}"
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def initialize params
|
|
147
|
+
o = Options.new params
|
|
148
|
+
@opts, @args = o.opts, o.args
|
|
149
|
+
@inputs = []
|
|
150
|
+
@inputs.push @args.shift
|
|
151
|
+
@output = (@opts.delete :output) || @args.pop
|
|
152
|
+
@inputs.push @args.shift until @args.empty?
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
def run
|
|
156
|
+
open_output do |o|
|
|
157
|
+
if @opts[ :reverse] then
|
|
158
|
+
open_inputs do |f|
|
|
159
|
+
Dump.reverse f, o
|
|
160
|
+
end
|
|
161
|
+
else
|
|
162
|
+
cls = (@opts.delete :cnums) ? DumpNums : Dump
|
|
163
|
+
open_inputs do |f|
|
|
164
|
+
d = cls.new **@opts
|
|
165
|
+
d.run f do |l| o.puts l end
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
rescue ArgumentError
|
|
170
|
+
$stderr.puts "Incompatible Options: #@opts"
|
|
171
|
+
1
|
|
172
|
+
rescue Errno::EPIPE
|
|
173
|
+
$stderr.puts "Broken pipe. Stop."
|
|
174
|
+
rescue Errno::ESPIPE
|
|
175
|
+
$stderr.puts "Seek not possible. Specify an output file."
|
|
176
|
+
2
|
|
177
|
+
rescue Interrupt
|
|
178
|
+
$stderr.puts "Interrupted."
|
|
179
|
+
1
|
|
180
|
+
rescue
|
|
181
|
+
$stderr.puts "Error: #$! (#{$!.class})"
|
|
182
|
+
1
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
private
|
|
186
|
+
|
|
187
|
+
def open_inputs
|
|
188
|
+
plain = @opts.delete :plain
|
|
189
|
+
@inputs.each do |i|
|
|
190
|
+
i = nil if i == "-"
|
|
191
|
+
@opts[ :input] = i unless plain
|
|
192
|
+
if i then
|
|
193
|
+
File.open i, "r" do |f|
|
|
194
|
+
yield f
|
|
195
|
+
end
|
|
196
|
+
else
|
|
197
|
+
yield $stdin
|
|
198
|
+
end
|
|
199
|
+
ensure
|
|
200
|
+
@opts.delete :input
|
|
201
|
+
end
|
|
202
|
+
nil
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
def open_output
|
|
206
|
+
o = @output unless @output == "-"
|
|
207
|
+
if o then
|
|
208
|
+
File.open o, "w" do |o| yield o end
|
|
209
|
+
else
|
|
210
|
+
yield $stdout
|
|
211
|
+
end
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
exit (Application.new $*).run.to_i
|
|
217
|
+
|
|
218
|
+
end
|
|
219
|
+
|
data/lib/nxxd/nvim.rb
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
#
|
|
2
|
+
# nxxd/nvim.rb -- Neovim commands
|
|
3
|
+
#
|
|
4
|
+
|
|
5
|
+
require "nxxd"
|
|
6
|
+
|
|
7
|
+
(Neovim::Client === $vim rescue false) or
|
|
8
|
+
raise "This file must be required from inside Neovim using the 'nvim' Rubygem."
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
[
|
|
12
|
+
"command -nargs=? -bang -complete=file -bar HexDump call rubyeval('Nxxd::Nvim.dump<bang> %q|<args>|')",
|
|
13
|
+
"command -nargs=? -range=% -bang -complete=file -bar HexPack call rubyeval('Nxxd::Nvim.pack<bang> %q|<args>|, <line1>..<line2>')",
|
|
14
|
+
].each { |c| $vim.command c }
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
module Kernel
|
|
19
|
+
class <<self
|
|
20
|
+
def popen_outerr a
|
|
21
|
+
ro, wo = IO.pipe
|
|
22
|
+
re, we = IO.pipe
|
|
23
|
+
fork do
|
|
24
|
+
ro.close
|
|
25
|
+
re.close
|
|
26
|
+
STDOUT.reopen wo
|
|
27
|
+
STDERR.reopen we
|
|
28
|
+
exec a
|
|
29
|
+
end
|
|
30
|
+
wo.close
|
|
31
|
+
we.close
|
|
32
|
+
yield ro, re
|
|
33
|
+
Process.waitpid
|
|
34
|
+
ensure
|
|
35
|
+
ro.close
|
|
36
|
+
re.close
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
module Nxxd
|
|
43
|
+
|
|
44
|
+
module Nvim
|
|
45
|
+
|
|
46
|
+
class <<self
|
|
47
|
+
|
|
48
|
+
def dump a
|
|
49
|
+
a ||= $vim.get_name 0
|
|
50
|
+
File.open a do |f|
|
|
51
|
+
dump_file a, f
|
|
52
|
+
end
|
|
53
|
+
$vim.get_current_buf.set_var "origfile", a
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def dump! a
|
|
57
|
+
Kernel.popen_outerr a do |ro,re|
|
|
58
|
+
dump_file a, ro
|
|
59
|
+
e = re.read.lines.map { |x| x.chomp! ; "# #{x}" }
|
|
60
|
+
$vim.put e, "l", false, true
|
|
61
|
+
end
|
|
62
|
+
$?.success? or $vim.put [ "### Exit code: #{$?.exitstatus}"], "l", false, true
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
private
|
|
66
|
+
|
|
67
|
+
def dump_file a, f
|
|
68
|
+
$vim.command "enew"
|
|
69
|
+
$vim.set_option filetype: "xxd", buftype: "nofile"
|
|
70
|
+
$vim.get_current_buf.set_name "[hex: #{a}]"
|
|
71
|
+
(Dump.new input: a).run f do |l|
|
|
72
|
+
$vim.put [l], "l", false, true
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
public
|
|
77
|
+
|
|
78
|
+
def pack a, range
|
|
79
|
+
pack_check a, range do |d|
|
|
80
|
+
raise "File exists. Overwrite with '!'." if File.exist? d
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def pack! a, range
|
|
85
|
+
pack_check a, range do || end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
private
|
|
89
|
+
|
|
90
|
+
def pack_check a, range
|
|
91
|
+
b = $vim.get_current_buf
|
|
92
|
+
a = a.notempty?
|
|
93
|
+
a ||= b.get_var "origfile" rescue nil
|
|
94
|
+
a or raise "No file name given."
|
|
95
|
+
yield a
|
|
96
|
+
File.open a, "w" do |f|
|
|
97
|
+
r = Range.new b, range
|
|
98
|
+
Nxxd::Dump.reverse r, f
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
class Range
|
|
105
|
+
def initialize buf, range
|
|
106
|
+
@buf, @range = buf, range
|
|
107
|
+
end
|
|
108
|
+
def each_line &block
|
|
109
|
+
@buf.each @range, &block
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
end
|
|
116
|
+
|
data/lib/nxxd/version.rb
ADDED
data/lib/nxxd.rb
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
#
|
|
2
|
+
# nxxd.rb -- Hex Dump Tool
|
|
3
|
+
#
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
module Nxxd
|
|
7
|
+
|
|
8
|
+
module ReadChunks
|
|
9
|
+
|
|
10
|
+
private
|
|
11
|
+
|
|
12
|
+
def read_chunks input
|
|
13
|
+
case input
|
|
14
|
+
when String then
|
|
15
|
+
i = 0
|
|
16
|
+
while i < input.bytesize do
|
|
17
|
+
b = input.byteslice i, @line_size
|
|
18
|
+
yield b
|
|
19
|
+
i += @line_size
|
|
20
|
+
end
|
|
21
|
+
else
|
|
22
|
+
loop do
|
|
23
|
+
b = input.read @line_size
|
|
24
|
+
break unless b
|
|
25
|
+
yield b
|
|
26
|
+
break if b.length < @line_size
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
class Dump
|
|
34
|
+
|
|
35
|
+
include ReadChunks
|
|
36
|
+
|
|
37
|
+
LINE_SIZE = 16
|
|
38
|
+
ADDR_FMT = "%%0%dx:"
|
|
39
|
+
|
|
40
|
+
def initialize full: nil, upper: false, line_size: nil, addr_len: nil, input: nil
|
|
41
|
+
@full = full
|
|
42
|
+
@input = input
|
|
43
|
+
@line_size = line_size||LINE_SIZE
|
|
44
|
+
@addr_fmt = ADDR_FMT % (addr_len||8)
|
|
45
|
+
@nib_fmt = "%02x"
|
|
46
|
+
if upper then
|
|
47
|
+
@addr_fmt.upcase!
|
|
48
|
+
@nib_fmt.upcase!
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def run input
|
|
53
|
+
addr = 0
|
|
54
|
+
prev, repeat = nil, false
|
|
55
|
+
yield "# #@input" if @input
|
|
56
|
+
read_chunks input do |b|
|
|
57
|
+
if b == prev and not @full then
|
|
58
|
+
unless repeat then
|
|
59
|
+
yield "*"
|
|
60
|
+
repeat = true
|
|
61
|
+
end
|
|
62
|
+
else
|
|
63
|
+
r = @addr_fmt % addr
|
|
64
|
+
r << " "
|
|
65
|
+
h = b.unpack "C*"
|
|
66
|
+
sp = false
|
|
67
|
+
@line_size.times {
|
|
68
|
+
x = h.shift
|
|
69
|
+
r << (x ? @nib_fmt % x : " ")
|
|
70
|
+
r << " " if sp
|
|
71
|
+
sp = !sp
|
|
72
|
+
}
|
|
73
|
+
r << " " << (b.gsub /[^ -~]/, ".")
|
|
74
|
+
yield r
|
|
75
|
+
prev, repeat = b, false
|
|
76
|
+
end
|
|
77
|
+
addr += b.size
|
|
78
|
+
end
|
|
79
|
+
yield @addr_fmt % addr
|
|
80
|
+
nil
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
class <<self
|
|
85
|
+
|
|
86
|
+
def reverse input, output = nil
|
|
87
|
+
output ||= ""
|
|
88
|
+
o = String === output ? (WriteChunksString.new output) : output
|
|
89
|
+
o.set_encoding Encoding::ASCII_8BIT
|
|
90
|
+
r, repeat = nil, false
|
|
91
|
+
input.each_line { |l|
|
|
92
|
+
l.chomp!
|
|
93
|
+
case l
|
|
94
|
+
when /^\s*(?:#|$)/ then nil
|
|
95
|
+
when /^\*/ then repeat = true
|
|
96
|
+
when /^(?:(\h+):)?\s*((?:\h\h ?)*)/ then
|
|
97
|
+
addr, nibs = $~.captures
|
|
98
|
+
if addr then
|
|
99
|
+
addr = $1.to_i 0x10
|
|
100
|
+
if repeat then
|
|
101
|
+
loop do
|
|
102
|
+
s = addr - o.tell
|
|
103
|
+
break if s <= 0
|
|
104
|
+
o.write s >= r.length ? r : r[ 0, s]
|
|
105
|
+
end
|
|
106
|
+
repeat = false
|
|
107
|
+
else
|
|
108
|
+
end
|
|
109
|
+
o.seek addr
|
|
110
|
+
end
|
|
111
|
+
r = (nibs.scan /\h\h/).map { |x| x.to_i 0x10 }.pack "C*"
|
|
112
|
+
o.write r
|
|
113
|
+
else
|
|
114
|
+
raise "Uninterpretable hex dump: #{l.chomp}"
|
|
115
|
+
end
|
|
116
|
+
}
|
|
117
|
+
output
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
class DumpNums
|
|
125
|
+
|
|
126
|
+
include ReadChunks
|
|
127
|
+
|
|
128
|
+
LINE_SIZE = 12
|
|
129
|
+
|
|
130
|
+
def initialize upper: false, line_size: nil, capitals: nil, input: nil
|
|
131
|
+
@line_size = line_size||LINE_SIZE
|
|
132
|
+
@nib_fmt = "%#04x"
|
|
133
|
+
@nib_fmt.upcase! if upper
|
|
134
|
+
if input then
|
|
135
|
+
@varname = input.dup
|
|
136
|
+
@varname.insert 0, "__" if @varname =~ /\A\d/
|
|
137
|
+
@varname.gsub! /[^a-zA-Z0-9]/, "_"
|
|
138
|
+
@varname.upcase! if capitals
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def run input, &block
|
|
143
|
+
if @varname then
|
|
144
|
+
yield "unsigned char #@varname[] = {"
|
|
145
|
+
len = run_plain input, &block
|
|
146
|
+
yield "};"
|
|
147
|
+
yield "unsigned int #@varname\_len = %d;" % len
|
|
148
|
+
else
|
|
149
|
+
run_plain input, &block
|
|
150
|
+
end
|
|
151
|
+
nil
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
private
|
|
155
|
+
|
|
156
|
+
def run_plain input
|
|
157
|
+
prev, len = nil, 0
|
|
158
|
+
read_chunks input do |b|
|
|
159
|
+
if prev then
|
|
160
|
+
prev << ","
|
|
161
|
+
yield prev
|
|
162
|
+
end
|
|
163
|
+
prev = " " + ((b.unpack "C*").map { |x| @nib_fmt % x }.join ", ")
|
|
164
|
+
len += b.bytesize
|
|
165
|
+
end
|
|
166
|
+
yield prev if prev
|
|
167
|
+
len
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
class WriteChunksString
|
|
173
|
+
def initialize str
|
|
174
|
+
@str = str
|
|
175
|
+
end
|
|
176
|
+
def set_encoding enc
|
|
177
|
+
@str.force_encoding enc
|
|
178
|
+
end
|
|
179
|
+
def tell ; @pos || @str.length ; end
|
|
180
|
+
def seek pos
|
|
181
|
+
s = pos - @str.length
|
|
182
|
+
if s >= 0 then
|
|
183
|
+
s.times { @str << "\0".b }
|
|
184
|
+
else
|
|
185
|
+
@pos = pos
|
|
186
|
+
end
|
|
187
|
+
end
|
|
188
|
+
def write b
|
|
189
|
+
if @pos then
|
|
190
|
+
l = b.length
|
|
191
|
+
@str[ @pos, l] = b
|
|
192
|
+
@pos += l
|
|
193
|
+
@pos = nil if @pos >= @str.length
|
|
194
|
+
else
|
|
195
|
+
@str << b
|
|
196
|
+
end
|
|
197
|
+
end
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
end
|
|
201
|
+
|
data/nxxd.gemspec
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
#
|
|
2
|
+
# nxxd.gemspec -- Gem Specification
|
|
3
|
+
#
|
|
4
|
+
|
|
5
|
+
require "./lib/nxxd/version"
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
Gem::Specification.new do |s|
|
|
9
|
+
s.name = "nxxd"
|
|
10
|
+
s.version = Nxxd::VERSION
|
|
11
|
+
s.platform = Gem::Platform::RUBY
|
|
12
|
+
s.required_ruby_version = ">= 3.1"
|
|
13
|
+
s.summary = "Hex Dump Tool"
|
|
14
|
+
s.description = <<~EOT
|
|
15
|
+
Yet another Xxd reimplementation.
|
|
16
|
+
EOT
|
|
17
|
+
s.license = "LicenseRef-LICENSE"
|
|
18
|
+
|
|
19
|
+
s.authors,
|
|
20
|
+
s.email = (<<~EOT.scan /(\S.*\S)\s*<(.*)>/).transpose
|
|
21
|
+
Bertram Scharpf <software@bertram-scharpf.de>
|
|
22
|
+
EOT
|
|
23
|
+
|
|
24
|
+
s.homepage = "https://github.com/BertramScharpf/ruby-nxxd"
|
|
25
|
+
|
|
26
|
+
s.requirements = "Just Ruby"
|
|
27
|
+
|
|
28
|
+
s.require_paths = %w(lib)
|
|
29
|
+
s.files = Dir[ "lib/**/*.rb", "bin/*", ] +
|
|
30
|
+
%w(LICENSE README.md nxxd.gemspec)
|
|
31
|
+
s.bindir = "bin"
|
|
32
|
+
s.extensions = %w()
|
|
33
|
+
s.executables = %w(nxxd)
|
|
34
|
+
s.extra_rdoc_files = %w()
|
|
35
|
+
|
|
36
|
+
end
|
|
37
|
+
|
metadata
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: nxxd
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: '1.0'
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Bertram Scharpf
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 2025-01-30 00:00:00.000000000 Z
|
|
11
|
+
dependencies: []
|
|
12
|
+
description: 'Yet another Xxd reimplementation.
|
|
13
|
+
|
|
14
|
+
'
|
|
15
|
+
email:
|
|
16
|
+
- software@bertram-scharpf.de
|
|
17
|
+
executables:
|
|
18
|
+
- nxxd
|
|
19
|
+
extensions: []
|
|
20
|
+
extra_rdoc_files: []
|
|
21
|
+
files:
|
|
22
|
+
- LICENSE
|
|
23
|
+
- README.md
|
|
24
|
+
- bin/nxxd
|
|
25
|
+
- lib/nxxd.rb
|
|
26
|
+
- lib/nxxd/nvim.rb
|
|
27
|
+
- lib/nxxd/version.rb
|
|
28
|
+
- nxxd.gemspec
|
|
29
|
+
homepage: https://github.com/BertramScharpf/ruby-nxxd
|
|
30
|
+
licenses:
|
|
31
|
+
- LicenseRef-LICENSE
|
|
32
|
+
metadata: {}
|
|
33
|
+
rdoc_options: []
|
|
34
|
+
require_paths:
|
|
35
|
+
- lib
|
|
36
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - ">="
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '3.1'
|
|
41
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
42
|
+
requirements:
|
|
43
|
+
- - ">="
|
|
44
|
+
- !ruby/object:Gem::Version
|
|
45
|
+
version: '0'
|
|
46
|
+
requirements:
|
|
47
|
+
- Just Ruby
|
|
48
|
+
rubygems_version: 3.6.2
|
|
49
|
+
specification_version: 4
|
|
50
|
+
summary: Hex Dump Tool
|
|
51
|
+
test_files: []
|