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 }