98e7e75ec6
Yeah, if the skipping is longer than the file itself, we need to take the skip value, not the address. Also, only print the last newline when we've actually printed at least 1 address.
164 lines
3.3 KiB
C
164 lines
3.3 KiB
C
/* See LICENSE file for copyright and license details. */
|
|
#include <ctype.h>
|
|
#include <inttypes.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "util.h"
|
|
|
|
static size_t bytes_per_line = 16;
|
|
static off_t maxbytes = -1;
|
|
static off_t skip = 0;
|
|
static unsigned char radix = 'o';
|
|
static unsigned char type = 'o';
|
|
|
|
static void
|
|
printaddress(FILE *f, off_t addr)
|
|
{
|
|
char fmt[] = "%07j# ";
|
|
|
|
if (radix == 'n') {
|
|
fputc(' ', f);
|
|
} else {
|
|
fmt[4] = radix;
|
|
fprintf(f, fmt, (intmax_t)addr);
|
|
}
|
|
}
|
|
|
|
static void
|
|
printchar(FILE *f, unsigned char c)
|
|
{
|
|
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",
|
|
};
|
|
const char *fmtdict[] = {
|
|
['d'] = "%4hhd ", ['o'] = "%03hho ",
|
|
['u'] = "%3hhu ", ['x'] = "%02hhx ",
|
|
};
|
|
|
|
switch (type) {
|
|
case 'a':
|
|
c &= ~128; /* clear high bit as required by standard */
|
|
if (c < LEN(namedict) || c == 127) {
|
|
fprintf(f, "%3s ", (c == 127) ? "del" : namedict[c]);
|
|
} else {
|
|
fprintf(f, "%3c ", c);
|
|
}
|
|
break;
|
|
case 'c':
|
|
if (strchr("\a\b\t\n\b\f\r\0", c)) {
|
|
fprintf(f, "%3s ", escdict[c]);
|
|
} else {
|
|
fprintf(f, "%3c ", c);
|
|
}
|
|
break;
|
|
default:
|
|
fprintf(f, fmtdict[type], c);
|
|
}
|
|
}
|
|
|
|
static void
|
|
od(FILE *in, char *in_name, FILE *out, char *out_name)
|
|
{
|
|
off_t addr;
|
|
size_t i, chunklen;
|
|
unsigned char buf[BUFSIZ];
|
|
|
|
for (addr = 0; (chunklen = fread(buf, 1, BUFSIZ, in)); ) {
|
|
for (i = 0; i < chunklen && (maxbytes == -1 ||
|
|
(addr - skip) < maxbytes); ++i, ++addr) {
|
|
if (addr - skip < 0)
|
|
continue;
|
|
if (((addr - skip) % bytes_per_line) == 0) {
|
|
if (addr - skip)
|
|
fputc('\n', out);
|
|
printaddress(out, addr);
|
|
}
|
|
printchar(out, buf[i]);
|
|
}
|
|
if (feof(in) || ferror(in) || ferror(out))
|
|
break;
|
|
}
|
|
if (addr - skip > 0)
|
|
fputc('\n', out);
|
|
if (radix != 'n') {
|
|
printaddress(out, MAX(addr, skip));
|
|
fputc('\n', out);
|
|
}
|
|
}
|
|
|
|
static void
|
|
usage(void)
|
|
{
|
|
eprintf("usage: %s [-A d|o|x|n] [-t a|c|d|o|u|x] [-v] [file ...]\n", argv0);
|
|
}
|
|
|
|
int
|
|
main(int argc, char *argv[])
|
|
{
|
|
FILE *fp;
|
|
int ret = 0;
|
|
char *s;
|
|
|
|
ARGBEGIN {
|
|
case 'A':
|
|
s = EARGF(usage());
|
|
if (strlen(s) != 1 || !strchr("doxn", s[0]))
|
|
usage();
|
|
radix = s[0];
|
|
break;
|
|
case 'j':
|
|
if ((skip = parseoffset(EARGF(usage()))) < 0)
|
|
return 1;
|
|
break;
|
|
case 'N':
|
|
if ((maxbytes = parseoffset(EARGF(usage()))) < 0)
|
|
return 1;
|
|
break;
|
|
case 't':
|
|
s = EARGF(usage());
|
|
if (strlen(s) != 1 || !strchr("acdoux", s[0]))
|
|
usage();
|
|
type = s[0];
|
|
break;
|
|
case 'v':
|
|
/* Always set. Use "uniq -f 1 -c" to handle duplicate lines. */
|
|
break;
|
|
default:
|
|
usage();
|
|
} ARGEND;
|
|
|
|
if (!argc) {
|
|
od(stdin, "<stdin>", stdout, "<stdout>");
|
|
} else {
|
|
for (; *argv; argc--, argv++) {
|
|
if (!strcmp(*argv, "-")) {
|
|
*argv = "<stdin>";
|
|
fp = stdin;
|
|
} else if (!(fp = fopen(*argv, "r"))) {
|
|
weprintf("fopen %s:", *argv);
|
|
ret = 1;
|
|
continue;
|
|
}
|
|
od(fp, *argv, stdout, "<stdout>");
|
|
if (fp != stdin && fshut(fp, *argv))
|
|
ret = 1;
|
|
}
|
|
}
|
|
|
|
ret |= fshut(stdin, "<stdin>") | fshut(stdout, "<stdout>") |
|
|
fshut(stderr, "<stderr>");
|
|
|
|
return ret;
|
|
}
|