mirror of
git://git.suckless.org/sbase
synced 2024-12-22 15:04:53 +00:00
d098ac4abc
This is the behavior specified by POSIX.
333 lines
6.2 KiB
C
333 lines
6.2 KiB
C
/* See LICENSE file for copyright and license details. */
|
|
#include <ctype.h>
|
|
#include <fcntl.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
|
|
#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 int big_endian;
|
|
|
|
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(const unsigned char *s, unsigned char format, size_t len)
|
|
{
|
|
long long res, basefac;
|
|
size_t i;
|
|
char fmt[] = " %#*ll#";
|
|
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",
|
|
};
|
|
|
|
switch (format) {
|
|
case 'a':
|
|
c = *s & ~128; /* clear high bit as required by standard */
|
|
if (c < LEN(namedict) || c == 127) {
|
|
printf(" %3s", (c == 127) ? "del" : namedict[c]);
|
|
} else {
|
|
printf(" %3c", c);
|
|
}
|
|
break;
|
|
case 'c':
|
|
if (strchr("\a\b\t\n\v\f\r\0", *s)) {
|
|
printf(" %3s", escdict[*s]);
|
|
} else if (!isprint(*s)) {
|
|
printf(" %3o", *s);
|
|
} else {
|
|
printf(" %3c", *s);
|
|
}
|
|
break;
|
|
default:
|
|
if (big_endian) {
|
|
for (res = 0, basefac = 1, i = len; i; i--) {
|
|
res += s[i - 1] * basefac;
|
|
basefac <<= 8;
|
|
}
|
|
} else {
|
|
for (res = 0, basefac = 1, i = 0; i < len; i++) {
|
|
res += s[i] * basefac;
|
|
basefac <<= 8;
|
|
}
|
|
}
|
|
fmt[2] = big_endian ? '-' : ' ';
|
|
fmt[6] = format;
|
|
printf(fmt, (int)(3 * len + len - 1), res);
|
|
}
|
|
}
|
|
|
|
static void
|
|
printline(const unsigned char *line, size_t len, off_t addr)
|
|
{
|
|
struct type *t = NULL;
|
|
size_t i;
|
|
int first = 1;
|
|
unsigned char *tmp;
|
|
|
|
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; i += MIN(len - i, t ? t->len : 4)) {
|
|
if (len - i < (t ? t->len : 4)) {
|
|
tmp = ecalloc(t ? t->len : 4, 1);
|
|
memcpy(tmp, line + i, len - i);
|
|
printchunk(tmp, t ? t->format : 'o',
|
|
t ? t->len : 4);
|
|
free(tmp);
|
|
} else {
|
|
printchunk(line + i, t ? t->format : 'o',
|
|
t ? t->len : 4);
|
|
}
|
|
}
|
|
fputc('\n', stdout);
|
|
if (TAILQ_EMPTY(&head) || (!len && !first))
|
|
break;
|
|
}
|
|
}
|
|
|
|
static int
|
|
od(int fd, char *fname, int last)
|
|
{
|
|
static unsigned char *line;
|
|
static size_t lineoff;
|
|
static off_t addr;
|
|
unsigned char buf[BUFSIZ];
|
|
size_t i, size = sizeof(buf);
|
|
ssize_t n;
|
|
|
|
while (skip - addr > 0) {
|
|
n = read(fd, buf, MIN(skip - addr, sizeof(buf)));
|
|
if (n < 0)
|
|
weprintf("read %s:", fname);
|
|
if (n <= 0)
|
|
return n;
|
|
addr += n;
|
|
}
|
|
if (!line)
|
|
line = emalloc(linelen);
|
|
|
|
for (;;) {
|
|
if (max >= 0)
|
|
size = MIN(max - (addr - skip), size);
|
|
if ((n = read(fd, buf, size)) <= 0)
|
|
break;
|
|
for (i = 0; i < n; i++, addr++) {
|
|
line[lineoff++] = buf[i];
|
|
if (lineoff == linelen) {
|
|
printline(line, lineoff, addr - lineoff + 1);
|
|
lineoff = 0;
|
|
}
|
|
}
|
|
}
|
|
if (n < 0) {
|
|
weprintf("read %s:", fname);
|
|
return n;
|
|
}
|
|
if (lineoff && last)
|
|
printline(line, lineoff, addr - lineoff);
|
|
if (last)
|
|
printline((unsigned char *)"", 0, addr);
|
|
return 0;
|
|
}
|
|
|
|
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
|
|
addtype(char format, int len)
|
|
{
|
|
struct type *t;
|
|
|
|
t = emalloc(sizeof(*t));
|
|
t->format = format;
|
|
t->len = len;
|
|
TAILQ_INSERT_TAIL(&head, t, entry);
|
|
}
|
|
|
|
static void
|
|
usage(void)
|
|
{
|
|
eprintf("usage: %s [-bdosvx] [-A addressformat] [-E | -e] [-j skip] "
|
|
"[-t outputformat] [file ...]\n", argv0);
|
|
}
|
|
|
|
int
|
|
main(int argc, char *argv[])
|
|
{
|
|
int fd;
|
|
struct type *t;
|
|
int ret = 0, len;
|
|
char *s;
|
|
|
|
big_endian = (*(uint16_t *)"\0\xff" == 0xff);
|
|
|
|
ARGBEGIN {
|
|
case 'A':
|
|
s = EARGF(usage());
|
|
if (strlen(s) != 1 || !strchr("doxn", s[0]))
|
|
usage();
|
|
addr_format = s[0];
|
|
break;
|
|
case 'b':
|
|
addtype('o', 1);
|
|
break;
|
|
case 'd':
|
|
addtype('u', 2);
|
|
break;
|
|
case 'E':
|
|
case 'e':
|
|
big_endian = (ARGC() == 'E');
|
|
break;
|
|
case 'j':
|
|
if ((skip = parseoffset(EARGF(usage()))) < 0)
|
|
usage();
|
|
break;
|
|
case 'N':
|
|
if ((max = parseoffset(EARGF(usage()))) < 0)
|
|
usage();
|
|
break;
|
|
case 'o':
|
|
addtype('o', 2);
|
|
break;
|
|
case 's':
|
|
addtype('d', 2);
|
|
break;
|
|
case 't':
|
|
s = EARGF(usage());
|
|
for (; *s; s++) {
|
|
switch (*s) {
|
|
case 'a':
|
|
case 'c':
|
|
addtype(*s, 1);
|
|
break;
|
|
case 'd':
|
|
case 'o':
|
|
case 'u':
|
|
case 'x':
|
|
/* todo: allow multiple digits */
|
|
if (*(s+1) > '0' && *(s+1) <= '9') {
|
|
len = *(s+1) - '0';
|
|
} else {
|
|
switch (*(s+1)) {
|
|
case 'C':
|
|
len = sizeof(char);
|
|
break;
|
|
case 'S':
|
|
len = sizeof(short);
|
|
break;
|
|
case 'I':
|
|
len = sizeof(int);
|
|
break;
|
|
case 'L':
|
|
len = sizeof(long);
|
|
break;
|
|
default:
|
|
len = sizeof(int);
|
|
}
|
|
}
|
|
addtype(*s++, len);
|
|
break;
|
|
default:
|
|
usage();
|
|
}
|
|
}
|
|
break;
|
|
case 'v':
|
|
/* always set - use uniq(1) to handle duplicate lines */
|
|
break;
|
|
case 'x':
|
|
addtype('x', 2);
|
|
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) {
|
|
if (od(0, "<stdin>", 1) < 0)
|
|
ret = 1;
|
|
} else {
|
|
for (; *argv; argc--, argv++) {
|
|
if (!strcmp(*argv, "-")) {
|
|
*argv = "<stdin>";
|
|
fd = 0;
|
|
} else if ((fd = open(*argv, O_RDONLY)) < 0) {
|
|
weprintf("open %s:", *argv);
|
|
ret = 1;
|
|
continue;
|
|
}
|
|
if (od(fd, *argv, (!*(argv + 1))) < 0)
|
|
ret = 1;
|
|
if (fd != 0)
|
|
close(fd);
|
|
}
|
|
}
|
|
|
|
ret |= fshut(stdout, "<stdout>") | fshut(stderr, "<stderr>");
|
|
|
|
return ret;
|
|
}
|