xref: /aosp_15_r20/external/sg3_utils/utils/hxascdmp.c (revision 44704f698541f6367e81f991ef8bb54ccbf3fc18)
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