/* See LICENSE file for copyright and license details. */ #include #include #include #include #include #include "queue.h" #include "util.h" struct type { unsigned char format; unsigned int len; TAILQ_ENTRY(type) entry; }; static TAILQ_HEAD(head, type) head = TAILQ_HEAD_INITIALIZER(head); static unsigned char addr_format = 'o'; static off_t skip = 0; static off_t max = -1; static size_t linelen = 1; static void printaddress(off_t addr) { char fmt[] = "%07j#"; if (addr_format == 'n') { fputc(' ', stdout); } else { fmt[4] = addr_format; printf(fmt, (intmax_t)addr); } } static void printchunk(unsigned char *s, unsigned char format, size_t len) { long long res, basefac; size_t i; char fmt[] = " %0*ll#"; const char *namedict[] = { "nul", "soh", "stx", "etx", "eot", "enq", "ack", "bel", "bs", "ht", "nl", "vt", "ff", "cr", "so", "si", "dle", "dc1", "dc2", "dc3", "dc4", "nak", "syn", "etb", "can", "em", "sub", "esc", "fs", "gs", "rs", "us", "sp", }; const char *escdict[] = { ['\0'] = "\\0", ['\a'] = "\\a", ['\b'] = "\\b", ['\t'] = "\\t", ['\n'] = "\\n", ['\v'] = "\\v", ['\f'] = "\\f", ['\r'] = "\\r", }; switch (format) { case 'a': *s &= ~128; /* clear high bit as required by standard */ if (*s < LEN(namedict) || *s == 127) { printf(" %3s", (*s == 127) ? "del" : namedict[*s]); } else { printf(" %3c", *s); } break; case 'c': if (strchr("\a\b\t\n\v\f\r\0", *s)) { printf(" %3s", escdict[*s]); } else { printf(" %3c", *s); } break; default: res = 0; basefac = 1; #if __BYTE_ORDER == __BIG_ENDIAN for (i = len; i; i--) { res += s[i - 1] * basefac; #else for (i = 0; i < len; i++) { res += s[i] * basefac; #endif basefac <<= 8; } fmt[6] = format; printf(fmt, (int)(3 * len + len - 1), res); } } static void printline(unsigned char *line, size_t len, off_t addr) { struct type *t = NULL; size_t i; int first = 1; if (TAILQ_EMPTY(&head)) goto once; TAILQ_FOREACH(t, &head, entry) { once: if (first) { printaddress(addr); first = 0; } else { printf("%*c", (addr_format == 'n') ? 1 : 7, ' '); } for (i = 0; i < len; ) { printchunk(line + i, t ? t->format : 'o', MIN(len - i, t ? t->len : 4)); i += MIN(len - i, t ? t->len : 4); } fputc('\n', stdout); if (TAILQ_EMPTY(&head) || (!len && !first)) break; } } static void od(FILE *fp, char *fname, int last) { static unsigned char *line; static size_t lineoff; size_t i; unsigned char buf[BUFSIZ]; static off_t addr; size_t buflen; while (skip - addr) { buflen = fread(buf, 1, MIN(skip - addr, BUFSIZ), fp); addr += buflen; if (feof(fp) || ferror(fp)) return; } if (!line) line = emalloc(linelen); while ((buflen = fread(buf, 1, max >= 0 ? max - (addr - skip) : BUFSIZ, fp))) { for (i = 0; i < buflen; i++, addr++) { line[lineoff++] = buf[i]; if (lineoff == linelen) { printline(line, lineoff, addr - lineoff + 1); lineoff = 0; } } } if (lineoff) printline(line, lineoff, addr - lineoff); printline((unsigned char *)"", 0, addr); } static int lcm(unsigned int a, unsigned int b) { unsigned int c, d, e; for (c = a, d = b; c ;) { e = c; c = d % c; d = e; } return a / d * b; } static void usage(void) { eprintf("usage: %s", argv0); } int main(int argc, char *argv[]) { FILE *fp; struct type *t; int ret = 0; char *s; ARGBEGIN { case 'A': s = EARGF(usage()); if (strlen(s) != 1 || !strchr("doxn", s[0])) usage(); addr_format = s[0]; break; case 'j': if ((skip = parseoffset(EARGF(usage()))) < 0) usage(); break; case 'N': if ((max = parseoffset(EARGF(usage()))) < 0) usage(); break; case 't': s = EARGF(usage()); for (; *s; s++) { t = emalloc(sizeof(struct type)); switch (*s) { case 'a': case 'c': t->format = *s; t->len = 1; TAILQ_INSERT_TAIL(&head, t, entry); break; case 'd': case 'o': case 'u': case 'x': t->format = *s; /* todo: allow multiple digits */ if (*(s+1) > '0' || *(s+1) <= '9') { t->len = *(s+1) - '0'; s++; } else { switch (*(s + 1)) { case 'C': t->len = sizeof(char); break; case 'S': t->len = sizeof(short); break; case 'I': t->len = sizeof(int); break; case 'L': t->len = sizeof(long); break; default: t->len = 4; } } TAILQ_INSERT_TAIL(&head, t, entry); break; default: usage(); } } break; case 'v': /* always set - use uniq(1) to handle duplicate lines */ break; default: usage(); } ARGEND; /* line length is lcm of type lengths and >= 16 by doubling */ TAILQ_FOREACH(t, &head, entry) linelen = lcm(linelen, t->len); if (TAILQ_EMPTY(&head)) linelen = 16; while (linelen < 16) linelen *= 2; if (!argc) { od(stdin, "", 1); } else { for (; *argv; argc--, argv++) { if (!strcmp(*argv, "-")) { *argv = ""; fp = stdin; } else if (!(fp = fopen(*argv, "r"))) { weprintf("fopen %s:", *argv); ret = 1; continue; } od(fp, *argv, (!*(argv + 1))); if (fp != stdin && fshut(fp, *argv)) ret = 1; } } ret |= fshut(stdin, "") | fshut(stdout, "") | fshut(stderr, ""); return ret; }