OSDN Git Service

Add xxd -i (and xxd -ri).
authorElliott Hughes <enh@google.com>
Sun, 24 Dec 2017 08:42:38 +0000 (00:42 -0800)
committerRob Landley <rob@landley.net>
Wed, 27 Dec 2017 04:28:10 +0000 (22:28 -0600)
The original xxd doesn't support -r with -i. The original also outputs
"unsigned char name[] = { ... };" for input other than stdin, but that
actually makes it less useful --- many languages support array
initializers, but far fewer support that exact declaration syntax.

Also fix the -c range checking and defaulting: ><= in the argument string
only works for floating point arguments.

Bug: http://b/64711851

tests/xxd.test
toys/other/xxd.c

index 5f43b8b..97634e8 100644 (file)
@@ -25,12 +25,15 @@ testing "-c 8 -g 4 file1" "xxd -c 8 -g 4 file1" \
 testing "-c 8 -g 3 file1" "xxd -c 8 -g 3 file1" \
     "00000000: 746869 732069 7320 this is \n00000008: 736f6d 652074 6578 some tex\n00000010: 740a               t.\n" "" ""
 
+testing "-i" "cat file1 | xxd -i -" "  0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x73, 0x6f, 0x6d, 0x65,\n  0x20, 0x74, 0x65, 0x78, 0x74, 0x0a\n" "" ""
+
 testing "-p" "xxd -p file1" "7468697320697320736f6d6520746578740a\n" "" ""
 
 testing "-s" "xxd -s 13 file1" "0000000d: 7465 7874 0a                             text.\n" "" ""
 
-testing "-r" "xxd file1 | xxd -r" "this is some text\n" "" ""
-testing "-r -p" "xxd -p file1 | xxd -r -p" "this is some text\n" "" ""
+testing "-r" "echo -e '    00000000: 7468 6973 2069 7320 736f 6d65 2074 6578  this is some tex\n00000010: 740a                                     t.' | xxd -r" "this is some text\n" "" ""
+SKIP_HOST=1 testing "-r -i" "echo -e '0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x73, 0x6f, 0x6d, 0x65,\n  0x20, 0x74, 0x65, 0x78, 0x74, 0x0a' | xxd -ri" "this is some text\n" "" ""
+testing "-r -p" "echo 7468697320697320736f6d6520746578740a | xxd -r -p" "this is some text\n" "" ""
 
 testing "-r garbage" "echo '0000: 68 65 6c6c 6fxxxx' | xxd -r -" "hello" "" ""
 
index 0ff5947..edc5772 100644 (file)
@@ -2,22 +2,28 @@
  *
  * Copyright 2015 The Android Open Source Project
  *
- * No obvious standard, output looks like:
- * 0000000: 4c69 6e75 7820 7665 7273 696f 6e20 332e  Linux version 3.
+ * No obvious standard.
+ * Regular output:
+ *   "00000000: 4c69 6e75 7820 7665 7273 696f 6e20 342e  Linux version 4."
+ * xxd -i "include" or "initializer" output:
+ *   "  0x4c, 0x69, 0x6e, 0x75, 0x78, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f,"
+ * xxd -p "plain" output:
+ *   "4c696e75782076657273696f6e20342e392e302d342d616d643634202864"
 
-USE_XXD(NEWTOY(xxd, ">1c#<1>4096=16l#g#<1=2prs#[!rs]", TOYFLAG_USR|TOYFLAG_BIN))
+USE_XXD(NEWTOY(xxd, ">1c#l#g#<1=2iprs#[!rs]", TOYFLAG_USR|TOYFLAG_BIN))
 
 config XXD
   bool "xxd"
   default y
   help
-    usage: xxd [-c n] [-g n] [-l n] [-p] [-r] [-s n] [file]
+    usage: xxd [-c n] [-g n] [-i] [-l n] [-p] [-r] [-s n] [file]
 
     Hexdump a file to stdout.  If no file is listed, copy from stdin.
     Filename "-" is a synonym for stdin.
 
     -c n       Show n bytes per line (default 16)
     -g n       Group bytes by adding a ' ' every n bytes (default 2)
+    -i Include file output format (comma-separated hex byte literals)
     -l n       Limit of n bytes before stopping (default is no limit)
     -p Plain hexdump (30 bytes/line, no grouping)
     -r Reverse operation: turn a hexdump into a binary file
@@ -70,6 +76,27 @@ static void do_xxd(int fd, char *name)
   if (len<0) perror_exit("read");
 }
 
