Home | History | Annotate | Download | only in other
      1 /* xxd.c - hexdump.
      2  *
      3  * Copyright 2015 The Android Open Source Project
      4  *
      5  * No obvious standard.
      6  * Regular output:
      7  *   "00000000: 4c69 6e75 7820 7665 7273 696f 6e20 342e  Linux version 4."
      8  * xxd -i "include" or "initializer" output:
      9  *   "  0x4c, 0x69, 0x6e, 0x75, 0x78, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f,"
     10  * xxd -p "plain" output:
     11  *   "4c696e75782076657273696f6e20342e392e302d342d616d643634202864"
     12 
     13 USE_XXD(NEWTOY(xxd, ">1c#l#g#<1=2iprs#[!rs]", TOYFLAG_USR|TOYFLAG_BIN))
     14 
     15 config XXD
     16   bool "xxd"
     17   default y
     18   help
     19     usage: xxd [-c n] [-g n] [-i] [-l n] [-p] [-r] [-s n] [file]
     20 
     21     Hexdump a file to stdout.  If no file is listed, copy from stdin.
     22     Filename "-" is a synonym for stdin.
     23 
     24     -c n	Show n bytes per line (default 16)
     25     -g n	Group bytes by adding a ' ' every n bytes (default 2)
     26     -i	Include file output format (comma-separated hex byte literals)
     27     -l n	Limit of n bytes before stopping (default is no limit)
     28     -p	Plain hexdump (30 bytes/line, no grouping)
     29     -r	Reverse operation: turn a hexdump into a binary file
     30     -s n	Skip to offset n
     31 */
     32 
     33 #define FOR_xxd
     34 #include "toys.h"
     35 
     36 GLOBALS(
     37   long s;
     38   long g;
     39   long l;
     40   long c;
     41 )
     42 
     43 static void do_xxd(int fd, char *name)
     44 {
     45   long long pos = 0;
     46   long long limit = TT.l;
     47   int i, len, space;
     48 
     49   if (toys.optflags&FLAG_s) {
     50     xlseek(fd, TT.s, SEEK_SET);
     51     pos = TT.s;
     52     if (limit) limit += TT.s;
     53   }
     54 
     55   while (0<(len = readall(fd, toybuf,
     56                           (limit && limit-pos<TT.c)?limit-pos:TT.c))) {
     57     if (!(toys.optflags&FLAG_p)) printf("%08llx: ", pos);
     58     pos += len;
     59     space = 2*TT.c+TT.c/TT.g+1;
     60 
     61     for (i=0; i<len;) {
     62       space -= printf("%02x", toybuf[i]);
     63       if (!(++i%TT.g)) {
     64         putchar(' ');
     65         space--;
     66       }
     67     }
     68 
     69     if (!(toys.optflags&FLAG_p)) {
     70       printf("%*s", space, "");
     71       for (i=0; i<len; i++)
     72         putchar((toybuf[i]>=' ' && toybuf[i]<='~') ? toybuf[i] : '.');
     73     }
     74     putchar('\n');
     75   }
     76   if (len<0) perror_exit("read");
     77 }
     78 
     79 static void do_xxd_include(int fd, char *name)
     80 {
     81   long long total = 0;
     82   int c = 1, i, len;
     83 
     84   // The original xxd outputs a header/footer if given a filename (not stdin).
     85   // We don't, which means that unlike the original we can implement -ri.
     86   while ((len = read(fd, toybuf, sizeof(toybuf))) > 0) {
     87     total += len;
     88     for (i = 0; i < len; ++i) {
     89       printf("%s%#.02x", c > 1 ? ", " : "  ", toybuf[i]);
     90       if (c++ == TT.c) {
     91         xprintf(",\n");
     92         c = 1;
     93       }
     94     }
     95   }
     96   if (len < 0) perror_msg_raw(name);
     97   if (c > 1) xputc('\n');
     98 }
     99 
    100 static int dehex(char ch)
    101 {
    102   if (ch >= '0' && ch <= '9') return ch - '0';
    103   if (ch >= 'a' && ch <= 'f') return ch - 'a' + 10;
    104   if (ch >= 'A' && ch <= 'F') return ch - 'a' + 10;
    105   return (ch == '\n') ? -2 : -1;
    106 }
    107 
    108 static void do_xxd_reverse(int fd, char *name)
    109 {
    110   FILE *fp = xfdopen(fd, "r");
    111   int tmp;
    112 
    113   if (toys.optflags&FLAG_i) {
    114     // -ri is a very easy special case.
    115     while (fscanf(fp, " 0x%02x,", &tmp) == 1) {
    116       fputc(tmp & 0xff, stdout);
    117     }
    118   } else {
    119     while (!feof(fp)) {
    120       int col = 0;
    121 
    122       // Each line of a regular hexdump starts with an offset/address.
    123       // Each line of a plain hexdump just goes straight into the bytes.
    124       if (!(toys.optflags&FLAG_p)) {
    125         long long pos;
    126 
    127         if (fscanf(fp, "%llx: ", &pos) == 1) {
    128           if (fseek(stdout, pos, SEEK_SET) != 0) {
    129             // TODO: just write out zeros if non-seekable?
    130             perror_exit("%s: seek failed", name);
    131           }
    132         }
    133       }
    134 
    135       // A plain hexdump can have as many bytes per line as you like,
    136       // but a non-plain hexdump assumes garbage after it's seen the
    137       // specified number of bytes.
    138       while (toys.optflags&FLAG_p || col < TT.c) {
    139         int n1, n2;
    140 
    141         // If we're at EOF or EOL or we read some non-hex...
    142         if ((n1 = n2 = dehex(fgetc(fp))) < 0 || (n2 = dehex(fgetc(fp))) < 0) {
    143           // If we're at EOL, start on that line.
    144           if (n1 == -2 || n2 == -2) continue;
    145           // Otherwise, skip to the next line.
    146           break;
    147         }
    148 
    149         fputc((n1 << 4) | (n2 & 0xf), stdout);
    150         col++;
    151 
    152         // Is there any grouping going on? Ignore a single space.
    153         tmp = fgetc(fp);
    154         if (tmp != ' ') ungetc(tmp, fp);
    155       }
    156 
    157       // Skip anything else on this line (such as the ASCII dump).
    158       while ((tmp = fgetc(fp)) != EOF && tmp != '\n')
    159         ;
    160     }
    161   }
    162 
    163   if (ferror(fp)) perror_msg_raw(name);
    164   fclose(fp);
    165 }
    166 
    167 void xxd_main(void)
    168 {
    169   if (TT.c < 0 || TT.c > 256) error_exit("invalid -c: %ld", TT.c);
    170   if (TT.c == 0) TT.c = (toys.optflags&FLAG_i)?12:16;
    171 
    172   // Plain style is 30 bytes/line, no grouping.
    173   if (toys.optflags&FLAG_p) TT.c = TT.g = 30;
    174 
    175   loopfiles(toys.optargs,
    176     toys.optflags&FLAG_r ? do_xxd_reverse
    177       : (toys.optflags&FLAG_i ? do_xxd_include : do_xxd));
    178 }
    179