sbase

suckless unix tools
git clone git://git.2f30.org/sbase
Log | Files | Refs | README | LICENSE

od.c (6365B)


      1 /* See LICENSE file for copyright and license details. */
      2 #include <ctype.h>
      3 #include <fcntl.h>
      4 #include <stdint.h>
      5 #include <stdio.h>
      6 #include <stdlib.h>
      7 #include <string.h>
      8 #include <unistd.h>
      9 
     10 #include "queue.h"
     11 #include "util.h"
     12 
     13 struct type {
     14 	unsigned char     format;
     15 	unsigned int      len;
     16 	TAILQ_ENTRY(type) entry;
     17 };
     18 
     19 static TAILQ_HEAD(head, type) head = TAILQ_HEAD_INITIALIZER(head);
     20 static unsigned char addr_format = 'o';
     21 static off_t skip = 0;
     22 static off_t max = -1;
     23 static size_t linelen = 1;
     24 static int big_endian;
     25 
     26 static void
     27 printaddress(off_t addr)
     28 {
     29 	char fmt[] = "%07j#";
     30 
     31 	if (addr_format == 'n') {
     32 		fputc(' ', stdout);
     33 	} else {
     34 		fmt[4] = addr_format;
     35 		printf(fmt, (intmax_t)addr);
     36 	}
     37 }
     38 
     39 static void
     40 printchunk(const unsigned char *s, unsigned char format, size_t len)
     41 {
     42 	long long res, basefac;
     43 	size_t i;
     44 	char fmt[] = " %#*ll#";
     45 	unsigned char c;
     46 
     47 	const char *namedict[] = {
     48 		"nul", "soh", "stx", "etx", "eot", "enq", "ack",
     49 		"bel", "bs",  "ht",  "nl",  "vt",  "ff",  "cr",
     50 		"so",  "si",  "dle", "dc1", "dc2", "dc3", "dc4",
     51 		"nak", "syn", "etb", "can", "em",  "sub", "esc",
     52 		"fs",  "gs",  "rs",  "us",  "sp",
     53 	};
     54 	const char *escdict[] = {
     55 		['\0'] = "\\0", ['\a'] = "\\a",
     56 		['\b'] = "\\b", ['\t'] = "\\t",
     57 		['\n'] = "\\n", ['\v'] = "\\v",
     58 		['\f'] = "\\f", ['\r'] = "\\r",
     59 	};
     60 
     61 	switch (format) {
     62 	case 'a':
     63 		c = *s & ~128; /* clear high bit as required by standard */
     64 		if (c < LEN(namedict) || c == 127) {
     65 			printf(" %3s", (c == 127) ? "del" : namedict[c]);
     66 		} else {
     67 			printf(" %3c", c);
     68 		}
     69 		break;
     70 	case 'c':
     71 		if (strchr("\a\b\t\n\v\f\r\0", *s)) {
     72 			printf(" %3s", escdict[*s]);
     73 		} else if (!isprint(*s)) {
     74 			printf(" %3o", *s);
     75 		} else {
     76 			printf(" %3c", *s);
     77 		}
     78 		break;
     79 	default:
     80 		if (big_endian) {
     81 			for (res = 0, basefac = 1, i = len; i; i--) {
     82 				res += s[i - 1] * basefac;
     83 				basefac <<= 8;
     84 			}
     85 		} else {
     86 			for (res = 0, basefac = 1, i = 0; i < len; i++) {
     87 				res += s[i] * basefac;
     88 				basefac <<= 8;
     89 			}
     90 		}
     91 		fmt[2] = big_endian ? '-' : ' ';
     92 		fmt[6] = format;
     93 		printf(fmt, (int)(3 * len + len - 1), res);
     94 	}
     95 }
     96 
     97 static void
     98 printline(const unsigned char *line, size_t len, off_t addr)
     99 {
    100 	struct type *t = NULL;
    101 	size_t i;
    102 	int first = 1;
    103 	unsigned char *tmp;
    104 
    105 	if (TAILQ_EMPTY(&head))
    106 		goto once;
    107 	TAILQ_FOREACH(t, &head, entry) {
    108 once:
    109 		if (first) {
    110 			printaddress(addr);
    111 			first = 0;
    112 		} else {
    113 			printf("%*c", (addr_format == 'n') ? 1 : 7, ' ');
    114 		}
    115 		for (i = 0; i < len; i += MIN(len - i, t ? t->len : 4)) {
    116 			if (len - i < (t ? t->len : 4)) {
    117 				tmp = ecalloc(t ? t->len : 4, 1);
    118 				memcpy(tmp, line + i, len - i);
    119 				printchunk(tmp, t ? t->format : 'o',
    120 				           t ? t->len : 4);
    121 				free(tmp);
    122 			} else {
    123 				printchunk(line + i, t ? t->format : 'o',
    124 				           t ? t->len : 4);
    125 			}
    126 		}
    127 		fputc('\n', stdout);
    128 		if (TAILQ_EMPTY(&head) || (!len && !first))
    129 			break;
    130 	}
    131 }
    132 
    133 static int
    134 od(int fd, char *fname, int last)
    135 {
    136 	static unsigned char *line;
    137 	static size_t lineoff;
    138 	static off_t addr;
    139 	unsigned char buf[BUFSIZ];
    140 	size_t i, size = sizeof(buf);
    141 	ssize_t n;
    142 
    143 	while (skip - addr > 0) {
    144 		n = read(fd, buf, MIN(skip - addr, sizeof(buf)));
    145 		if (n < 0)
    146 			weprintf("read %s:", fname);
    147 		if (n <= 0)
    148 			return n;
    149 		addr += n;
    150 	}
    151 	if (!line)
    152 		line = emalloc(linelen);
    153 
    154 	for (;;) {
    155 		if (max >= 0)
    156 			size = MIN(max - (addr - skip), size);
    157 		if ((n = read(fd, buf, size)) <= 0)
    158 			break;
    159 		for (i = 0; i < n; i++, addr++) {
    160 			line[lineoff++] = buf[i];
    161 			if (lineoff == linelen) {
    162 				printline(line, lineoff, addr - lineoff + 1);
    163 				lineoff = 0;
    164 			}
    165 		}
    166 	}
    167 	if (n < 0) {
    168 		weprintf("read %s:", fname);
    169 		return n;
    170 	}
    171 	if (lineoff && last)
    172 		printline(line, lineoff, addr - lineoff);
    173 	if (last)
    174 		printline((unsigned char *)"", 0, addr);
    175 	return 0;
    176 }
    177 
    178 static int
    179 lcm(unsigned int a, unsigned int b)
    180 {
    181 	unsigned int c, d, e;
    182 
    183 	for (c = a, d = b; c ;) {
    184 		e = c;
    185 		c = d % c;
    186 		d = e;
    187 	}
    188 
    189 	return a / d * b;
    190 }
    191 
    192 static void
    193 addtype(char format, int len)
    194 {
    195 	struct type *t;
    196 
    197 	t = emalloc(sizeof(*t));
    198 	t->format = format;
    199 	t->len = len;
    200 	TAILQ_INSERT_TAIL(&head, t, entry);
    201 }
    202 
    203 static void
    204 usage(void)
    205 {
    206 	eprintf("usage: %s [-bdosvx] [-A addressformat] [-E | -e] [-j skip] "
    207 	        "[-t outputformat] [file ...]\n", argv0);
    208 }
    209 
    210 int
    211 main(int argc, char *argv[])
    212 {
    213 	int fd;
    214 	struct type *t;
    215 	int ret = 0, len;
    216 	char *s;
    217 
    218 	big_endian = (*(uint16_t *)"\0\xff" == 0xff);
    219 
    220 	ARGBEGIN {
    221 	case 'A':
    222 		s = EARGF(usage());
    223 		if (strlen(s) != 1 || !strchr("doxn", s[0]))
    224 			usage();
    225 		addr_format = s[0];
    226 		break;
    227 	case 'b':
    228 		addtype('o', 1);
    229 		break;
    230 	case 'd':
    231 		addtype('u', 2);
    232 		break;
    233 	case 'E':
    234 	case 'e':
    235 		big_endian = (ARGC() == 'E');
    236 		break;
    237 	case 'j':
    238 		if ((skip = parseoffset(EARGF(usage()))) < 0)
    239 			usage();
    240 		break;
    241 	case 'N':
    242 		if ((max = parseoffset(EARGF(usage()))) < 0)
    243 			usage();
    244 		break;
    245 	case 'o':
    246 		addtype('o', 2);
    247 		break;
    248 	case 's':
    249 		addtype('d', 2);
    250 		break;
    251 	case 't':
    252 		s = EARGF(usage());
    253 		for (; *s; s++) {
    254 			switch (*s) {
    255 			case 'a':
    256 			case 'c':
    257 				addtype(*s, 1);
    258 				break;
    259 			case 'd':
    260 			case 'o':
    261 			case 'u':
    262 			case 'x':
    263 				/* todo: allow multiple digits */
    264 				if (*(s+1) > '0' && *(s+1) <= '9') {
    265 					len = *(s+1) - '0';
    266 				} else {
    267 					switch (*(s+1)) {
    268 					case 'C':
    269 						len = sizeof(char);
    270 						break;
    271 					case 'S':
    272 						len = sizeof(short);
    273 						break;
    274 					case 'I':
    275 						len = sizeof(int);
    276 						break;
    277 					case 'L':
    278 						len = sizeof(long);
    279 						break;
    280 					default:
    281 						len = sizeof(int);
    282 					}
    283 				}
    284 				addtype(*s++, len);
    285 				break;
    286 			default:
    287 				usage();
    288 			}
    289 		}
    290 		break;
    291 	case 'v':
    292 		/* always set - use uniq(1) to handle duplicate lines */
    293 		break;
    294 	case 'x':
    295 		addtype('x', 2);
    296 		break;
    297 	default:
    298 		usage();
    299 	} ARGEND
    300 
    301 	/* line length is lcm of type lengths and >= 16 by doubling */
    302 	TAILQ_FOREACH(t, &head, entry)
    303 		linelen = lcm(linelen, t->len);
    304 	if (TAILQ_EMPTY(&head))
    305 		linelen = 16;
    306 	while (linelen < 16)
    307 		linelen *= 2;
    308 
    309 	if (!argc) {
    310 		if (od(0, "<stdin>", 1) < 0)
    311 			ret = 1;
    312 	} else {
    313 		for (; *argv; argc--, argv++) {
    314 			if (!strcmp(*argv, "-")) {
    315 				*argv = "<stdin>";
    316 				fd = 0;
    317 			} else if ((fd = open(*argv, O_RDONLY)) < 0) {
    318 				weprintf("open %s:", *argv);
    319 				ret = 1;
    320 				continue;
    321 			}
    322 			if (od(fd, *argv, (!*(argv + 1))) < 0)
    323 				ret = 1;
    324 			if (fd != 0)
    325 				close(fd);
    326 		}
    327 	}
    328 
    329 	ret |= fshut(stdout, "<stdout>") | fshut(stderr, "<stderr>");
    330 
    331 	return ret;
    332 }