od.c - sbase - suckless unix tools
(HTM) git clone git://git.suckless.org/sbase
(DIR) Log
(DIR) Files
(DIR) Refs
(DIR) README
(DIR) LICENSE
---
od.c (6363B)
---
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 }