xref: /third_party/toybox/toys/pending/hexdump.c (revision 0f66f451)
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
10USE_HEXDUMP(NEWTOY(hexdump, "bcCdn#<0os#<0vx[!bcCdox]", TOYFLAG_USR|TOYFLAG_BIN))
11USE_HD(OLDTOY(hd, hexdump, TOYFLAG_USR|TOYFLAG_BIN))
12
13config 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
29config 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
41GLOBALS(
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
52const 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
65void 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
104newline:
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
146void 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