-
Notifications
You must be signed in to change notification settings - Fork 0
/
ahd.apl
executable file
·291 lines (237 loc) · 9.67 KB
/
ahd.apl
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
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
#!/usr/local/bin/apl --script
⍝ This file is part of AHD.
⍝
⍝ Copyright (c) 2024 ona-li-toki-e-jan-Epiphany-tawa-mi
⍝
⍝ AHD is free software: you can redistribute it and/or modify it under the terms
⍝ of the GNU General Public License as published by the Free Software
⍝ Foundation, either version 3 of the License, or (at your option) any later
⍝ version.
⍝
⍝ AHD is distributed in the hope that it will be useful, but WITHOUT ANY
⍝ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
⍝ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
⍝
⍝ You should have received a copy of the GNU General Public License along with
⍝ AHD. If not, see <https://www.gnu.org/licenses/>.
⍝ AHD - A HexDuper. Hex dump utility.
⊣ ⍎")COPY_ONCE fio.apl"
⊣ ⍎")COPY_ONCE logging.apl"
⍝ The path to the apl interpreter used to call this program.
ARGS∆APL_PATH←⍬
⍝ The name of this file/program.
ARGS∆PROGRAM_NAME←⍬
⍝ A vector of filenames given via the command line.
ARGS∆FILENAMES←⍬
⍝ Whether "++" was encountered, meaning all following option-like arguments are
⍝ to be treated as files.
ARGS∆END_OF_OPTIONS←0
⍝ If of tally > 0, the code generator should be used. The value of this variable
⍝ will be the name of the language to generate for as a character vector.
ARGS∆CODE_GENERATOR_LANGUAGE←⍬
⍝ For options with arguments. When set to 1, the next argument is evaluated as
⍝ the repsective option's argument.
ARGS∆EXPECT_CODE_GENERATOR_LANGUAGE←0
⍝ Displays a short help message.
∇ARGS∆DISPLAY_SHORT_HELP
⍞←"Try '",ARGS∆PROGRAM_NAME," -- +h' for more information\n"
⍞←"Try '",ARGS∆APL_PATH," --script ",ARGS∆PROGRAM_NAME," -- +h' for more information\n"
∇
⍝ Displays help information.
∇ARGS∆DISPLAY_HELP
⍞←"Usages:\n"
⍞←" ",ARGS∆PROGRAM_NAME," -- [options...] [FILE...]\n"
⍞←" ",ARGS∆APL_PATH," --script ",ARGS∆PROGRAM_NAME," -- [options...] [FILE...]\n"
⍞←"\n"
⍞←"Displays FILE contents (or input from stdin if no FILEs were specified) in\n"
⍞←"hexidecimal.\n"
⍞←"\n"
⍞←"Options:\n"
⍞←" +h, ++help display this help information.\n"
⍞←" +v, ++version display version.\n"
⍞←" +c, ++code-generator\n"
⍞←" Outputs code to bake the data into a program. Expects a language as an\n"
⍞←" argument. Supported Languages: c\n"
∇
⍝ Displays the version.
∇ARGS∆DISPLAY_VERSION
⍞←"ahd 0.1.5"
∇
⍝ Enables the code generator and tries to and set it to use the given language.
∇ARGS∆SET_CODE_GENERATOR_LANGUAGE LANGUAGE
→("c"≡LANGUAGE) ⍴ LKNOWN_LANGUAGE
ARGS∆DISPLAY_SHORT_HELP
PANIC "language '",LANGUAGE,"' does not support code generation"
LKNOWN_LANGUAGE:
ARGS∆CODE_GENERATOR_LANGUAGE←LANGUAGE
ARGS∆EXPECT_CODE_GENERATOR_LANGUAGE←0
∇
⍝ Parses a single character option (anything after a single "+") and updates
⍝ ARGS∆* accordingly.
∇ARGS∆PARSE_SHORT_OPTION OPTION
→({OPTION≡⍵}¨'h' 'v' 'c') / LHELP LVERSION LCODE_GENERATOR
LDEFAULT:
ARGS∆DISPLAY_SHORT_HELP
PANIC "unknown option '+",OPTION,"'"
LHELP: ARGS∆DISPLAY_HELP ◊ ⍎")OFF" ◊ →LSWITCH_END
LVERSION: ARGS∆DISPLAY_VERSION ◊ ⍎")OFF" ◊ →LSWITCH_END
LCODE_GENERATOR: ARGS∆EXPECT_CODE_GENERATOR_LANGUAGE←1 ◊ →LSWITCH_END
LSWITCH_END:
∇
⍝ Parses a command line argument and updates ARGS∆* accordingly.
∇ARGS∆PARSE_ARG ARGUMENT
⍝ If "++" was encountered, we just treat everything as a file.
→ARGS∆END_OF_OPTIONS ⍴ LFILE
⍝ Handles arguments to options with arguments.
→ARGS∆EXPECT_CODE_GENERATOR_LANGUAGE ⍴ LSET_CODE_GENERATOR_LANGUAGE
⍝ Handles "++".
→("++"≡ARGUMENT) ⍴ LDOUBLE_PLUS
⍝ Handles short options
→((1<≢ARGUMENT)∧('+'≡↑ARGUMENT)∧"++"≢2↑ARGUMENT) ⍴ LSHORT_OPTION
⍝ Test for known long options.
→({ARGUMENT≡⍵}¨ "++help" "++version" "++code-generator") / LHELP LVERSION LCODE_GENERATOR
⍝ Jumps to error print if ARGUMENT is an unknown long option.
→("++"≡2↑ARGUMENT) ⍴ LINVALID_LONG_OPTION
LDEFAULT:
LFILE:
⍝ Anything leftover is a file.
ARGS∆FILENAMES←ARGS∆FILENAMES,⊂ARGUMENT
→LSWITCH_END
LINVALID_LONG_OPTION:
ARGS∆DISPLAY_SHORT_HELP
PANIC "unknown option '",ARGUMENT,"'"
LSHORT_OPTION: ARGS∆PARSE_SHORT_OPTION¨ 1↓ARGUMENT ◊ →LSWITCH_END
LDOUBLE_PLUS: ARGS∆END_OF_OPTIONS←1 ◊ →LSWITCH_END
LSET_CODE_GENERATOR_LANGUAGE:
ARGS∆SET_CODE_GENERATOR_LANGUAGE ARGUMENT
→LSWITCH_END
LHELP: ARGS∆DISPLAY_HELP ◊ →LSWITCH_END
LVERSION: ARGS∆DISPLAY_VERSION ◊ →LSWITCH_END
LCODE_GENERATOR: ARGS∆EXPECT_CODE_GENERATOR_LANGUAGE←1 ◊ →LSWITCH_END
LSWITCH_END:
∇
⍝ Parses command line arguments and updates ARGS∆* accordingly.
∇ARGS∆PARSE_ARGS ARGUMENTS
⍝ ARGUMENTS looks like "apl --script <script> -- [user arguments...]".
ARGS∆APL_PATH←↑ARGUMENTS[1]
ARGS∆PROGRAM_NAME←↑ARGUMENTS[3]
→(4≥≢ARGUMENTS) ⍴ LNO_ARGUMENTS
ARGS∆PARSE_ARG¨ 4↓ARGUMENTS
LNO_ARGUMENTS:
⍝ Tests for any options with arguments that were not supplied an argument.
→(~ARGS∆EXPECT_CODE_GENERATOR_LANGUAGE) ⍴ LNO_INVALID_OPTIONS
ARGS∆DISPLAY_SHORT_HELP
PANIC "options '+c' and '++code-generator' expect an argument"
LNO_INVALID_OPTIONS:
∇
⍝ Converts a number into a uppercase-hexidecimal character vector.
⍝ →⍵ - the number.
⍝ →⍺ - the number of digits the resulting vector should have.
⍝ ←The character vector.
HEXIFY←{{⍵⌷"0123456789ABCDEF"}¨1+⍵⊤⍨⍺/16}
⍝ Returns whether the given byte is a displayable, non-control ASCII character.
⍝ →⍵ - a byte.
⍝ ←1 if the byte matches the criteria, else 0.
IS_DISPLAYABLE←{(126≥⍵)∧32≤⍵}
⍝ The bvte-value of a space character.
SPACE_BYTE←⎕UCS ' '
⍝ The number of bytes to print out per line.
HEXDUMP_BYTES_PER_LINE←16
⍝ The number of hexidecimal digits needed to represent a byte.
HEXDUMP_BYTE_DIGITS←2
⍝ Prints out a line of hexdump output of the byte vector.
⍝ →BYTE_VECTOR - the byte vector.
⍝ →OFFSET - the current line's byte offset.
∇OFFSET HEXDUMP_LINE BYTE_VECTOR
⍝ Offset.
⍞←7 HEXIFY OFFSET ◊ ⍞←":"
⍝ Bytes.
⊣ {⍞←HEXDUMP_BYTE_DIGITS HEXIFY ⍵ ⊣ ⍞←" "}¨ BYTE_VECTOR
⍝ Characters.
⍞←⎕UCS SPACE_BYTE/⍨(HEXDUMP_BYTE_DIGITS+1)×HEXDUMP_BYTES_PER_LINE-≢BYTE_VECTOR
⍞←" |"
⍞←⎕UCS {(SPACE_BYTE ⍵)⌷⍨1+ IS_DISPLAYABLE ⍵}¨ BYTE_VECTOR
⍞←"|\n"
∇
⍝ Prints out a hexdump.
⍝ →FILE_DESCRIPTOR - the file descriptor to generate the hexdump from.
∇HEXDUMP FILE_DESCRIPTOR; OFFSET;BYTE_VECTOR
OFFSET←0
LREAD_LOOP:
BYTE_VECTOR←HEXDUMP_BYTES_PER_LINE FIO∆FREAD_SIZED FILE_DESCRIPTOR
OFFSET HEXDUMP_LINE BYTE_VECTOR
OFFSET←OFFSET+≢BYTE_VECTOR
→(0≢FIO∆FEOF FILE_DESCRIPTOR) ⍴ LEND_READ_LOOP
→(0≢FIO∆FERROR FILE_DESCRIPTOR) ⍴ LEND_READ_LOOP
→LREAD_LOOP
LEND_READ_LOOP:
∇
⍝ Prints out a line of C code output of the byte vector.
⍝ →BYTE_VECTOR - the byte vector to print.
∇GENERATE_C_LINE BYTE_VECTOR
⍞←" "
⊣ {⍞←", " ⊣ ⍞←⍵ ⊣ ⍞←"0x"}¨ 2 HEXIFY¨ BYTE_VECTOR
⍞←"\n"
∇
⍝ Prints out C code.
⍝ →FILE_DESCRIPTOR - the file descriptor to generate C from.
∇GENERATE_C FILE_DESCRIPTOR; BYTE_VECTOR;BYTE_COUNT
BYTE_COUNT←0
⍞←"unsigned char data[] = {\n"
LREAD_LOOP:
BYTE_VECTOR←16 FIO∆FREAD_SIZED FILE_DESCRIPTOR
BYTE_COUNT←BYTE_COUNT+≢BYTE_VECTOR
GENERATE_C_LINE BYTE_VECTOR
→(0≢FIO∆FEOF FILE_DESCRIPTOR) ⍴ LEND_READ_LOOP
→(0≢FIO∆FERROR FILE_DESCRIPTOR) ⍴ LEND_READ_LOOP
→LREAD_LOOP
LEND_READ_LOOP:
⍞←"};\n"
⍞←"unsigned int data_length = " ◊ ⍞←BYTE_COUNT ◊ ⍞←";\n"
∇
⍝ Handles printing the output of the given file descriptor (hexdump, code,
⍝ etc..).
⍝ →FILE_DESCRIPTOR - the file descriptor to read from. Will not be closed.
∇HANDLE_FD FILE_DESCRIPTOR
⍝ If a code generator is selected, we use that, else we just do a hexdump.
→("c"≡ARGS∆CODE_GENERATOR_LANGUAGE) ⍴ LGENERATE_C
→(0≡≢ARGS∆CODE_GENERATOR_LANGUAGE) ⍴ LNO_SET_LANGUAGE
PANIC "HANDLE_FILE: unreachable"
LNO_SET_LANGUAGE:
HEXDUMP FILE_DESCRIPTOR
→LSWITCH_END
LGENERATE_C: GENERATE_C FILE_DESCRIPTOR ◊ →LSWITCH_END
LSWITCH_END:
LREAD_ERROR:
∇
⍝ Handles printing the output of the given file (hexdump, code, etc..).
⍝ →PATH - the path of the file.
⍝ →PRINT_PATH - whether to print the path along with the output. A scalar 1
⍝ means print, 0 means don't.
⍝ ←IGNORE - magic return value so the function works in defuns.
∇IGNORE←PRINT_PATH HANDLE_FILE PATH; DESCRIPTOR
→(~PRINT_PATH) ⍴ LDONT_PRINT_PATH
⍞←PATH,":\n"
LDONT_PRINT_PATH:
DESCRIPTOR←"r" FIO∆FOPEN PATH
→(0<DESCRIPTOR) ⍴ LNO_READ_ERROR
ERROR "failed to open file" ◊ →LREAD_ERROR
LNO_READ_ERROR:
HANDLE_FD DESCRIPTOR
LREAD_ERROR:
IGNORE←⍬
∇
∇MAIN
ARGS∆PARSE_ARGS ⎕ARG
→(0≡≢ARGS∆FILENAMES) ⍴ LREAD_STDIN
→(1≡≢ARGS∆FILENAMES) ⍴ LREAD_FILE
⍝ We only add the filenames when there are multiple fixes to output, to
⍝ differentiate them.
⊣ {1 HANDLE_FILE ⍵}¨ ARGS∆FILENAMES
→LSWITCH_END
LREAD_FILE: ⊣ 0 HANDLE_FILE ↑ARGS∆FILENAMES ◊ →LSWITCH_END
LREAD_STDIN: HANDLE_FD FIO∆STDIN ◊ →LSWITCH_END
LSWITCH_END:
∇
MAIN
)OFF