generated from 8dcc/c-template
-
Notifications
You must be signed in to change notification settings - Fork 2
/
args.c
331 lines (293 loc) · 12.1 KB
/
args.c
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
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
#include <stddef.h>
#include <stdbool.h>
#include <string.h>
#include <stdlib.h>
#include "include/args.h"
#include "include/util.h"
/*----------------------------------------------------------------------------*/
/* Program mode. Determines how the bytes will be displayed. */
enum EProgramMode g_mode = MODE_ASCII;
/* Start and end offsets for reading the input file. Zero means ignore. */
size_t g_offset_start = 0;
size_t g_offset_end = 0;
/* Block size used in some modes like MODE_ENTROPY. */
uint32_t g_block_size = DEFAULT_BLOCK_SIZE;
/* Width in pixels of the output image (before applying the zoom) */
uint32_t g_output_width = DEFAULT_OUTPUT_WIDTH;
/* Width and height of each "pixel" when drawn in the actual PNG image */
uint32_t g_output_zoom = DEFAULT_OUTPUT_ZOOM;
/* Side of each square used when transforming the generated image. Values lower
* than two are ignored. */
uint32_t g_transform_squares_side = 0;
/*----------------------------------------------------------------------------*/
/* Mode names and descriptions used when parsing the program arguments */
static struct {
const char* arg;
const char* desc;
} g_mode_names[] = {
[MODE_GRAYSCALE] = {
.arg = "grayscale",
.desc =
" The brightness of each pixel represents the value of each\n"
" sample (00..FF).",
},
[MODE_ASCII] = {
.arg = "ascii",
.desc =
" The color of each pixel represents the \"printability\" of\n"
" each sample in a linear way. Black represents a null byte\n"
" (00), white represents a set byte (FF), blue represents\n"
" printable characters and red represents any other value.",
},
[MODE_ENTROPY] = {
.arg = "entropy",
.desc =
" The intensity of each pixel represents its entropy (i.e.\n"
" its \"predictability\"). This is useful for distinguishing\n"
" compressed/encrypted from non-compressed chunks.",
},
[MODE_HISTOGRAM] = {
.arg = "histogram",
.desc =
" Each row represents a byte (00..FF), and the width of each\n"
" line represents the frequency of that byte relative to the\n"
" most frequent one.",
},
[MODE_BIGRAMS] = {
.arg = "bigrams",
.desc =
" The coordinates of each point are determined by a pair of\n"
" samples in the input. This can be used to identify\n"
" patterns of different file formats.",
},
[MODE_DOTPLOT] = {
.arg = "dotplot",
.desc =
" Measure self-similarity. A point (X,Y) in the graph shows\n"
" if the X-th sample matches the Y-th sample.",
},
};
/*----------------------------------------------------------------------------*/
void parse_args(int argc, char** argv) {
enum EArgError arg_error = ARG_ERR_NONE;
if (argc < 3) {
/* Too few arguments, but one of them was --help */
if (argc > 1 && (strcmp(argv[argc - 1], "--help") == 0 ||
strcmp(argv[argc - 1], "-h") == 0)) {
arg_error = ARG_ERR_HELP;
goto check_arg_err;
}
log_err("Not enough arguments.");
arg_error = ARG_ERR_USAGE;
goto check_arg_err;
}
/* Parse arguments */
for (int i = 1; i < argc - 2; i++) {
if (argv[i][0] != '-' || argv[i][1] != '-') {
log_err("Expected option argument, instead found: \"%s\"", argv[i]);
arg_error = ARG_ERR_USAGE;
goto check_arg_err;
}
const char* option = &argv[i][2];
if (strcmp(option, "help") == 0) {
arg_error = ARG_ERR_HELP;
goto check_arg_err;
} else if (strcmp(option, "mode") == 0) {
i++;
if (i >= argc - 2) {
log_err("Not enough arguments for option: \"%s\".", option);
arg_error = ARG_ERR_EXIT;
goto check_arg_err;
}
bool got_match = false;
for (size_t mode = 0; mode < LENGTH(g_mode_names); mode++) {
if (strcmp(argv[i], g_mode_names[mode].arg) == 0) {
got_match = true;
g_mode = mode;
break;
}
}
if (!got_match) {
log_err("Unknown mode: \"%s\".\n", argv[i]);
arg_error = ARG_ERR_EXIT;
goto check_arg_err;
}
} else if (strcmp(option, "offset-start") == 0) {
i++;
if (i >= argc - 2) {
log_err("Not enough arguments for option: \"%s\".", option);
arg_error = ARG_ERR_EXIT;
goto check_arg_err;
}
if (sscanf(argv[i], "%zx", &g_offset_start) != 1) {
log_err("Invalid format for start offset. Example: \"e1c5\"");
arg_error = ARG_ERR_EXIT;
goto check_arg_err;
}
} else if (strcmp(option, "offset-end") == 0) {
i++;
if (i >= argc - 2) {
log_err("Not enough arguments for option: \"%s\".", option);
arg_error = ARG_ERR_EXIT;
goto check_arg_err;
}
if (sscanf(argv[i], "%zx", &g_offset_end) != 1) {
log_err("Invalid format for end offset. Example: \"e1c5\"");
arg_error = ARG_ERR_EXIT;
goto check_arg_err;
}
} else if (strcmp(option, "zoom") == 0) {
i++;
if (i >= argc - 2) {
log_err("Not enough arguments for option: \"%s\".", option);
arg_error = ARG_ERR_EXIT;
goto check_arg_err;
}
int signed_zoom;
if (sscanf(argv[i], "%d", &signed_zoom) != 1 || signed_zoom <= 0) {
log_err(
"The zoom factor must be an integer greater than zero.");
arg_error = ARG_ERR_EXIT;
goto check_arg_err;
}
g_output_zoom = signed_zoom;
} else if (strcmp(option, "width") == 0) {
i++;
if (i >= argc - 2) {
log_err("Not enough arguments for option: \"%s\".", option);
arg_error = ARG_ERR_EXIT;
goto check_arg_err;
}
int signed_width;
if (sscanf(argv[i], "%d", &signed_width) != 1 ||
signed_width <= 0) {
log_err("The width must be an integer greater than zero.");
arg_error = ARG_ERR_EXIT;
goto check_arg_err;
}
g_output_width = signed_width;
} else if (strcmp(option, "block-size") == 0) {
i++;
if (i >= argc - 2) {
log_err("Not enough arguments for option: \"%s\".", option);
arg_error = ARG_ERR_EXIT;
goto check_arg_err;
}
int signed_size;
if (sscanf(argv[i], "%d", &signed_size) != 1 || signed_size <= 0) {
log_err("The block size must be an integer greater than zero.");
arg_error = ARG_ERR_EXIT;
goto check_arg_err;
}
g_block_size = signed_size;
} else if (strcmp(option, "transform-squares") == 0) {
i++;
if (i >= argc - 2) {
log_err("Not enough arguments for option: \"%s\".", option);
arg_error = ARG_ERR_EXIT;
goto check_arg_err;
}
int signed_side;
if (sscanf(argv[i], "%d", &signed_side) != 1 || signed_side <= 1) {
log_err("The square side must be an integer greater than one.");
arg_error = ARG_ERR_EXIT;
goto check_arg_err;
}
g_transform_squares_side = signed_side;
} else {
log_err("Invalid option: \"%s\".", option);
arg_error = ARG_ERR_USAGE;
goto check_arg_err;
}
}
/* Ensure that there are no invalid argument combinations */
if (g_offset_end != 0 && g_offset_end <= g_offset_start) {
log_err("The end offset (%zx) must be bigger than the start offset "
"(%zx).",
g_offset_end, g_offset_start);
arg_error = ARG_ERR_EXIT;
goto check_arg_err;
}
/* Also check for ignored argument combinations. These just cause a
* warning. */
if (g_output_width != DEFAULT_OUTPUT_WIDTH &&
(g_mode == MODE_BIGRAMS || g_mode == MODE_DOTPLOT)) {
log_wrn("The output width will be overwritten by the current mode "
"(%s).",
g_mode_names[g_mode].arg);
}
if (g_block_size != DEFAULT_BLOCK_SIZE && g_mode != MODE_ENTROPY) {
log_wrn("The current mode (%s) isn't affected by the block size.",
g_mode_names[g_mode].arg);
}
if (g_block_size <= 1 && g_mode == MODE_ENTROPY) {
log_wrn("The block size (%d) is too small for the current mode (%s). "
"Overwritting to %d bytes.",
g_block_size, g_mode_names[g_mode].arg, DEFAULT_BLOCK_SIZE);
g_block_size = DEFAULT_BLOCK_SIZE;
}
if (g_transform_squares_side > 1 &&
(g_mode == MODE_HISTOGRAM || g_mode == MODE_BIGRAMS ||
g_mode == MODE_DOTPLOT)) {
log_wrn("The \"squares\" transformation is not recommended for the "
"current mode (%s).",
g_mode_names[g_mode].arg);
}
check_arg_err:
if (arg_error >= ARG_ERR_EXIT) {
if (arg_error >= ARG_ERR_USAGE) {
fprintf(stderr,
"Usage:\n"
" %s [OPTION...] INPUT OUTPUT.png\n",
argv[0]);
if (arg_error == ARG_ERR_HELP) {
fprintf(
stderr,
"\nPossible options:\n"
" --help\n"
" Show this help and exit the program.\n\n"
" --offset-start OFFSET\n"
" Start processing the file from OFFSET. Specified in "
"hexadecimal\n"
" format, without any prefix.\n\n"
" --offset-end OFFSET\n"
" Stop processing the file at OFFSET. Specified in "
"hexadecimal\n"
" format, without any prefix. Zero means the end of the "
"file.\n\n"
" --zoom FACTOR\n"
" Scale each pixel by FACTOR.\n\n"
" --width WIDTH\n"
" Set the default width to WIDTH. This parameter is "
"ignored in\n"
" some modes (bigrams, dotplot, etc.). This width "
"indicates the\n"
" sample number in each row before applying the "
"zoom.\n\n"
" --sample-step BYTES\n"
" Only read one out of every BYTES from the input. "
"Specified in\n"
" decimal format.\n\n"
" --block-size BYTES\n"
" Set the size for some block-specific modes like "
"entropy.\n\n"
" --transform-squares SIDE\n"
" After generating the image, group its pixels into "
"squares of\n"
" side SIDE. If the image dimensions are not divisible "
"by SIDE,\n"
" they will be increased. This option is useful with "
"the entropy\n"
" mode.\n\n"
" --mode MODE\n"
" Set the current mode to MODE. Available modes:\n");
for (size_t mode = 0; mode < LENGTH(g_mode_names); mode++)
fprintf(stderr,
" %s:\n"
"%s\n",
g_mode_names[mode].arg, g_mode_names[mode].desc);
}
}
exit(1);
}
}