10f66f451Sopenharmony_ci/* hexdump.c - Dump file content in hexadecimal format to stdout
20f66f451Sopenharmony_ci *
30f66f451Sopenharmony_ci * Copyright 2021 Moritz Röhrich <moritz@ildefons.de>
40f66f451Sopenharmony_ci *
50f66f451Sopenharmony_ci * No standard
60f66f451Sopenharmony_ci *
70f66f451Sopenharmony_ci * TODO:
80f66f451Sopenharmony_ci *  - Implement format strings (see man (1) hexdump)
90f66f451Sopenharmony_ci
100f66f451Sopenharmony_ciUSE_HEXDUMP(NEWTOY(hexdump, "bcCdn#<0os#<0vx[!bcCdox]", TOYFLAG_USR|TOYFLAG_BIN))
110f66f451Sopenharmony_ciUSE_HD(OLDTOY(hd, hexdump, TOYFLAG_USR|TOYFLAG_BIN))
120f66f451Sopenharmony_ci
130f66f451Sopenharmony_ciconfig HEXDUMP
140f66f451Sopenharmony_ci  bool "hexdump"
150f66f451Sopenharmony_ci  default n
160f66f451Sopenharmony_ci  help
170f66f451Sopenharmony_ci    usage: hexdump [-bcCdovx] [-n LEN] [-s SKIP] [FILE...]
180f66f451Sopenharmony_ci
190f66f451Sopenharmony_ci    Dump file(s) in hexadecimal format.
200f66f451Sopenharmony_ci
210f66f451Sopenharmony_ci    -n LEN	Show LEN bytes of output
220f66f451Sopenharmony_ci    -s SKIP	Skip bytes of input
230f66f451Sopenharmony_ci    -v	Verbose (don't combine identical lines)
240f66f451Sopenharmony_ci
250f66f451Sopenharmony_ci    Display type:
260f66f451Sopenharmony_ci    -b One byte octal   -c One byte character -C Canonical (hex + ASCII)
270f66f451Sopenharmony_ci    -d Two byte decimal -o Two byte octal     -x Two byte hexadecimal (default)
280f66f451Sopenharmony_ci
290f66f451Sopenharmony_ciconfig HD
300f66f451Sopenharmony_ci  bool "hd"
310f66f451Sopenharmony_ci  default HEXDUMP
320f66f451Sopenharmony_ci  help
330f66f451Sopenharmony_ci    usage: hd [FILE...]
340f66f451Sopenharmony_ci
350f66f451Sopenharmony_ci    Display file(s) in cannonical hex+ASCII format.
360f66f451Sopenharmony_ci*/
370f66f451Sopenharmony_ci
380f66f451Sopenharmony_ci#define FOR_hexdump
390f66f451Sopenharmony_ci#include "toys.h"
400f66f451Sopenharmony_ci
410f66f451Sopenharmony_ciGLOBALS(
420f66f451Sopenharmony_ci    long s, n;
430f66f451Sopenharmony_ci
440f66f451Sopenharmony_ci    long long len, pos, ppos;
450f66f451Sopenharmony_ci    const char *fmt;
460f66f451Sopenharmony_ci    unsigned int fn, bc;  // file number and byte count
470f66f451Sopenharmony_ci    char linebuf[16];  // line buffer - serves double duty for sqeezing repeat
480f66f451Sopenharmony_ci                       // lines and for accumulating full lines accross file
490f66f451Sopenharmony_ci                       // boundaries if necessesary.
500f66f451Sopenharmony_ci)
510f66f451Sopenharmony_ci
520f66f451Sopenharmony_ciconst char *make_printable(unsigned char byte) {
530f66f451Sopenharmony_ci  switch (byte) {
540f66f451Sopenharmony_ci    case '\0': return "\\0";
550f66f451Sopenharmony_ci    case '\a': return "\\a";
560f66f451Sopenharmony_ci    case '\b': return "\\b";
570f66f451Sopenharmony_ci    case '\t': return "\\t";
580f66f451Sopenharmony_ci    case '\n': return "\\n";
590f66f451Sopenharmony_ci    case '\v': return "\\v";
600f66f451Sopenharmony_ci    case '\f': return "\\f";
610f66f451Sopenharmony_ci    default: return "??";  // for all unprintable bytes
620f66f451Sopenharmony_ci  }
630f66f451Sopenharmony_ci}
640f66f451Sopenharmony_ci
650f66f451Sopenharmony_civoid do_hexdump(int fd, char *name)
660f66f451Sopenharmony_ci{
670f66f451Sopenharmony_ci  unsigned short block, adv, i;
680f66f451Sopenharmony_ci  int sl, fs;  // skip line, file size
690f66f451Sopenharmony_ci
700f66f451Sopenharmony_ci  TT.fn++;  // keep track of how many files have been printed.
710f66f451Sopenharmony_ci  // skipp ahead, if necessary skip entire files:
720f66f451Sopenharmony_ci  if (FLAG(s) && (TT.s-TT.pos>0)) {
730f66f451Sopenharmony_ci    fs = xlseek(fd, 0L, SEEK_END);
740f66f451Sopenharmony_ci
750f66f451Sopenharmony_ci    if (fs < TT.s) {
760f66f451Sopenharmony_ci      TT.pos += fs;
770f66f451Sopenharmony_ci      TT.ppos += fs;
780f66f451Sopenharmony_ci    } else {
790f66f451Sopenharmony_ci      xlseek(fd, TT.s-TT.pos, SEEK_SET);
800f66f451Sopenharmony_ci      TT.ppos = TT.s;
810f66f451Sopenharmony_ci      TT.pos = TT.s;
820f66f451Sopenharmony_ci    }
830f66f451Sopenharmony_ci  }
840f66f451Sopenharmony_ci
850f66f451Sopenharmony_ci  for (sl = 0;
860f66f451Sopenharmony_ci       0 < (TT.len = readall(fd, toybuf,
870f66f451Sopenharmony_ci                             (TT.n && TT.s+TT.n-TT.pos<16-(TT.bc%16))
880f66f451Sopenharmony_ci                                ? TT.s+TT.n-TT.pos : 16-(TT.bc%16)));
890f66f451Sopenharmony_ci       TT.pos += TT.len) {
900f66f451Sopenharmony_ci    // This block compares the data read from file to the last line printed.
910f66f451Sopenharmony_ci    // If they don't match a new line is printed, else the line is skipped.
920f66f451Sopenharmony_ci    // If a * has already been printed to indicate a skipped line, printing the
930f66f451Sopenharmony_ci    // * is also skipped.
940f66f451Sopenharmony_ci    for (i = 0; i < 16 && i < TT.len; i++){
950f66f451Sopenharmony_ci      if (FLAG(v) || TT.len < 16 || toybuf[i] != TT.linebuf[i]) goto newline;
960f66f451Sopenharmony_ci    }
970f66f451Sopenharmony_ci    if (sl == 0) {
980f66f451Sopenharmony_ci      printf("*\n");
990f66f451Sopenharmony_ci      sl = 1;
1000f66f451Sopenharmony_ci    }
1010f66f451Sopenharmony_ci    TT.ppos += TT.len;
1020f66f451Sopenharmony_ci    continue;
1030f66f451Sopenharmony_ci
1040f66f451Sopenharmony_cinewline:
1050f66f451Sopenharmony_ci    strncpy(TT.linebuf+(TT.bc%16), toybuf, TT.len);
1060f66f451Sopenharmony_ci    TT.bc = TT.bc % 16 + TT.len;
1070f66f451Sopenharmony_ci    sl = 0;
1080f66f451Sopenharmony_ci    if (TT.pos + TT.bc == TT.s+TT.n || TT.fn == toys.optc || TT.bc == 16) {
1090f66f451Sopenharmony_ci      if (!FLAG(C) && !FLAG(c)) {
1100f66f451Sopenharmony_ci        printf("%07llx", TT.ppos);
1110f66f451Sopenharmony_ci        adv = FLAG(b) ? 1 : 2;
1120f66f451Sopenharmony_ci        for (i = 0; i < TT.bc; i += adv) {
1130f66f451Sopenharmony_ci          block = (FLAG(b) || i == TT.bc-1)
1140f66f451Sopenharmony_ci            ? TT.linebuf[i] : (TT.linebuf[i] | TT.linebuf[i+1] << 8);
1150f66f451Sopenharmony_ci          printf(TT.fmt, block);
1160f66f451Sopenharmony_ci        }
1170f66f451Sopenharmony_ci      } else if (FLAG(C)) {
1180f66f451Sopenharmony_ci        printf("%08llx", TT.ppos);
1190f66f451Sopenharmony_ci        for (i = 0; i < 16; i++) {
1200f66f451Sopenharmony_ci          if (!(i % 8)) putchar(' ');
1210f66f451Sopenharmony_ci          if (i < TT.bc) printf(" %02x", TT.linebuf[i]);
1220f66f451Sopenharmony_ci          else printf("   ");
1230f66f451Sopenharmony_ci        }
1240f66f451Sopenharmony_ci        printf("  |");
1250f66f451Sopenharmony_ci        for (i = 0; i < TT.bc; i++) {
1260f66f451Sopenharmony_ci          if (TT.linebuf[i] < ' ' || TT.linebuf[i] > '~') putchar('.');
1270f66f451Sopenharmony_ci          else putchar(TT.linebuf[i]);
1280f66f451Sopenharmony_ci        }
1290f66f451Sopenharmony_ci        putchar('|');
1300f66f451Sopenharmony_ci      } else {
1310f66f451Sopenharmony_ci        printf("%07llx", TT.ppos);
1320f66f451Sopenharmony_ci        for (i = 0; i < TT.bc; i++) {
1330f66f451Sopenharmony_ci          if (TT.linebuf[i] >= ' ' && TT.linebuf[i] <= '~')
1340f66f451Sopenharmony_ci            printf("%4c", TT.linebuf[i]);
1350f66f451Sopenharmony_ci          else printf("%4s", make_printable(TT.linebuf[i]));
1360f66f451Sopenharmony_ci        }
1370f66f451Sopenharmony_ci      }
1380f66f451Sopenharmony_ci      putchar('\n');
1390f66f451Sopenharmony_ci      TT.ppos += TT.bc;
1400f66f451Sopenharmony_ci    }
1410f66f451Sopenharmony_ci  }
1420f66f451Sopenharmony_ci
1430f66f451Sopenharmony_ci  if (TT.len < 0) perror_exit("read");
1440f66f451Sopenharmony_ci}
1450f66f451Sopenharmony_ci
1460f66f451Sopenharmony_civoid hexdump_main(void)
1470f66f451Sopenharmony_ci{
1480f66f451Sopenharmony_ci  if FLAG(b) TT.fmt = " %03o";
1490f66f451Sopenharmony_ci  else if FLAG(d) TT.fmt = " %05d";
1500f66f451Sopenharmony_ci  else if FLAG(o) TT.fmt = " %06o";
1510f66f451Sopenharmony_ci  else TT.fmt = " %04x";
1520f66f451Sopenharmony_ci
1530f66f451Sopenharmony_ci  loopfiles(toys.optargs, do_hexdump);
1540f66f451Sopenharmony_ci  FLAG(C) ? printf("%08llx\n", TT.pos) : printf("%07llx\n", TT.pos);
1550f66f451Sopenharmony_ci}
156