• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* hexdump.c - Dump file content in hexadecimal format to stdout
2  *
3  * Copyright 2021 Moritz Röhrich <moritz@ildefons.de>
4  *
5  * No standard
6  *
7  * TODO:
8  *  - Implement format strings (see man (1) hexdump)
9 
10 USE_HEXDUMP(NEWTOY(hexdump, "bcCdn#<0os#<0vx[!bcCdox]", TOYFLAG_USR|TOYFLAG_BIN))
11 USE_HD(OLDTOY(hd, hexdump, TOYFLAG_USR|TOYFLAG_BIN))
12 
13 config HEXDUMP
14   bool "hexdump"
15   default n
16   help
17     usage: hexdump [-bcCdovx] [-n LEN] [-s SKIP] [FILE...]
18 
19     Dump file(s) in hexadecimal format.
20 
21     -n LEN	Show LEN bytes of output
22     -s SKIP	Skip bytes of input
23     -v	Verbose (don't combine identical lines)
24 
25     Display type:
26     -b One byte octal   -c One byte character -C Canonical (hex + ASCII)
27     -d Two byte decimal -o Two byte octal     -x Two byte hexadecimal (default)
28 
29 config HD
30   bool "hd"
31   default HEXDUMP
32   help
33     usage: hd [FILE...]
34 
35     Display file(s) in cannonical hex+ASCII format.
36 */
37 
38 #define FOR_hexdump
39 #include "toys.h"
40 
GLOBALS(long s,n;long long len,pos,ppos;const char * fmt;unsigned int fn,bc;char linebuf[16];)41 GLOBALS(
42     long s, n;
43 
44     long long len, pos, ppos;
45     const char *fmt;
46     unsigned int fn, bc;  // file number and byte count
47     char linebuf[16];  // line buffer - serves double duty for sqeezing repeat
48                        // lines and for accumulating full lines accross file
49                        // boundaries if necessesary.
50 )
51 
52 const char *make_printable(unsigned char byte) {
53   switch (byte) {
54     case '\0': return "\\0";
55     case '\a': return "\\a";
56     case '\b': return "\\b";
57     case '\t': return "\\t";
58     case '\n': return "\\n";
59     case '\v': return "\\v";
60     case '\f': return "\\f";
61     default: return "??";  // for all unprintable bytes
62   }
63 }
64 
do_hexdump(int fd,char * name)65 void do_hexdump(int fd, char *name)
66 {
67   unsigned short block, adv, i;
68   int sl, fs;  // skip line, file size
69 
70   TT.fn++;  // keep track of how many files have been printed.
71   // skipp ahead, if necessary skip entire files:
72   if (FLAG(s) && (TT.s-TT.pos>0)) {
73     fs = xlseek(fd, 0L, SEEK_END);
74 
75     if (fs < TT.s) {
76       TT.pos += fs;
77       TT.ppos += fs;
78     } else {
79       xlseek(fd, TT.s-TT.pos, SEEK_SET);
80       TT.ppos = TT.s;
81       TT.pos = TT.s;
82     }
83   }
84 
85   for (sl = 0;
86        0 < (TT.len = readall(fd, toybuf,
87                              (TT.n && TT.s+TT.n-TT.pos<16-(TT.bc%16))
88                                 ? TT.s+TT.n-TT.pos : 16-(TT.bc%16)));
89        TT.pos += TT.len) {
90     // This block compares the data read from file to the last line printed.
91     // If they don't match a new line is printed, else the line is skipped.
92     // If a * has already been printed to indicate a skipped line, printing the
93     // * is also skipped.
94     for (i = 0; i < 16 && i < TT.len; i++){
95       if (FLAG(v) || TT.len < 16 || toybuf[i] != TT.linebuf[i]) goto newline;
96     }
97     if (sl == 0) {
98       printf("*\n");
99       sl = 1;
100     }
101     TT.ppos += TT.len;
102     continue;
103 
104 newline:
105     strncpy(TT.linebuf+(TT.bc%16), toybuf, TT.len);
106     TT.bc = TT.bc % 16 + TT.len;
107     sl = 0;
108     if (TT.pos + TT.bc == TT.s+TT.n || TT.fn == toys.optc || TT.bc == 16) {
109       if (!FLAG(C) && !FLAG(c)) {
110         printf("%07llx", TT.ppos);
111         adv = FLAG(b) ? 1 : 2;
112         for (i = 0; i < TT.bc; i += adv) {
113           block = (FLAG(b) || i == TT.bc-1)
114             ? TT.linebuf[i] : (TT.linebuf[i] | TT.linebuf[i+1] << 8);
115           printf(TT.fmt, block);
116         }
117       } else if (FLAG(C)) {
118         printf("%08llx", TT.ppos);
119         for (i = 0; i < 16; i++) {
120           if (!(i % 8)) putchar(' ');
121           if (i < TT.bc) printf(" %02x", TT.linebuf[i]);
122           else printf("   ");
123         }
124         printf("  |");
125         for (i = 0; i < TT.bc; i++) {
126           if (TT.linebuf[i] < ' ' || TT.linebuf[i] > '~') putchar('.');
127           else putchar(TT.linebuf[i]);
128         }
129         putchar('|');
130       } else {
131         printf("%07llx", TT.ppos);
132         for (i = 0; i < TT.bc; i++) {
133           if (TT.linebuf[i] >= ' ' && TT.linebuf[i] <= '~')
134             printf("%4c", TT.linebuf[i]);
135           else printf("%4s", make_printable(TT.linebuf[i]));
136         }
137       }
138       putchar('\n');
139       TT.ppos += TT.bc;
140     }
141   }
142 
143   if (TT.len < 0) perror_exit("read");
144 }
145 
hexdump_main(void)146 void hexdump_main(void)
147 {
148   if FLAG(b) TT.fmt = " %03o";
149   else if FLAG(d) TT.fmt = " %05d";
150   else if FLAG(o) TT.fmt = " %06o";
151   else TT.fmt = " %04x";
152 
153   loopfiles(toys.optargs, do_hexdump);
154   FLAG(C) ? printf("%08llx\n", TT.pos) : printf("%07llx\n", TT.pos);
155 }
156