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#o#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] [-o 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     -o n	Add n to display offset
     29     -p	Plain hexdump (30 bytes/line, no grouping)
     30     -r	Reverse operation: turn a hexdump into a binary file
     31     -s n	Skip to offset n
     32 */
     33 
     34 #define FOR_xxd
     35 #include "toys.h"
     36 
     37 GLOBALS(
     38   long s, g, o, l, c;
     39 )
     40 
     41 static void do_xxd(int fd, char *name)
     42 {
     43   long long pos = 0;
     44   long long limit = TT.l;
     45   int i, len, space;
     46 
     47   if (toys.optflags&FLAG_s) {
     48     xlseek(fd, TT.s, SEEK_SET);
     49     pos = TT.s;
     50     if (limit) limit += TT.s;
     51   }
     52 
     53   while (0<(len = readall(fd, toybuf,
     54                           (limit && limit-pos<TT.c)?limit-pos:TT.c))) {
     55     if (!(toys.optflags&FLAG_p)) printf("%08llx: ", TT.o + pos);
     56     pos += len;
     57     space = 2*TT.c+TT.c/TT.g+1;
     58 
     59     for (i=0; i<len;) {
     60       space -= printf("%02x", toybuf[i]);
     61       if (!(++i%TT.g)) {
     62         putchar(' ');
     63         space--;
     64       }
     65     }
     66 
     67     if (!(toys.optflags&FLAG_p)) {
     68       printf("%*s", space, "");
     69       for (i=0; i<len; i++)
     70         putchar((toybuf[i]>=' ' && toybuf[i]<='~') ? toybuf[i] : '.');
     71     }
     72     putchar('\n');
     73   }
     74   if (len<0) perror_exit("read");
     75 }
     76 
     77 static void do_xxd_include(int fd, char *name)
     78 {
     79   long long total = 0;
     80   int c = 1, i, len;
     81 
     82   // The original xxd outputs a header/footer if given a filename (not stdin).
     83   // We don't, which means that unlike the original we can implement -ri.
     84   while ((len = read(fd, toybuf, sizeof(toybuf))) > 0) {
     85     total += len;
     86     for (i = 0; i < len; ++i) {
     87       printf("%s%#.02x", c > 1 ? ", " : "  ", toybuf[i]);
     88       if (c++ == TT.c) {
     89         xprintf(",\n");
     90         c = 1;
     91       }
     92     }
     93   }
     94   if (len < 0) perror_msg_raw(name);
     95   if (c > 1) xputc('\n');
     96 }
     97 
     98 static int dehex(char ch)
     99 {
    100   if (ch >= '0' && ch <= '9') return ch - '0';
    101   if (ch >= 'a' && ch <= 'f') return ch - 'a' + 10;
    102   if (ch >= 'A' && ch <= 'F') return ch - 'A' + 10;
    103   return (ch == '\n') ? -2 : -1;
    104 }
    105 
    106 static void do_xxd_reverse(int fd, char *name)
    107 {
    108   FILE *fp = xfdopen(fd, "r");
    109   int tmp;
    110 
    111   if (toys.optflags&FLAG_i) {
    112     // -ri is a very easy special case.
    113     while (fscanf(fp, " 0x%02x,", &tmp) == 1) {
    114       fputc(tmp & 0xff, stdout);
    115     }
    116   } else {
    117     while (!feof(fp)) {
    118       int col = 0;
    119 
    120       // Each line of a regular hexdump starts with an offset/address.
    121       // Each line of a plain hexdump just goes straight into the bytes.
    122       if (!(toys.optflags&FLAG_p)) {
    123         long long pos;
    124 
    125         if (fscanf(fp, "%llx: ", &pos) == 1) {
    126           if (fseek(stdout, pos, SEEK_SET) != 0) {
    127             // TODO: just write out zeros if non-seekable?
    128             perror_exit("%s: seek failed", name);
    129           }
    130         }
    131       }
    132 
    133       // A plain hexdump can have as many bytes per line as you like,
    134       // but a non-plain hexdump assumes garbage after it's seen the
    135       // specified number of bytes.
    136       while (toys.optflags&FLAG_p || col < TT.c) {
    137         int n1, n2;
    138 
    139         // If we're at EOF or EOL or we read some non-hex...
    140         if ((n1 = n2 = dehex(fgetc(fp))) < 0 || (n2 = dehex(fgetc(fp))) < 0) {
    141           // If we're at EOL, start on that line.
    142           if (n1 == -2 || n2 == -2) continue;
    143           // Otherwise, skip to the next line.
    144           break;
    145         }
    146 
    147         fputc((n1 << 4) | (n2 & 0xf), stdout);
    148         col++;
    149 
    150         // Is there any grouping going on? Ignore a single space.
    151         tmp = fgetc(fp);
    152         if (tmp != ' ') ungetc(tmp, fp);
    153       }
    154 
    155       // Skip anything else on this line (such as the ASCII dump).
    156       while ((tmp = fgetc(fp)) != EOF && tmp != '\n')
    157         ;
    158     }
    159   }
    160 
    161   if (ferror(fp)) perror_msg_raw(name);
    162   fclose(fp);
    163 }
    164 
    165 void xxd_main(void)
    166 {
    167   if (TT.c < 0 || TT.c > 256) error_exit("invalid -c: %ld", TT.c);
    168   if (TT.c == 0) TT.c = (toys.optflags&FLAG_i)?12:16;
    169 
    170   // Plain style is 30 bytes/line, no grouping.
    171   if (toys.optflags&FLAG_p) TT.c = TT.g = 30;
    172 
    173   loopfiles(toys.optargs,
    174     toys.optflags&FLAG_r ? do_xxd_reverse
    175       : (toys.optflags&FLAG_i ? do_xxd_include : do_xxd));
    176 }
    177