od.c - sbase - suckless unix tools | |
git clone git://git.suckless.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 … | |
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 sk… | |
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 } |