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}