aboutsummaryrefslogtreecommitdiff
path: root/toys/pending/xxd.c
blob: 3a84c988bb82831c257aac8b433c9403929c1361 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
/* xxd.c - hexdump.
 *
 * Copyright 2015 The Android Open Source Project
 *
 * TODO: support for reversing a hexdump back into the original data.
 * TODO: support > 4GiB files?
 * TODO: -s seek

USE_XXD(NEWTOY(xxd, ">1c#l#g#", TOYFLAG_USR|TOYFLAG_BIN))

config XXD
  bool "xxd"
  default n
  help
    usage: xxd [-c n] [-g n] [-l 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).
    -l n	Limit of n bytes before stopping (default is no limit).
*/

#define FOR_xxd
#include "toys.h"

static const char* hex_digits = "0123456789abcdef";

GLOBALS(
  long bytes_per_group; // -g
  long limit; // -l
  long bytes_per_line; // -c
)

static void xxd_file(FILE *fp)
{
  // "0000000: 4c69 6e75 7820 7665 7273 696f 6e20 332e  Linux version 3.".
  size_t hex_size = 8 + 2 +
      2*TT.bytes_per_line + TT.bytes_per_line/TT.bytes_per_group + 1;
  size_t line_size = hex_size + TT.bytes_per_line + 1;
  char *line = xmalloc(line_size);
  int offset = 0;
  int bytes_this_line = 0;
  int line_index = 0;
  int ch;

  memset(line, ' ', line_size);
  line[line_size - 1] = 0;

  while ((ch = getc(fp)) != EOF) {
    if (bytes_this_line == 0) line_index = sprintf(line, "%08x: ", offset);
    ++offset;

    line[line_index++] = hex_digits[(ch >> 4) & 0xf];
    line[line_index++] = hex_digits[ch & 0xf];
    line[hex_size + bytes_this_line] = (ch >= ' ' && ch <= '~') ? ch : '.';

    ++bytes_this_line;
    if (bytes_this_line == TT.bytes_per_line) {
      puts(line);
      memset(line, ' ', line_size - 1);
      bytes_this_line = 0;
    } else if ((bytes_this_line % TT.bytes_per_group) == 0) {
      line[line_index++] = ' ';
    }
    if ((toys.optflags & FLAG_l) && offset == TT.limit) {
      break;
    }
  }
  if (bytes_this_line != 0) {
    line[hex_size + bytes_this_line] = 0;
    puts(line);
  }

  if (CFG_FREE) free(line);
}

void xxd_main(void)
{
  FILE *fp;

  if (!TT.bytes_per_line) TT.bytes_per_line = 16;
  else if (TT.bytes_per_line < 0)
    error_exit("invalid -c value: %d", TT.bytes_per_line);

  if (!TT.bytes_per_group) TT.bytes_per_group = 2;
  else if (TT.bytes_per_group < 0)
    error_exit("invalid -g value: %d", TT.bytes_per_group);

  if (!*toys.optargs || !strcmp(*toys.optargs, "-")) fp = stdin;
  else fp = xfopen(*toys.optargs, "r");

  xxd_file(fp);

  fclose(fp);
}