1 /* od.c - Provide octal/hex dumps of data 2 * 3 * Copyright 2012 Andre Renaud <andre (at) bluewatersys.com> 4 * Copyright 2012 Rob Landley <rob (at) landley.net> 5 * 6 * See http://opengroup.org/onlinepubs/9699919799/utilities/od.html 7 8 USE_OD(NEWTOY(od, "j#vw#<1=16N#xsodcbA:t*", TOYFLAG_USR|TOYFLAG_BIN)) 9 10 config OD 11 bool "od" 12 default y 13 help 14 usage: od [-bcdosxv] [-j #] [-N #] [-w #] [-A doxn] [-t acdfoux[#]] 15 16 -A Address base (decimal, octal, hexdecimal, none) 17 -j Skip this many bytes of input 18 -N Stop dumping after this many bytes 19 -t Output type a(scii) c(har) d(ecimal) f(loat) o(ctal) u(nsigned) (he)x 20 plus optional size in bytes 21 aliases: -b=-t o1, -c=-t c, -d=-t u2, -o=-t o2, -s=-t d2, -x=-t x2 22 -v Don't collapse repeated lines together 23 -w Total line width in bytes (default 16) 24 */ 25 26 #define FOR_od 27 #include "toys.h" 28 29 GLOBALS( 30 struct arg_list *output_base; 31 char *address_base; 32 long max_count; 33 long width; 34 long jump_bytes; 35 36 int address_idx; 37 unsigned types, leftover, star; 38 char *buf; // Points to buffers[0] or buffers[1]. 39 char *bufs[2]; // Used to detect duplicate lines. 40 off_t pos; 41 ) 42 43 static char *ascii = "nulsohstxetxeotenqackbel bs ht nl vt ff cr so si" 44 "dledc1dc2dc3dc4naksynetbcan emsubesc fs gs rs us sp"; 45 46 struct odtype { 47 int type; 48 int size; 49 }; 50 51 static int od_out_t(struct odtype *t, char *buf, int *offset) 52 { 53 unsigned k; 54 int throw = 0, pad = 0; 55 56 // Handle ascii 57 if (t->type < 2) { 58 char c = TT.buf[(*offset)++]; 59 pad += 4; 60 61 if (!t->type) { 62 c &= 127; 63 if (c<=32) sprintf(buf, "%.3s", ascii+(3*c)); 64 else if (c==127) strcpy(buf, "del"); 65 else sprintf(buf, "%c", c); 66 } else { 67 char *bfnrtav = "\b\f\n\r\t\a\v", *s = strchr(bfnrtav, c); 68 if (s) sprintf(buf, "\\%c", "bfnrtav0"[s-bfnrtav]); 69 else if (c < 32 || c >= 127) sprintf(buf, "%03o", c); 70 else { 71 // TODO: this should be UTF8 aware. 72 sprintf(buf, "%c", c); 73 } 74 } 75 } else if (CFG_TOYBOX_FLOAT && t->type == 6) { 76 long double ld; 77 union {float f; double d; long double ld;} fdl; 78 79 memcpy(&fdl, TT.buf+*offset, t->size); 80 *offset += t->size; 81 if (sizeof(float) == t->size) { 82 ld = fdl.f; 83 pad += (throw = 8)+7; 84 } else if (sizeof(double) == t->size) { 85 ld = fdl.d; 86 pad += (throw = 17)+8; 87 } else if (sizeof(long double) == t->size) { 88 ld = fdl.ld; 89 pad += (throw = 21)+9; 90 } else error_exit("bad -tf '%d'", t->size); 91 92 sprintf(buf, "%.*Le", throw, ld); 93 // Integer types 94 } else { 95 unsigned long long ll = 0, or; 96 char *c[] = {"%*lld", "%*llu", "%0*llo", "%0*llx"}, 97 *class = c[t->type-2]; 98 99 // Work out width of field 100 if (t->size == 8) { 101 or = -1LL; 102 if (t->type == 2) or >>= 1; 103 } else or = (1LL<<(8*t->size))-1; 104 throw = sprintf(buf, class, 0, or); 105 106 // Accumulate integer based on size argument 107 for (k=0; k < t->size; k++) { 108 or = TT.buf[(*offset)++]; 109 ll |= or << (8*(IS_BIG_ENDIAN ? t->size-k-1 : k)); 110 } 111 112 // Handle negative values 113 if (t->type == 2) { 114 or = sizeof(or) - t->size; 115 throw++; 116 if (or && (ll & (1l<<((8*t->size)-1)))) 117 ll |= ((or<<(8*or))-1) << (8*t->size); 118 } 119 120 sprintf(buf, class, throw, ll); 121 pad += throw+1; 122 } 123 124 return pad; 125 } 126 127 static void od_outline(void) 128 { 129 unsigned flags = toys.optflags; 130 char buf[128], *abases[] = {"", "%07lld", "%07llo", "%06llx"}; 131 struct odtype *types = (struct odtype *)toybuf; 132 int i, j, len, pad; 133 134 if (TT.leftover<TT.width) memset(TT.buf+TT.leftover, 0, TT.width-TT.leftover); 135 136 // Handle duplciate lines as * 137 if (!(flags&FLAG_v) && TT.jump_bytes != TT.pos && TT.leftover 138 && !memcmp(TT.bufs[0], TT.bufs[1], TT.width)) 139 { 140 if (!TT.star) { 141 xputs("*"); 142 TT.star++; 143 } 144 145 // Print line position 146 } else { 147 TT.star = 0; 148 149 // off_t varies so expand it to largest possible size 150 xprintf(abases[TT.address_idx], (long long)TT.pos); 151 if (!TT.leftover) { 152 if (TT.address_idx) xputc('\n'); 153 return; 154 } 155 } 156 157 TT.pos += len = TT.leftover; 158 TT.leftover = 0; 159 if (TT.star) return; 160 161 // Find largest "pad" of the output types. 162 for (i = pad = 0; i<TT.types; i++) { 163 int bytes = 0; 164 165 // If more than one byte of input consumed, average rounding up. 166 j = od_out_t(types+i, buf, &bytes); 167 j = (j+bytes-1)/bytes; 168 169 if (j > pad) pad = j; 170 } 171 172 // For each output type, print one line 173 for (i=0; i<TT.types; i++) { 174 for (j = 0; j<len;) { 175 int bytes = j; 176 177 // pad for as many bytes as were consumed, and indent non-numbered lines 178 od_out_t(types+i, buf, &bytes); 179 xprintf("%*s", pad*(bytes-j) + 7*(!!i)*!j, buf); 180 j = bytes; 181 } 182 xputc('\n'); 183 } 184 185 // Toggle buffer for "same as last time" check. 186 TT.buf = (TT.buf == TT.bufs[0]) ? TT.bufs[1] : TT.bufs[0]; 187 } 188 189 // Loop through input files 190 static void do_od(int fd, char *name) 191 { 192 // Skip input, possibly more than one entire file. 193 if (TT.jump_bytes > TT.pos) { 194 off_t pos = TT.jump_bytes-TT.pos, off = lskip(fd, pos); 195 196 if (off >= 0) TT.pos += pos-off; 197 if (TT.jump_bytes > TT.pos) return; 198 } 199 200 for(;;) { 201 char *buf = TT.buf + TT.leftover; 202 int len = TT.width - TT.leftover; 203 204 if (toys.optflags & FLAG_N) { 205 if (!TT.max_count) break; 206 if (TT.max_count < len) len = TT.max_count; 207 } 208 209 len = readall(fd, buf, len); 210 if (len < 0) { 211 perror_msg_raw(name); 212 break; 213 } 214 if (TT.max_count) TT.max_count -= len; 215 TT.leftover += len; 216 if (TT.leftover < TT.width) break; 217 218 od_outline(); 219 } 220 } 221 222 // Handle one -t argument (including implicit ones) 223 static void append_base(char *base) 224 { 225 char *s = base; 226 struct odtype *types = (struct odtype *)toybuf; 227 int type; 228 229 for (;;) { 230 int size = 1; 231 232 if (!*s) return; 233 if (TT.types >= sizeof(toybuf)/sizeof(struct odtype)) break; 234 if (-1 == (type = stridx("acduox"USE_TOYBOX_FLOAT("f"), *(s++)))) break; 235 236 if (isdigit(*s)) { 237 size = strtol(s, &s, 10); 238 if (type < 2 && size != 1) break; 239 if (CFG_TOYBOX_FLOAT && type == 6 && size == sizeof(long double)); 240 else if (size < 1 || size > 8) break; 241 } else if (CFG_TOYBOX_FLOAT && type == 6) { 242 int sizes[] = {sizeof(float), sizeof(double), sizeof(long double)}; 243 if (-1 == (size = stridx("FDL", *s))) size = sizeof(double); 244 else { 245 s++; 246 size = sizes[size]; 247 } 248 } else if (type > 1) { 249 if (-1 == (size = stridx("CSIL", *s))) size = 4; 250 else { 251 s++; 252 size = 1 << size; 253 } 254 } 255 256 types[TT.types].type = type; 257 types[TT.types].size = size; 258 TT.types++; 259 } 260 261 error_exit("bad -t %s", base); 262 } 263 264 void od_main(void) 265 { 266 struct arg_list *arg; 267 268 TT.bufs[0] = xzalloc(TT.width); 269 TT.bufs[1] = xzalloc(TT.width); 270 TT.buf = TT.bufs[0]; 271 272 if (!TT.address_base) TT.address_idx = 2; 273 else if (0>(TT.address_idx = stridx("ndox", *TT.address_base))) 274 error_exit("bad -A '%c'", *TT.address_base); 275 276 // Collect -t entries 277 278 for (arg = TT.output_base; arg; arg = arg->next) append_base(arg->arg); 279 if (toys.optflags & FLAG_b) append_base("o1"); 280 if (toys.optflags & FLAG_c) append_base("c"); 281 if (toys.optflags & FLAG_d) append_base("u2"); 282 if (toys.optflags & FLAG_o) append_base("o2"); 283 if (toys.optflags & FLAG_s) append_base("d2"); 284 if (toys.optflags & FLAG_x) append_base("x2"); 285 if (!TT.types) append_base("o2"); 286 287 loopfiles(toys.optargs, do_od); 288 289 if (TT.leftover) od_outline(); 290 od_outline(); 291 292 if (CFG_TOYBOX_FREE) { 293 free(TT.bufs[0]); 294 free(TT.bufs[1]); 295 } 296 } 297