1
0
mirror of git://git.suckless.org/sbase synced 2024-12-22 15:04:53 +00:00
sbase/od.c
Michael Forney d098ac4abc od: For the 'c' type, format non-printable charecters as octal
This is the behavior specified by POSIX.
2018-02-25 23:01:57 -08:00

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;
}