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