diff options
-rw-r--r-- | toys/od.c | 354 |
1 files changed, 190 insertions, 164 deletions
@@ -3,229 +3,255 @@ * od.c - Provide octal/hex dumps of data * * Copyright 2012 Andre Renaud <andre@bluewatersys.com> + * Copyright 2012 Rob Landley <rob@landley.net> * * See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/od.html -USE_OD(NEWTOY(od, "j#vN#xsodbA:t*", TOYFLAG_USR|TOYFLAG_BIN)) +USE_OD(NEWTOY(od, "j#vN#xsodcbA:t*", TOYFLAG_USR|TOYFLAG_BIN)) config OD bool "od" default y help - usage: od [-bdosxv] [-j #] [-N #] [-A arg] [-t arg] + usage: od [-bdosxv] [-j #] [-N #] [-A doxn] [-t arg] + + -A Address base (decimal, octal, hexdecimal, none) + -t output type(s) a (ascii) c (char) d (decimal) foux */ #include "toys.h" -#define OD_BLOCK_SIZE 16 - #define FLAG_t (1 << 0) #define FLAG_A (1 << 1) #define FLAG_b (1 << 2) -#define FLAG_d (1 << 3) -#define FLAG_o (1 << 4) -#define FLAG_s (1 << 5) -#define FLAG_x (1 << 6) -#define FLAG_N (1 << 7) -#define FLAG_v (1 << 8) - +#define FLAG_c (1 << 3) +#define FLAG_d (1 << 4) +#define FLAG_o (1 << 5) +#define FLAG_s (1 << 6) +#define FLAG_x (1 << 7) +#define FLAG_N (1 << 8) +#define FLAG_v (1 << 9) DEFINE_GLOBALS( - struct arg_list *output_base; - char *address_base; - long max_count; - long jump_bytes; + struct arg_list *output_base; + char *address_base; + long max_count; + long jump_bytes; + + unsigned types, leftover, star, address_idx; + char *buf; + uint64_t bufs[4]; // force 64-bit alignment + off_t pos; ) #define TT this.od -static const char *ascii = "nulsohstxetceotenqackbel bs ht nl vt ff cr so si" - "deldc1dc2dc3dc4naksynetbcan emsubesc fs gs rs us sp"; +static char *ascii = "nulsohstxetxeotenqackbel bs ht nl vt ff cr so si" + "dledc1dc2dc3dc4naksynetbcan emsubesc fs gs rs us sp"; -static void display_line_1(char base, off_t offset, uint8_t *line, int len) -{ - while (len--) { - switch (base) { - case 'a': { - int ch = *line & 0x7f; - if (ch <= 32) printf(" %.3s", ascii+(3*ch)); - else if (ch == 127) printf(" del"); - else printf("%4c", ch); - break; - } - case 'o': printf(" %3.3o", *line); break; - case 'x': printf(" %2.2x", *line); break; - case 'd': printf(" %4d", *(int8_t *)line); break; - case 'u': printf(" %3u", *line); break; - } - line++; - } -} +struct odtype { + int type; + int size; +}; -static void display_line_2(char base, off_t offset, uint16_t *line, int len) +static void od_outline(void) { - while (len--) { - switch (base) { - case 'o': printf(" %6.6o", *line); break; - case 'x': printf(" %4.4x", *line); break; - case 'd': printf(" %6d", *(int16_t*)line); break; - case 'u': printf(" %5u", *line); break; - } - line++; - } -} + unsigned flags = toys.optflags; + char *abases[] = {"", "%07d", "%07o", "%06x"}; + struct odtype *types = (struct odtype *)toybuf, *t; + int i, len; -static void display_line_4(char base, off_t offset, uint32_t *line, int len) -{ - while (len--) { - switch (base) { - case 'o': printf(" %11.11o", *line); break; - case 'x': printf(" %8.8x", *line); break; - case 'd': printf(" %11d", *(int32_t *)line); break; - case 'u': printf(" %10u", *line); break; - case 'f': printf(" %15g", *(float *)line); break; + if (TT.leftover<16) memset(TT.buf+TT.leftover, 0, 16-TT.leftover); + + // Handle duplciate lines as * + if (!(flags&FLAG_v) && TT.jump_bytes != TT.pos && TT.leftover + && !memcmp(TT.bufs, TT.bufs + 2, 16)) + { + if (!TT.star) { + xputs("*"); + TT.star++; } - line++; - } -} -static void display_line_8(char base, off_t offset, uint64_t *line, int len) -{ - while (len--) { - switch (base) { - //case 'o': printf(" %22.22o", *line); break; - case 'x': printf(" %16.16" PRIx64, *line); break; - case 'd': printf(" %21" PRId64, *(int64_t *)line); break; - case 'u': printf(" %20" PRIu64, *line); break; - case 'f': printf(" %23g", *(double *)line); + // Print line position + } else { + TT.star = 0; + + xprintf(abases[TT.address_idx], TT.pos); + if (!TT.leftover) { + if (TT.address_idx) xputc('\n'); + return; } - line++; } -} -static void display_line(off_t offset, uint8_t *line, int len) -{ - struct arg_list *output = TT.output_base; - switch (*TT.address_base) { - case 'o': printf("%8.8zo", offset); break; - case 'd': printf("%7.7zd", offset); break; - case 'x': printf("%6.6zx", offset); break; - case 'n': - default: - break; - } - while (output) { - char base = *output->arg; - int width = atoi(&output->arg[1]); - - switch (width) { - case 1: display_line_1(base, offset, line, len); break; - case 2: display_line_2(base, offset, (uint16_t *)line, - (len + 1) / 2); break; - case 4: display_line_4(base, offset, (uint32_t *)line, - (len + 3) / 4); break; - case 8: display_line_8(base, offset, (uint64_t *)line, - (len + 7) / 8); break; + TT.pos += len = TT.leftover; + TT.leftover = 0; + if (TT.star) return; + + // For each output type, print one line + + for (i=0; i<TT.types; i++) { + int j = 0, pad = i ? 8 : 0; + char buf[128]; + + t = types+i; + while (j<len) { + unsigned k; + + // Handle ascii + if (t->type < 2) { + char c = TT.buf[j++]; + 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 *bfnrt = "\b\f\n\r\t", *s = strchr(bfnrt, c); + if (s) sprintf(buf, "\\%c", "bfnrt0"[s-bfnrt]); + else { + // TODO: this should be UTF8 aware. + sprintf(buf, "%c", c); + } + } + } else if (CFG_TOYBOX_FLOAT && t->type == 6) { + // TODO: floating point stuff + // Integer types + } else { + unsigned long long ll = 0, or; + int throw = 0; + 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[j++]; + 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; + } + xprintf("%*s", pad, buf); + pad = 0; } - output = output->next; + xputc('\n'); } - printf("\n"); + // buffer toggle for "same as last time" check. + TT.buf = (char *)((TT.buf == (char *)TT.bufs) ? TT.bufs+2 : TT.bufs); } static void do_od(int fd, char *name) { - uint8_t *block[2]; - int index = 0; - off_t offset = 0; - int last_match = 0; + // Skip input, possibly more than one entire file. + if (TT.jump_bytes < TT.pos) { + off_t off = lskip(fd, TT.jump_bytes); + if (off > 0) TT.pos += off; + if (TT.jump_bytes < TT.pos) return; + } - block[0] = (uint8_t *)toybuf; - block[1] = (uint8_t *)&toybuf[OD_BLOCK_SIZE]; + for(;;) { + char *buf = TT.buf + TT.leftover; + int len = 16 - TT.leftover; - if (TT.jump_bytes) { - xlseek(fd, TT.jump_bytes, SEEK_SET); - offset = TT.jump_bytes; - } + if (toys.optflags & FLAG_N) { + if (!TT.max_count) break; + if (TT.max_count < len) len = TT.max_count; + } - for (;;) { - int len; - int max_len = OD_BLOCK_SIZE; - if ((toys.optflags & FLAG_N) && offset + OD_BLOCK_SIZE > TT.max_count) - max_len = TT.max_count - offset; - len = xread(fd, block[index], max_len); - if (!len) break; - - memset(&block[index][len], 0, OD_BLOCK_SIZE - len); - - if (!(toys.optflags & FLAG_v) && offset > 0 && - memcmp(block[0], block[1], OD_BLOCK_SIZE) == 0) - { - if (!last_match) puts("*"); - last_match = 1; - } else { - display_line(offset, block[index], len); - last_match = 0; + len = readall(fd, buf, len); + if (len < 0) { + perror_msg("%s", name); + break; } - offset += len; - index = !index; + if (TT.max_count) TT.max_count -= len; + TT.leftover += len; + if (TT.leftover < 16) break; - if (len != OD_BLOCK_SIZE) break; + od_outline(); } - - if (!(toys.optflags & FLAG_N) && offset != TT.max_count) - display_line(offset, NULL, 0); } static void append_base(char *base) { - struct arg_list *arg, *prev; - arg = xmalloc(sizeof(struct arg_list)); - prev = TT.output_base; - TT.output_base = arg; - arg->arg = base; - arg->next = prev; -} + char *s = base; + struct odtype *types = (struct odtype *)toybuf; + int type; -static void valid_bases(void) -{ - struct arg_list *arg = TT.output_base; - while (arg) { - char base = arg->arg[0]; - int width = 1; - if (arg->arg[1]) - width = atoi(&arg->arg[1]); - switch (base) { - case 'a': - if (width != 1) - error_exit("invalid width for ascii base"); - break; - case 'd': case 'x': case 'o': case'u': - if (width != 1 && width != 2 && width != 4 && width != 8) - error_exit("this system doesn't provide a %d-byte type", width); - break; - case 'f': - if (width != 4 && width != 8) - error_exit("this system doesn't provide a %d-byte floating point type", - width); - break; - default: - error_exit("invalid base '%c'", base); + 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 < 0 || 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; + } } - arg = arg->next; - }; + types[TT.types].type = type; + types[TT.types].size = size; + TT.types++; + } + + error_exit("bad -t %s", base); } void od_main(void) { - if (!TT.address_base) TT.address_base = "o"; + struct arg_list *arg; + + TT.buf = (char *)TT.bufs; + + 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_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.output_base) append_base("o2"); - valid_bases(); + loopfiles(toys.optargs, do_od); + + if (TT.leftover) od_outline(); + od_outline(); } |