Skip to content

Commit

Permalink
Add xxd -p and -r.
Browse files Browse the repository at this point in the history
SELinux denials include hex-encoded paths in the log messages; xxd -r -p is
a convenient way to decode them.

The heuristics are a little weird to my mind, but match the documentation
and observed behavior.
  • Loading branch information
enh-google authored and landley committed Feb 19, 2016
1 parent 42cad12 commit 577b7ca
Show file tree
Hide file tree
Showing 2 changed files with 92 additions and 7 deletions.
19 changes: 19 additions & 0 deletions tests/xxd.test
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,23 @@ testing "xxd -c 8 -g 4 file1" "xxd -c 8 -g 4 file1" \
testing "xxd -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 "xxd -p" "xxd -p file1" "7468697320697320736f6d6520746578740a\n" "" ""

testing "xxd -r" "xxd file1 | xxd -r" "this is some text\n" "" ""
testing "xxd -r -p" "xxd -p file1 | xxd -r -p" "this is some text\n" "" ""

testing "xxd -r garbage" "echo '0000: 68 65 6c6c 6fxxxx' | xxd -r -" "hello" "" ""

# -r will only read -c bytes (default 16) before skipping to the next line,
# ignoring the rest.
testing "xxd -r long" \
"echo '0000: 40404040404040404040404040404040404040404040404040404040404040404040404040404040' | xxd -r -" \
"@@@@@@@@@@@@@@@@" "" ""

# -r -p ignores the usual -p 30-byte/line limit (or any limit set by -c) and
# will take as many bytes as you give it.
testing "xxd -r -p long" \
"echo '40404040404040404040404040404040404040404040404040404040404040404040404040404040' | xxd -r -p -" \
"@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@" "" ""

rm file1 file2
80 changes: 73 additions & 7 deletions toys/other/xxd.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,22 @@
* TODO: support for reversing a hexdump back into the original data.
* TODO: -s seek
USE_XXD(NEWTOY(xxd, ">1c#<1>4096=16l#g#<1=2", TOYFLAG_USR|TOYFLAG_BIN))
USE_XXD(NEWTOY(xxd, ">1c#<1>4096=16l#g#<1=2pr", TOYFLAG_USR|TOYFLAG_BIN))
config XXD
bool "xxd"
default y
help
usage: xxd [-c n] [-g n] [-l n] [file]
usage: xxd [-c n] [-g n] [-l n] [-p] [-r] [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).
-p Plain hexdump (30 bytes/line, no grouping).
-r Reverse operation: turn a hexdump into a binary file.
*/

#define FOR_xxd
Expand All @@ -39,7 +41,7 @@ static void do_xxd(int fd, char *name)
int i, len, space;

while (0<(len = readall(fd, toybuf, (TT.l && TT.l-pos<TT.c)?TT.l-pos:TT.c))) {
printf("%08llx: ", pos);
if (!(toys.optflags&FLAG_p)) printf("%08llx: ", pos);
pos += len;
space = 2*TT.c+TT.c/TT.g+1;

Expand All @@ -51,15 +53,79 @@ static void do_xxd(int fd, char *name)
}
}

printf("%*s", space, "");
for (i=0; i<len; i++)
putchar((toybuf[i]>=' ' && toybuf[i]<='~') ? toybuf[i] : '.');
if (!(toys.optflags&FLAG_p)) {
printf("%*s", space, "");
for (i=0; i<len; i++)
putchar((toybuf[i]>=' ' && toybuf[i]<='~') ? toybuf[i] : '.');
}
putchar('\n');
}
if (len<0) perror_exit("read");
}

static int dehex(char ch)
{
if (ch >= '0' && ch <= '9') return ch - '0';
if (ch >= 'a' && ch <= 'f') return ch - 'a' + 10;
if (ch >= 'A' && ch <= 'F') return ch - 'a' + 10;
return (ch == '\n') ? -2 : -1;
}

static void do_xxd_reverse(int fd, char *name)
{
FILE *fp = xfdopen(fd, "r");

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);
}
}
}

// 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++;

// 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')
;
}
if (ferror(fp)) perror_msg_raw(name);

fclose(fp);
}

void xxd_main(void)
{
loopfiles(toys.optargs, do_xxd);
// 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);
}

0 comments on commit 577b7ca

Please sign in to comment.