| /* od.c - Provide octal/hex dumps of data |
| * |
| * Copyright 2012 Andre Renaud <andre@bluewatersys.com> |
| * Copyright 2012 Rob Landley <rob@landley.net> |
| * |
| * See http://opengroup.org/onlinepubs/9699919799/utilities/od.html |
| |
| USE_OD(NEWTOY(od, "j#vw#<1=16N#xsodcbA:t*", TOYFLAG_USR|TOYFLAG_BIN)) |
| |
| config OD |
| bool "od" |
| default y |
| help |
| usage: od [-bcdosxv] [-j #] [-N #] [-w #] [-A doxn] [-t acdfoux[#]] |
| |
| -A Address base (decimal, octal, hexdecimal, none) |
| -j Skip this many bytes of input |
| -N Stop dumping after this many bytes |
| -t Output type a(scii) c(har) d(ecimal) f(loat) o(ctal) u(nsigned) (he)x |
| plus optional size in bytes |
| aliases: -b=-t o1, -c=-t c, -d=-t u2, -o=-t o2, -s=-t d2, -x=-t x2 |
| -v Don't collapse repeated lines together |
| -w Total line width in bytes (default 16). |
| */ |
| |
| #define FOR_od |
| #include "toys.h" |
| |
| GLOBALS( |
| struct arg_list *output_base; |
| char *address_base; |
| long max_count; |
| long width; |
| long jump_bytes; |
| |
| int address_idx; |
| unsigned types, leftover, star; |
| char *buf; // Points to buffers[0] or buffers[1]. |
| char *bufs[2]; // Used to detect duplicate lines. |
| off_t pos; |
| ) |
| |
| static char *ascii = "nulsohstxetxeotenqackbel bs ht nl vt ff cr so si" |
| "dledc1dc2dc3dc4naksynetbcan emsubesc fs gs rs us sp"; |
| |
| struct odtype { |
| int type; |
| int size; |
| }; |
| |
| static int od_out_t(struct odtype *t, char *buf, int *offset) |
| { |
| unsigned k; |
| int throw = 0, pad = 0; |
| |
| // Handle ascii |
| if (t->type < 2) { |
| char c = TT.buf[(*offset)++]; |
| pad += 4; |
| |
| if (!t->type) { |
| c &= 127; |
| if (c<=32) sprintf(buf, "%.3s", ascii+(3*c)); |
| else if (c==127) strcpy(buf, "del"); |
| else sprintf(buf, "%c", c); |
| } else { |
| char *bfnrtav = "\b\f\n\r\t\a\v", *s = strchr(bfnrtav, c); |
| if (s) sprintf(buf, "\\%c", "bfnrtav0"[s-bfnrtav]); |
| else if (c < 32 || c >= 127) sprintf(buf, "%03o", c); |
| else { |
| // TODO: this should be UTF8 aware. |
| sprintf(buf, "%c", c); |
| } |
| } |
| } else if (CFG_TOYBOX_FLOAT && t->type == 6) { |
| long double ld; |
| union {float f; double d; long double ld;} fdl; |
| |
| memcpy(&fdl, TT.buf+*offset, t->size); |
| *offset += t->size; |
| if (sizeof(float) == t->size) { |
| ld = fdl.f; |
| pad += (throw = 8)+7; |
| } else if (sizeof(double) == t->size) { |
| ld = fdl.d; |
| pad += (throw = 17)+8; |
| } else if (sizeof(long double) == t->size) { |
| ld = fdl.ld; |
| pad += (throw = 21)+9; |
| } else error_exit("bad -tf '%d'", t->size); |
| |
| sprintf(buf, "%.*Le", throw, ld); |
| // Integer types |
| } else { |
| unsigned long long ll = 0, or; |
| char *c[] = {"%*lld", "%*llu", "%0*llo", "%0*llx"}, |
| *class = c[t->type-2]; |
| |
| // Work out width of field |
| if (t->size == 8) { |
| or = -1LL; |
| if (t->type == 2) or >>= 1; |
| } else or = (1LL<<(8*t->size))-1; |
| throw = sprintf(buf, class, 0, or); |
| |
| // Accumulate integer based on size argument |
| for (k=0; k < t->size; k++) { |
| or = TT.buf[(*offset)++]; |
| ll |= or << (8*(IS_BIG_ENDIAN ? t->size-k-1 : k)); |
| } |
| |
| // Handle negative values |
| if (t->type == 2) { |
| or = sizeof(or) - t->size; |
| throw++; |
| if (or && (ll & (1l<<((8*t->size)-1)))) |
| ll |= ((or<<(8*or))-1) << (8*t->size); |
| } |
| |
| sprintf(buf, class, throw, ll); |
| pad += throw+1; |
| } |
| |
| return pad; |
| } |
| |
| static void od_outline(void) |
| { |
| unsigned flags = toys.optflags; |
| char buf[128], *abases[] = {"", "%07lld", "%07llo", "%06llx"}; |
| struct odtype *types = (struct odtype *)toybuf; |
| int i, j, len, pad; |
| |
| if (TT.leftover<TT.width) memset(TT.buf+TT.leftover, 0, TT.width-TT.leftover); |
| |
| // Handle duplciate lines as * |
| if (!(flags&FLAG_v) && TT.jump_bytes != TT.pos && TT.leftover |
| && !memcmp(TT.bufs[0], TT.bufs[1], TT.width)) |
| { |
| if (!TT.star) { |
| xputs("*"); |
| TT.star++; |
| } |
| |
| // Print line position |
| } else { |
| TT.star = 0; |
| |
| // off_t varies so expand it to largest possible size |
| xprintf(abases[TT.address_idx], (long long)TT.pos); |
| if (!TT.leftover) { |
| if (TT.address_idx) xputc('\n'); |
| return; |
| } |
| } |
| |
| TT.pos += len = TT.leftover; |
| TT.leftover = 0; |
| if (TT.star) return; |
| |
| // Find largest "pad" of the output types. |
| for (i = pad = 0; i<TT.types; i++) { |
| int bytes = 0; |
| |
| // If more than one byte of input consumed, average rounding up. |
| j = od_out_t(types+i, buf, &bytes); |
| j = (j+bytes-1)/bytes; |
| |
| if (j > pad) pad = j; |
| } |
| |
| // For each output type, print one line |
| for (i=0; i<TT.types; i++) { |
| for (j = 0; j<len;) { |
| int bytes = j; |
| |
| // pad for as many bytes as were consumed, and indent non-numbered lines |
| od_out_t(types+i, buf, &bytes); |
| xprintf("%*s", pad*(bytes-j) + 7*(!!i)*!j, buf); |
| j = bytes; |
| } |
| xputc('\n'); |
| } |
| |
| // Toggle buffer for "same as last time" check. |
| TT.buf = (TT.buf == TT.bufs[0]) ? TT.bufs[1] : TT.bufs[0]; |
| } |
| |
| // Loop through input files |
| static void do_od(int fd, char *name) |
| { |
| // Skip input, possibly more than one entire file. |
| if (TT.jump_bytes > TT.pos) { |
| off_t pos = TT.jump_bytes-TT.pos, off = lskip(fd, pos); |
| |
| if (off >= 0) TT.pos += pos-off; |
| if (TT.jump_bytes > TT.pos) return; |
| } |
| |
| for(;;) { |
| char *buf = TT.buf + TT.leftover; |
| int len = TT.width - TT.leftover; |
| |
| if (toys.optflags & FLAG_N) { |
| if (!TT.max_count) break; |
| if (TT.max_count < len) len = TT.max_count; |
| } |
| |
| len = readall(fd, buf, len); |
| if (len < 0) { |
| perror_msg_raw(name); |
| break; |
| } |
| if (TT.max_count) TT.max_count -= len; |
| TT.leftover += len; |
| if (TT.leftover < TT.width) break; |
| |
| od_outline(); |
| } |
| } |
| |
| // Handle one -t argument (including implicit ones) |
| static void append_base(char *base) |
| { |
| char *s = base; |
| struct odtype *types = (struct odtype *)toybuf; |
| int type; |
| |
| for (;;) { |
| int size = 1; |
| |
| if (!*s) return; |
| if (TT.types >= sizeof(toybuf)/sizeof(struct odtype)) break; |
| if (-1 == (type = stridx("acduox"USE_TOYBOX_FLOAT("f"), *(s++)))) break; |
| |
| if (isdigit(*s)) { |
| size = strtol(s, &s, 10); |
| if (type < 2 && size != 1) break; |
| if (CFG_TOYBOX_FLOAT && type == 6 && size == sizeof(long double)); |
| else if (size < 1 || size > 8) break; |
| } else if (CFG_TOYBOX_FLOAT && type == 6) { |
| int sizes[] = {sizeof(float), sizeof(double), sizeof(long double)}; |
| if (-1 == (size = stridx("FDL", *s))) size = sizeof(double); |
| else { |
| s++; |
| size = sizes[size]; |
| } |
| } else if (type > 1) { |
| if (-1 == (size = stridx("CSIL", *s))) size = 4; |
| else { |
| s++; |
| size = 1 << size; |
| } |
| } |
| |
| types[TT.types].type = type; |
| types[TT.types].size = size; |
| TT.types++; |
| } |
| |
| error_exit("bad -t %s", base); |
| } |
| |
| void od_main(void) |
| { |
| struct arg_list *arg; |
| |
| TT.bufs[0] = xzalloc(TT.width); |
| TT.bufs[1] = xzalloc(TT.width); |
| TT.buf = TT.bufs[0]; |
| |
| if (!TT.address_base) TT.address_idx = 2; |
| else if (0>(TT.address_idx = stridx("ndox", *TT.address_base))) |
| error_exit("bad -A '%c'", *TT.address_base); |
| |
| // Collect -t entries |
| |
| for (arg = TT.output_base; arg; arg = arg->next) append_base(arg->arg); |
| if (toys.optflags & FLAG_b) append_base("o1"); |
| if (toys.optflags & FLAG_c) append_base("c"); |
| if (toys.optflags & FLAG_d) append_base("u2"); |
| if (toys.optflags & FLAG_o) append_base("o2"); |
| if (toys.optflags & FLAG_s) append_base("d2"); |
| if (toys.optflags & FLAG_x) append_base("x2"); |
| if (!TT.types) append_base("o2"); |
| |
| loopfiles(toys.optargs, do_od); |
| |
| if (TT.leftover) od_outline(); |
| od_outline(); |
| |
| if (CFG_TOYBOX_FREE) { |
| free(TT.bufs[0]); |
| free(TT.bufs[1]); |
| } |
| } |