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