+static void do_xxd_include(int fd, char *name)
+{
+  long long total = 0;
+  int c = 1, i, len;
+
+  // The original xxd outputs a header/footer if given a filename (not stdin).
+  // We don't, which means that unlike the original we can implement -ri.
+  while ((len = read(fd, toybuf, sizeof(toybuf))) > 0) {
+    total += len;
+    for (i = 0; i < len; ++i) {
+      printf("%s%#.02x", c > 1 ? ", " : "  ", toybuf[i]);
+      if (c++ == TT.c) {
+        xprintf(",\n");
+        c = 1;
+      }
+    }
+  }
+  if (len < 0) perror_msg_raw(name);
+  if (c > 1) xputc('\n');
+}
+
 static int dehex(char ch)
 {
   if (ch >= '0' && ch <= '9') return ch - '0';
@@ -81,58 +108,71 @@ static int dehex(char ch)
 static void do_xxd_reverse(int fd, char *name)
 {
   FILE *fp = xfdopen(fd, "r");
+  int tmp;
 
-  while (!feof(fp)) {
-    int col = 0;
-    int tmp;
-
-    // Each line of a non-plain hexdump starts with an offset/address.
-    if (!(toys.optflags&FLAG_p)) {
-      long long pos;
-
-      if (fscanf(fp, "%llx: ", &pos) == 1) {
-        if (fseek(stdout, pos, SEEK_SET) != 0) {
-          // TODO: just write out zeros if non-seekable?
-          perror_exit("%s: seek failed", name);
+  if (toys.optflags&FLAG_i) {
+    // -ri is a very easy special case.
+    while (fscanf(fp, " 0x%02x,", &tmp) == 1) {
+      fputc(tmp & 0xff, stdout);
+    }
+  } else {
+    while (!feof(fp)) {
+      int col = 0;
+
+      // Each line of a regular hexdump starts with an offset/address.
+      // Each line of a plain hexdump just goes straight into the bytes.
+      if (!(toys.optflags&FLAG_p)) {
+        long long pos;
+
+        if (fscanf(fp, "%llx: ", &pos) == 1) {
+          if (fseek(stdout, pos, SEEK_SET) != 0) {
+            // TODO: just write out zeros if non-seekable?
+            perror_exit("%s: seek failed", name);
+          }
         }
       }
-    }
 
-    // A plain hexdump can have as many bytes per line as you like,
-    // but a non-plain hexdump assumes garbage after it's seen the
-    // specified number of bytes.
-    while (toys.optflags&FLAG_p || col < TT.c) {
-      int n1, n2;
-
-      // If we're at EOF or EOL or we read some non-hex...
-      if ((n1 = n2 = dehex(fgetc(fp))) < 0 || (n2 = dehex(fgetc(fp))) < 0) {
-        // If we're at EOL, start on that line.
-        if (n1 == -2 || n2 == -2) continue;
-        // Otherwise, skip to the next line.
-        break;
-      }
+      // A plain hexdump can have as many bytes per line as you like,
+      // but a non-plain hexdump assumes garbage after it's seen the
+      // specified number of bytes.
+      while (toys.optflags&FLAG_p || col < TT.c) {
+        int n1, n2;
+
+        // If we're at EOF or EOL or we read some non-hex...
+        if ((n1 = n2 = dehex(fgetc(fp))) < 0 || (n2 = dehex(fgetc(fp))) < 0) {
+          // If we're at EOL, start on that line.
+          if (n1 == -2 || n2 == -2) continue;
+          // Otherwise, skip to the next line.
+          break;
+        }
 
-      fputc((n1 << 4) | (n2 & 0xf), stdout);
-      col++;
+        fputc((n1 << 4) | (n2 & 0xf), stdout);
+        col++;
 
-      // Is there any grouping going on? Ignore a single space.
-      tmp = fgetc(fp);
-      if (tmp != ' ') ungetc(tmp, fp);
-    }
+        // Is there any grouping going on? Ignore a single space.
+        tmp = fgetc(fp);
+        if (tmp != ' ') ungetc(tmp, fp);
+      }
 
-    // Skip anything else on this line (such as the ASCII dump).
-    while ((tmp = fgetc(fp)) != EOF && tmp != '\n')
-      ;
+      // Skip anything else on this line (such as the ASCII dump).
+      while ((tmp = fgetc(fp)) != EOF && tmp != '\n')
+        ;
+    }
   }
-  if (ferror(fp)) perror_msg_raw(name);
 
+  if (ferror(fp)) perror_msg_raw(name);
   fclose(fp);
 }
 
 void xxd_main(void)
 {
+  if (TT.c < 0 || TT.c > 256) error_exit("invalid -c: %ld", TT.c);
+  if (TT.c == 0) TT.c = (toys.optflags&FLAG_i)?12:16;
+
   // Plain style is 30 bytes/line, no grouping.
   if (toys.optflags&FLAG_p) TT.c = TT.g = 30;
 
-  loopfiles(toys.optargs, toys.optflags&FLAG_r ? do_xxd_reverse : do_xxd);
+  loopfiles(toys.optargs,
+    toys.optflags&FLAG_r ? do_xxd_reverse
+      : (toys.optflags&FLAG_i ? do_xxd_include : do_xxd));
 }