1 /*
2 * Copyright (c) 2004-2019 Douglas Gilbert.
3 * All rights reserved.
4 * Use of this source code is governed by a BSD-style
5 * license that can be found in the BSD_LICENSE file.
6 *
7 * SPDX-License-Identifier: BSD-2-Clause
8 */
9
10 #include <stdlib.h>
11 #include <stdio.h>
12 #include <stdint.h>
13 #include <unistd.h>
14 #include <sys/types.h>
15 #include <sys/stat.h>
16 #include <fcntl.h>
17 #include <string.h>
18 #include <ctype.h>
19 #include <errno.h>
20 #define __STDC_FORMAT_MACROS 1
21 #include <inttypes.h>
22
23 #define DEF_BYTES_PER_LINE 16
24
25 static int bytes_per_line = DEF_BYTES_PER_LINE;
26
27 static const char * version_str = "1.11 20190527";
28
29 #define CHARS_PER_HEX_BYTE 3
30 #define BINARY_START_COL 6
31 #define MAX_LINE_LENGTH 257
32
33
34 #ifdef SG_LIB_MINGW
35 /* Non Unix OSes distinguish between text and binary files.
36 Set text mode on fd. Does nothing in Unix. Returns negative number on
37 failure. */
38 int
sg_set_text_mode(int fd)39 sg_set_text_mode(int fd)
40 {
41 return setmode(fd, O_TEXT);
42 }
43
44 /* Set binary mode on fd. Does nothing in Unix. Returns negative number on
45 failure. */
46 int
sg_set_binary_mode(int fd)47 sg_set_binary_mode(int fd)
48 {
49 return setmode(fd, O_BINARY);
50 }
51
52 #else
53 /* For Unix the following functions are dummies. */
54 int
sg_set_text_mode(int fd)55 sg_set_text_mode(int fd)
56 {
57 return fd; /* fd should be >= 0 */
58 }
59
60 int
sg_set_binary_mode(int fd)61 sg_set_binary_mode(int fd)
62 {
63 return fd;
64 }
65 #endif
66
67 /* Returns the number of times 'ch' is found in string 's' given the
68 * string's length. */
69 static int
num_chs_in_str(const char * s,int slen,int ch)70 num_chs_in_str(const char * s, int slen, int ch)
71 {
72 int res = 0;
73
74 while (--slen >= 0) {
75 if (ch == s[slen])
76 ++res;
77 }
78 return res;
79 }
80
81 /* If the number in 'buf' can be decoded or the multiplier is unknown
82 * then -1LL is returned. Accepts a hex prefix (0x or 0X) or a decimal
83 * multiplier suffix (as per GNU's dd (since 2002: SI and IEC 60027-2)).
84 * Main (SI) multipliers supported: K, M, G, T, P. Ignore leading spaces
85 * and tabs; accept comma, hyphen, space, tab and hash as terminator. */
86 int64_t
sg_get_llnum(const char * buf)87 sg_get_llnum(const char * buf)
88 {
89 int res, len, n;
90 int64_t num, ll;
91 uint64_t unum;
92 char * cp;
93 const char * b;
94 char c = 'c';
95 char c2 = '\0'; /* keep static checker happy */
96 char c3 = '\0'; /* keep static checker happy */
97 char lb[32];
98
99 if ((NULL == buf) || ('\0' == buf[0]))
100 return -1LL;
101 len = strlen(buf);
102 n = strspn(buf, " \t");
103 if (n > 0) {
104 if (n == len)
105 return -1LL;
106 buf += n;
107 len -= n;
108 }
109 /* following hack to keep C++ happy */
110 cp = strpbrk((char *)buf, " \t,#-");
111 if (cp) {
112 len = cp - buf;
113 n = (int)sizeof(lb) - 1;
114 len = (len < n) ? len : n;
115 memcpy(lb, buf, len);
116 lb[len] = '\0';
117 b = lb;
118 } else
119 b = buf;
120 if (('0' == b[0]) && (('x' == b[1]) || ('X' == b[1]))) {
121 res = sscanf(b + 2, "%" SCNx64 , &unum);
122 num = unum;
123 } else if ('H' == toupper((int)b[len - 1])) {
124 res = sscanf(b, "%" SCNx64 , &unum);
125 num = unum;
126 } else
127 res = sscanf(b, "%" SCNd64 "%c%c%c", &num, &c, &c2, &c3);
128 if (res < 1)
129 return -1LL;
130 else if (1 == res)
131 return num;
132 else {
133 if (res > 2)
134 c2 = toupper((int)c2);
135 if (res > 3)
136 c3 = toupper((int)c3);
137 switch (toupper((int)c)) {
138 case 'C':
139 return num;
140 case 'W':
141 return num * 2;
142 case 'B':
143 return num * 512;
144 case 'K':
145 if (2 == res)
146 return num * 1024;
147 if (('B' == c2) || ('D' == c2))
148 return num * 1000;
149 if (('I' == c2) && (4 == res) && ('B' == c3))
150 return num * 1024;
151 return -1LL;
152 case 'M':
153 if (2 == res)
154 return num * 1048576;
155 if (('B' == c2) || ('D' == c2))
156 return num * 1000000;
157 if (('I' == c2) && (4 == res) && ('B' == c3))
158 return num * 1048576;
159 return -1LL;
160 case 'G':
161 if (2 == res)
162 return num * 1073741824;
163 if (('B' == c2) || ('D' == c2))
164 return num * 1000000000;
165 if (('I' == c2) && (4 == res) && ('B' == c3))
166 return num * 1073741824;
167 return -1LL;
168 case 'T':
169 if (2 == res)
170 return num * 1099511627776LL;
171 if (('B' == c2) || ('D' == c2))
172 return num * 1000000000000LL;
173 if (('I' == c2) && (4 == res) && ('B' == c3))
174 return num * 1099511627776LL;
175 return -1LL;
176 case 'P':
177 if (2 == res)
178 return num * 1099511627776LL * 1024;
179 if (('B' == c2) || ('D' == c2))
180 return num * 1000000000000LL * 1000;
181 if (('I' == c2) && (4 == res) && ('B' == c3))
182 return num * 1099511627776LL * 1024;
183 return -1LL;
184 case 'X':
185 cp = (char *)strchr(b, 'x');
186 if (NULL == cp)
187 cp = (char *)strchr(b, 'X');
188 if (cp) {
189 ll = sg_get_llnum(cp + 1);
190 if (-1LL != ll)
191 return num * ll;
192 }
193 return -1LL;
194 default:
195 fprintf(stderr, "unrecognized multiplier\n");
196 return -1LL;
197 }
198 }
199 }
200
201 static void
dStrHex(const char * str,int len,long start,int noAddr)202 dStrHex(const char* str, int len, long start, int noAddr)
203 {
204 const char* p = str;
205 unsigned char c;
206 char buff[MAX_LINE_LENGTH];
207 long a = start;
208 int bpstart, cpstart;
209 int j, k, line_length, nl, cpos, bpos, midline_space;
210
211 if (noAddr) {
212 bpstart = 0;
213 cpstart = ((CHARS_PER_HEX_BYTE * bytes_per_line) + 1) + 5;
214 } else {
215 bpstart = BINARY_START_COL;
216 cpstart = BINARY_START_COL +
217 ((CHARS_PER_HEX_BYTE * bytes_per_line) + 1) + 5;
218 }
219 cpos = cpstart;
220 bpos = bpstart;
221 midline_space = ((bytes_per_line + 1) / 2);
222
223 if (len <= 0)
224 return;
225 line_length = BINARY_START_COL +
226 (bytes_per_line * (1 + CHARS_PER_HEX_BYTE)) + 7;
227 if (line_length >= MAX_LINE_LENGTH) {
228 fprintf(stderr, "bytes_per_line causes maximum line length of %d "
229 "to be exceeded\n", MAX_LINE_LENGTH);
230 return;
231 }
232 memset(buff, ' ', line_length);
233 buff[line_length] = '\0';
234 if (0 == noAddr) {
235 k = sprintf(buff + 1, "%.2lx", a);
236 buff[k + 1] = ' ';
237 }
238
239 for(j = 0; j < len; j++) {
240 nl = (0 == (j % bytes_per_line));
241 if ((j > 0) && nl) {
242 printf("%s\n", buff);
243 bpos = bpstart;
244 cpos = cpstart;
245 a += bytes_per_line;
246 memset(buff,' ', line_length);
247 if (0 == noAddr) {
248 k = sprintf(buff + 1, "%.2lx", a);
249 buff[k + 1] = ' ';
250 }
251 }
252 c = *p++;
253 bpos += (nl && noAddr) ? 0 : CHARS_PER_HEX_BYTE;
254 if ((bytes_per_line > 4) && ((j % bytes_per_line) == midline_space))
255 bpos++;
256 sprintf(&buff[bpos], "%.2x", (int)(unsigned char)c);
257 buff[bpos + 2] = ' ';
258 if ((c < ' ') || (c >= 0x7f))
259 c='.';
260 buff[cpos++] = c;
261 }
262 if (cpos > cpstart)
263 printf("%s\n", buff);
264 }
265
266 static void
dStrHexOnly(const char * str,int len,long start,int noAddr)267 dStrHexOnly(const char* str, int len, long start, int noAddr)
268 {
269 const char* p = str;
270 unsigned char c;
271 char buff[MAX_LINE_LENGTH];
272 long a = start;
273 int bpstart, bpos, nl;
274 int midline_space = ((bytes_per_line + 1) / 2);
275 int j, k, line_length;
276
277 if (len <= 0)
278 return;
279 bpstart = (noAddr ? 0 : BINARY_START_COL);
280 bpos = bpstart;
281 line_length = (noAddr ? 0 : BINARY_START_COL) +
282 (bytes_per_line * CHARS_PER_HEX_BYTE) + 4;
283 if (line_length >= MAX_LINE_LENGTH) {
284 fprintf(stderr, "bytes_per_line causes maximum line length of %d "
285 "to be exceeded\n", MAX_LINE_LENGTH);
286 return;
287 }
288 memset(buff, ' ', line_length);
289 buff[line_length] = '\0';
290 if (0 == noAddr) {
291 k = sprintf(buff + 1, "%.2lx", a);
292 buff[k + 1] = ' ';
293 }
294
295 for(j = 0; j < len; j++) {
296 nl = (0 == (j % bytes_per_line));
297 if ((j > 0) && nl) {
298 printf("%s\n", buff);
299 bpos = bpstart;
300 a += bytes_per_line;
301 memset(buff,' ', line_length);
302 if (0 == noAddr) {
303 k = sprintf(buff + 1, "%.2lx", a);
304 buff[k + 1] = ' ';
305 }
306 }
307 c = *p++;
308 bpos += (nl && noAddr) ? 0 : CHARS_PER_HEX_BYTE;
309 if ((bytes_per_line > 4) && ((j % bytes_per_line) == midline_space))
310 bpos++;
311 sprintf(&buff[bpos], "%.2x", (int)(unsigned char)c);
312 buff[bpos + 2] = ' ';
313 }
314 if (bpos > bpstart)
315 printf("%s\n", buff);
316 }
317
318 static void
usage()319 usage()
320 {
321 fprintf(stderr, "Usage: hxascdmp [-1] [-2] [-b=<n>] [-h] [-H] [-N] "
322 "[-o=<off>] [-q]\n"
323 " [-V] [-?] [<file>+]\n");
324 fprintf(stderr, " where:\n");
325 fprintf(stderr, " -1 print first byte in hex, prepend '0x' "
326 "if '-H' given\n");
327 fprintf(stderr, " -2 like '-1' but print first two bytes\n");
328 fprintf(stderr, " -b=<n> bytes per line to display "
329 "(def: 16)\n");
330 fprintf(stderr, " -h print this usage message\n");
331 fprintf(stderr, " -H print hex only (i.e. no ASCII "
332 "to right)\n");
333 fprintf(stderr, " -N no address, start in first column\n");
334 fprintf(stderr, " -o=<off> start decoding at byte <off>. Suffix "
335 "multipliers allowed\n");
336 fprintf(stderr, " -q quiet: suppress output of header "
337 "info\n");
338 fprintf(stderr, " -V print version string then exits\n");
339 fprintf(stderr, " -? print this usage message\n");
340 fprintf(stderr, " <file>+ reads file(s) and outputs each "
341 "as hex ASCII\n");
342 fprintf(stderr, " if no <file> then reads stdin\n\n");
343 fprintf(stderr, "Sends hex ASCII dump of stdin/file to stdout\n");
344 }
345
346 int
main(int argc,const char ** argv)347 main(int argc, const char ** argv)
348 {
349 char buff[8192];
350 int num = 8192;
351 long start = 0;
352 int64_t offset = 0;
353 int res, k, u, len, n;
354 int inFile = STDIN_FILENO;
355 int doHelp = 0;
356 int doHex = 0;
357 int noAddr = 0;
358 int doVersion = 0;
359 int hasFilename = 0;
360 int quiet = 0;
361 int print1 = 0;
362 int print2 = 0;
363 int ret = 0;
364 const char * cp;
365
366 for (k = 1; k < argc; k++) {
367 cp = argv[k];
368 len = strlen(cp);
369 if (0 == strncmp("-b=", cp, 3)) {
370 res = sscanf(cp + 3, "%d", &u);
371 if ((1 != res) || (u < 1)) {
372 fprintf(stderr, "Bad value after '-b=' option\n");
373 usage();
374 return 1;
375 }
376 bytes_per_line = u;
377 } else if (0 == strncmp("-o=", cp, 3)) {
378 int64_t off = sg_get_llnum(cp + 3);
379
380 if (off == -1) {
381 fprintf(stderr, "Bad value after '-o=' option\n");
382 usage();
383 return 1;
384 }
385 offset = off;
386 } else if ((len > 1) && ('-' == cp[0]) && ('-' != cp[1])) {
387 res = 0;
388 n = num_chs_in_str(cp + 1, len - 1, '1');
389 print1 += n;
390 res += n;
391 n = num_chs_in_str(cp + 1, len - 1, '2');
392 print2 += n;
393 res += n;
394 n = num_chs_in_str(cp + 1, len - 1, 'h');
395 doHelp += n;
396 res += n;
397 n = num_chs_in_str(cp + 1, len - 1, 'H');
398 doHex += n;
399 res += n;
400 n = num_chs_in_str(cp + 1, len - 1, 'N');
401 noAddr += n;
402 res += n;
403 n = num_chs_in_str(cp + 1, len - 1, 'q');
404 quiet += n;
405 res += n;
406 n = num_chs_in_str(cp + 1, len - 1, 'V');
407 doVersion += n;
408 res += n;
409 n = num_chs_in_str(cp + 1, len - 1, '?');
410 doHelp += n;
411 res += n;
412 if (0 == res) {
413 fprintf(stderr, "No option recognized in str: %s\n", cp);
414 usage();
415 return 1;
416 }
417 } else if (0 == strcmp("-?", argv[k]))
418 ++doHelp;
419 else if (*argv[k] == '-') {
420 fprintf(stderr, "unknown switch: %s\n", argv[k]);
421 usage();
422 return 1;
423 } else {
424 hasFilename = 1;
425 break;
426 }
427 if (print2)
428 print1 += print2 + print2;
429 }
430 if (doVersion) {
431 printf("%s\n", version_str);
432 return 0;
433 }
434 if (doHelp) {
435 usage();
436 return 0;
437 }
438
439 /* Make sure num to fetch is integral multiple of bytes_per_line */
440 if (0 != (num % bytes_per_line))
441 num = (num / bytes_per_line) * bytes_per_line;
442
443 if (hasFilename) {
444 for ( ; k < argc; k++)
445 {
446 inFile = open(argv[k], O_RDONLY);
447 if (inFile < 0) {
448 fprintf(stderr, "Couldn't open file: %s\n", argv[k]);
449 ret = 1;
450 } else {
451 sg_set_binary_mode(inFile);
452 if (offset > 0) {
453 int err;
454 int64_t off_res;
455
456 off_res = lseek(inFile, offset, SEEK_SET);
457 if (off_res < 0) {
458 err = errno;
459 fprintf(stderr, "failed moving filepos: wanted=%"
460 PRId64 " [0x%" PRIx64 "]\nlseek error: %s\n",
461 offset, offset, strerror(err));
462 goto fini1;
463 }
464 start = offset;
465 } else
466 start = 0;
467 if (! (doHex || quiet || print1))
468 printf("ASCII hex dump of file: %s\n", argv[k]);
469 while ((res = read(inFile, buff, num)) > 0) {
470 if (print1) {
471 if (1 == print1) {
472 if (doHex)
473 printf("0x%02x\n", (uint8_t)(buff[0]));
474 else
475 printf("%02x\n", (uint8_t)(buff[0]));
476 } else {
477 uint16_t us;
478
479 memcpy(&us, buff, 2);
480 if (doHex)
481 printf("0x%04x\n", us);
482 else
483 printf("%04x\n", us);
484 }
485 break;
486 }
487 if (doHex)
488 dStrHexOnly(buff, res, start, noAddr);
489 else
490 dStrHex(buff, res, start, noAddr);
491 start += (long)res;
492 }
493 }
494 fini1:
495 close(inFile);
496 }
497 } else {
498 sg_set_binary_mode(inFile);
499 if (offset > 0) {
500 start = offset;
501 do { /* eat up offset bytes */
502 if ((res = read(inFile, buff,
503 (num > offset ? offset : num))) > 0)
504 offset -= res;
505 else {
506 fprintf(stderr, "offset read() error: %s\n",
507 strerror(errno));
508 break;
509 }
510 } while (offset > 0);
511 }
512 while ((res = read(inFile, buff, num)) > 0) {
513 if (doHex)
514 dStrHexOnly(buff, res, start, noAddr);
515 else
516 dStrHex(buff, res, start, noAddr);
517 start += (long)res;
518 }
519 }
520 return ret;
521 }
522