toybox/toys/posix/od.c
<<
>>
Prefs
   1/* od.c - Provide octal/hex dumps of data
   2 *
   3 * Copyright 2012 Andre Renaud <andre@bluewatersys.com>
   4 * Copyright 2012 Rob Landley <rob@landley.net>
   5 *
   6 * See http://opengroup.org/onlinepubs/9699919799/utilities/od.html
   7
   8USE_OD(NEWTOY(od, "j#vw#<1=16N#xsodcbA:t*", TOYFLAG_USR|TOYFLAG_BIN))
   9
  10config OD
  11  bool "od"
  12  default y
  13  help
  14    usage: od [-bcdosxv] [-j #] [-N #] [-w #] [-A doxn] [-t acdfoux[#]]
  15
  16    -A  Address base (decimal, octal, hexdecimal, none)
  17    -j  Skip this many bytes of input
  18    -N  Stop dumping after this many bytes
  19    -t  Output type a(scii) c(har) d(ecimal) f(loat) o(ctal) u(nsigned) (he)x
  20        plus optional size in bytes
  21        aliases: -b=-t o1, -c=-t c, -d=-t u2, -o=-t o2, -s=-t d2, -x=-t x2
  22    -v  Don't collapse repeated lines together
  23    -w  Total line width in bytes (default 16)
  24*/
  25
  26#define FOR_od
  27#include "toys.h"
  28
  29GLOBALS(
  30  struct arg_list *output_base;
  31  char *address_base;
  32  long max_count;
  33  long width;
  34  long jump_bytes;
  35
  36  int address_idx;
  37  unsigned types, leftover, star;
  38  char *buf; // Points to buffers[0] or buffers[1].
  39  char *bufs[2]; // Used to detect duplicate lines.
  40  off_t pos;
  41)
  42
  43static char *ascii = "nulsohstxetxeotenqackbel bs ht nl vt ff cr so si"
  44  "dledc1dc2dc3dc4naksynetbcan emsubesc fs gs rs us sp";
  45
  46struct odtype {
  47  int type;
  48  int size;
  49};
  50
  51static int od_out_t(struct odtype *t, char *buf, int *offset)
  52{
  53  unsigned k;
  54  int throw = 0, pad = 0;
  55
  56  // Handle ascii
  57  if (t->type < 2) {
  58    char c = TT.buf[(*offset)++];
  59    pad += 4;
  60
  61    if (!t->type) {
  62      c &= 127;
  63      if (c<=32) sprintf(buf, "%.3s", ascii+(3*c));
  64      else if (c==127) strcpy(buf, "del");
  65      else sprintf(buf, "%c", c);
  66    } else {
  67      char *bfnrtav = "\b\f\n\r\t\a\v", *s = strchr(bfnrtav, c);
  68      if (s) sprintf(buf, "\\%c", "bfnrtav0"[s-bfnrtav]);
  69      else if (c < 32 || c >= 127) sprintf(buf, "%03o", c);
  70      else {
  71        // TODO: this should be UTF8 aware.
  72        sprintf(buf, "%c", c);
  73      }
  74    }
  75  } else if (CFG_TOYBOX_FLOAT && t->type == 6) {
  76    long double ld;
  77    union {float f; double d; long double ld;} fdl;
  78
  79    memcpy(&fdl, TT.buf+*offset, t->size);
  80    *offset += t->size;
  81    if (sizeof(float) == t->size) {
  82      ld = fdl.f;
  83      pad += (throw = 8)+7;
  84    } else if (sizeof(double) == t->size) {
  85      ld = fdl.d;
  86      pad += (throw = 17)+8;
  87    } else if (sizeof(long double) == t->size) {
  88      ld = fdl.ld;
  89      pad += (throw = 21)+9;
  90    } else error_exit("bad -tf '%d'", t->size);
  91
  92    sprintf(buf, "%.*Le", throw, ld);
  93  // Integer types
  94  } else {
  95    unsigned long long ll = 0, or;
  96    char *c[] = {"%*lld", "%*llu", "%0*llo", "%0*llx"},
  97      *class = c[t->type-2];
  98
  99    // Work out width of field
 100    if (t->size == 8) {
 101      or = -1LL;
 102      if (t->type == 2) or >>= 1;
 103    } else or = (1LL<<(8*t->size))-1;
 104    throw = sprintf(buf, class, 0, or);
 105
 106    // Accumulate integer based on size argument
 107    for (k=0; k < t->size; k++) {
 108      or = TT.buf[(*offset)++];
 109      ll |= or << (8*(IS_BIG_ENDIAN ? t->size-k-1 : k));
 110    }
 111
 112    // Handle negative values
 113    if (t->type == 2) {
 114      or = sizeof(or) - t->size;
 115      throw++;
 116      if (or && (ll & (1l<<((8*t->size)-1))))
 117        ll |= ((or<<(8*or))-1) << (8*t->size);
 118    }
 119
 120    sprintf(buf, class, throw, ll);
 121    pad += throw+1;
 122  }
 123
 124  return pad;
 125}
 126
 127static void od_outline(void)
 128{
 129  unsigned flags = toys.optflags;
 130  char buf[128], *abases[] = {"", "%07lld", "%07llo", "%06llx"};
 131  struct odtype *types = (struct odtype *)toybuf;
 132  int i, j, len, pad;
 133
 134  if (TT.leftover<TT.width) memset(TT.buf+TT.leftover, 0, TT.width-TT.leftover);
 135
 136  // Handle duplciate lines as *
 137  if (!(flags&FLAG_v) && TT.jump_bytes != TT.pos && TT.leftover
 138    && !memcmp(TT.bufs[0], TT.bufs[1], TT.width))
 139  {
 140    if (!TT.star) {
 141      xputs("*");
 142      TT.star++;
 143    }
 144
 145  // Print line position
 146  } else {
 147    TT.star = 0;
 148
 149    // off_t varies so expand it to largest possible size
 150    xprintf(abases[TT.address_idx], (long long)TT.pos);
 151    if (!TT.leftover) {
 152      if (TT.address_idx) xputc('\n');
 153      return;
 154    }
 155  }
 156
 157  TT.pos += len = TT.leftover;
 158  TT.leftover = 0;
 159  if (TT.star) return;
 160
 161  // Find largest "pad" of the output types.
 162  for (i = pad = 0; i<TT.types; i++) {
 163    int bytes = 0;
 164
 165    // If more than one byte of input consumed, average rounding up.
 166    j = od_out_t(types+i, buf, &bytes);
 167    j = (j+bytes-1)/bytes;
 168   
 169    if (j > pad) pad = j;
 170  }
 171
 172  // For each output type, print one line
 173  for (i=0; i<TT.types; i++) {
 174    for (j = 0; j<len;) {
 175      int bytes = j;
 176
 177      // pad for as many bytes as were consumed, and indent non-numbered lines
 178      od_out_t(types+i, buf, &bytes);
 179      xprintf("%*s", pad*(bytes-j) + 7*(!!i)*!j, buf);
 180      j = bytes;
 181    }
 182    xputc('\n');
 183  }
 184
 185  // Toggle buffer for "same as last time" check.
 186  TT.buf = (TT.buf == TT.bufs[0]) ? TT.bufs[1] : TT.bufs[0];
 187}
 188
 189// Loop through input files
 190static void do_od(int fd, char *name)
 191{
 192  // Skip input, possibly more than one entire file.
 193  if (TT.jump_bytes > TT.pos) {
 194    off_t pos = TT.jump_bytes-TT.pos, off = lskip(fd, pos);
 195
 196    if (off >= 0) TT.pos += pos-off;
 197    if (TT.jump_bytes > TT.pos) return;
 198  }
 199
 200  for(;;) {
 201    char *buf = TT.buf + TT.leftover;
 202    int len = TT.width - TT.leftover;
 203
 204    if (toys.optflags & FLAG_N) {
 205      if (!TT.max_count) break;
 206      if (TT.max_count < len) len = TT.max_count;
 207    }
 208
 209    len = readall(fd, buf, len);
 210    if (len < 0) {
 211      perror_msg_raw(name);
 212      break;
 213    }
 214    if (TT.max_count) TT.max_count -= len;
 215    TT.leftover += len;
 216    if (TT.leftover < TT.width) break;
 217
 218    od_outline();
 219  }
 220}
 221
 222// Handle one -t argument (including implicit ones)
 223static void append_base(char *base)
 224{
 225  char *s = base;
 226  struct odtype *types = (struct odtype *)toybuf;
 227  int type;
 228
 229  for (;;) {
 230    int size = 1;
 231
 232    if (!*s) return;
 233    if (TT.types >= sizeof(toybuf)/sizeof(struct odtype)) break;
 234    if (-1 == (type = stridx("acduox"USE_TOYBOX_FLOAT("f"), *(s++)))) break;
 235
 236    if (isdigit(*s)) {
 237      size = strtol(s, &s, 10);
 238      if (type < 2 && size != 1) break;
 239      if (CFG_TOYBOX_FLOAT && type == 6 && size == sizeof(long double));
 240      else if (size < 1 || size > 8) break;
 241    } else if (CFG_TOYBOX_FLOAT && type == 6) {
 242      int sizes[] = {sizeof(float), sizeof(double), sizeof(long double)};
 243      if (-1 == (size = stridx("FDL", *s))) size = sizeof(double);
 244      else {
 245        s++;
 246        size = sizes[size];
 247      }
 248    } else if (type > 1) {
 249      if (-1 == (size = stridx("CSIL", *s))) size = 4;
 250      else {
 251        s++;
 252        size = 1 << size;
 253      }
 254    }
 255
 256    types[TT.types].type = type;
 257    types[TT.types].size = size;
 258    TT.types++;
 259  }
 260
 261  error_exit("bad -t %s", base);
 262}
 263
 264void od_main(void)
 265{
 266  struct arg_list *arg;
 267
 268  TT.bufs[0] = xzalloc(TT.width);
 269  TT.bufs[1] = xzalloc(TT.width);
 270  TT.buf = TT.bufs[0];
 271
 272  if (!TT.address_base) TT.address_idx = 2;
 273  else if (0>(TT.address_idx = stridx("ndox", *TT.address_base)))
 274    error_exit("bad -A '%c'", *TT.address_base);
 275
 276  // Collect -t entries
 277
 278  for (arg = TT.output_base; arg; arg = arg->next) append_base(arg->arg);
 279  if (toys.optflags & FLAG_b) append_base("o1");
 280  if (toys.optflags & FLAG_c) append_base("c");
 281  if (toys.optflags & FLAG_d) append_base("u2");
 282  if (toys.optflags & FLAG_o) append_base("o2");
 283  if (toys.optflags & FLAG_s) append_base("d2");
 284  if (toys.optflags & FLAG_x) append_base("x2");
 285  if (!TT.types) append_base("o2");
 286
 287  loopfiles(toys.optargs, do_od);
 288
 289  if (TT.leftover) od_outline();
 290  od_outline();
 291
 292  if (CFG_TOYBOX_FREE) {
 293    free(TT.bufs[0]);
 294    free(TT.bufs[1]);
 295  }
 296}
 297