xxd.c 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  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. USE_XXD(NEWTOY(xxd, ">1c#<0>256l#o#g#<1=2iprs#[!rs]", TOYFLAG_USR|TOYFLAG_BIN))
  13. config XXD
  14. bool "xxd"
  15. default y
  16. help
  17. usage: xxd [-c n] [-g n] [-i] [-l n] [-o n] [-p] [-r] [-s n] [file]
  18. Hexdump a file to stdout. If no file is listed, copy from stdin.
  19. Filename "-" is a synonym for stdin.
  20. -c n Show n bytes per line (default 16)
  21. -g n Group bytes by adding a ' ' every n bytes (default 2)
  22. -i Output include file (CSV hex bytes, plus C header/footer if not stdin)
  23. -l n Limit of n bytes before stopping (default is no limit)
  24. -o n Add n to display offset
  25. -p Plain hexdump (30 bytes/line, no grouping)
  26. -r Reverse operation: turn a hexdump into a binary file
  27. -s n Skip to offset n
  28. */
  29. #define FOR_xxd
  30. #include "toys.h"
  31. GLOBALS(
  32. long s, g, o, l, c;
  33. )
  34. static void do_xxd(int fd, char *name)
  35. {
  36. long long pos = 0;
  37. long long limit = TT.l;
  38. int i, len, space;
  39. if (FLAG(s)) {
  40. xlseek(fd, TT.s, SEEK_SET);
  41. pos = TT.s;
  42. if (limit) limit += TT.s;
  43. }
  44. while (0<(len = readall(fd, toybuf,
  45. (limit && limit-pos<TT.c)?limit-pos:TT.c)))
  46. {
  47. if (!FLAG(p)) printf("%08llx: ", TT.o + pos);
  48. pos += len;
  49. space = 2*TT.c+TT.c/TT.g+1;
  50. for (i=0; i<len;) {
  51. space -= printf("%02x", toybuf[i]);
  52. if (!(++i%TT.g)) {
  53. putchar(' ');
  54. space--;
  55. }
  56. }
  57. if (!FLAG(p)) {
  58. printf("%*s", space, "");
  59. for (i=0; i<len; i++)
  60. putchar((toybuf[i]>=' ' && toybuf[i]<='~') ? toybuf[i] : '.');
  61. }
  62. putchar('\n');
  63. }
  64. if (len<0) perror_exit("read");
  65. }
  66. static void do_xxd_include(int fd, char *name)
  67. {
  68. int c = 1, i, len;
  69. // The original xxd outputs a header/footer if given a filename (not stdin).
  70. // We don't, which means that unlike the original we can implement -ri.
  71. while ((len = read(fd, toybuf, sizeof(toybuf))) > 0) {
  72. for (i = 0; i < len; ++i) {
  73. printf("%s%#.02x", c > 1 ? ", " : " ", toybuf[i]);
  74. if (c++ == TT.c) {
  75. xprintf(",\n");
  76. c = 1;
  77. }
  78. }
  79. }
  80. if (len < 0) perror_msg_raw(name);
  81. if (c > 1) xputc('\n');
  82. }
  83. static int dehex(char ch)
  84. {
  85. if (ch >= '0' && ch <= '9') return ch - '0';
  86. if (ch >= 'a' && ch <= 'f') return ch - 'a' + 10;
  87. if (ch >= 'A' && ch <= 'F') return ch - 'A' + 10;
  88. return (ch == '\n') ? -2 : -1;
  89. }
  90. static void do_xxd_reverse(int fd, char *name)
  91. {
  92. FILE *fp = xfdopen(fd, "r");
  93. int tmp;
  94. // -ri is a very easy special case.
  95. if (FLAG(i)) while (fscanf(fp, " 0x%02x,", &tmp) == 1) xputc(tmp);
  96. else while (!feof(fp)) {
  97. int col = 0;
  98. // Each line of a regular hexdump starts with an offset/address.
  99. // Each line of a plain hexdump just goes straight into the bytes.
  100. if (!FLAG(p)) {
  101. long long pos;
  102. if (fscanf(fp, "%llx: ", &pos) == 1) {
  103. if (fseek(stdout, pos, SEEK_SET) != 0) {
  104. // TODO: just write out zeros if non-seekable?
  105. perror_exit("%s: seek failed", name);
  106. }
  107. }
  108. }
  109. // A plain hexdump can have as many bytes per line as you like,
  110. // but a non-plain hexdump assumes garbage after it's seen the
  111. // specified number of bytes.
  112. while (FLAG(p) || col < TT.c) {
  113. int n1, n2;
  114. // If we're at EOF or EOL or we read some non-hex...
  115. if ((n1 = n2 = dehex(fgetc(fp))) < 0 || (n2 = dehex(fgetc(fp))) < 0) {
  116. // If we're at EOL, start on that line.
  117. if (n1 == -2 || n2 == -2) continue;
  118. // Otherwise, skip to the next line.
  119. break;
  120. }
  121. fputc((n1 << 4) | (n2 & 0xf), stdout);
  122. col++;
  123. // Is there any grouping going on? Ignore a single space.
  124. tmp = fgetc(fp);
  125. if (tmp != ' ') ungetc(tmp, fp);
  126. }
  127. // Skip anything else on this line (such as the ASCII dump).
  128. while ((tmp = fgetc(fp)) != EOF && tmp != '\n');
  129. }
  130. if (ferror(fp)) perror_msg_raw(name);
  131. fclose(fp);
  132. }
  133. void xxd_main(void)
  134. {
  135. if (!TT.c) TT.c = FLAG(i) ? 12 : 16;
  136. // Plain style is 30 bytes/line, no grouping.
  137. if (FLAG(p)) TT.c = TT.g = 30;
  138. loopfiles(toys.optargs,
  139. FLAG(r) ? do_xxd_reverse : (FLAG(i) ? do_xxd_include : do_xxd));
  140. }