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