compat: move hexdump -R functionality to xxd -r

function                                             old     new   delta
xxd_main                                             466     680    +214
packed_usage                                       33474   33483      +9
hexdump_opts                                          17      16      -1
hexdump_main                                         565     401    -164
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 2/2 up/down: 223/-165)           Total: 58 bytes

Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
diff --git a/util-linux/hexdump.c b/util-linux/hexdump.c
index 065b839..2174c30 100644
--- a/util-linux/hexdump.c
+++ b/util-linux/hexdump.c
@@ -15,16 +15,6 @@
 //config:	The hexdump utility is used to display binary data in a readable
 //config:	way that is comparable to the output from most hex editors.
 //config:
-//config:config FEATURE_HEXDUMP_REVERSE
-//config:	bool "Support -R, reverse of 'hexdump -Cv'"
-//config:	default y
-//config:	depends on HEXDUMP
-//config:	help
-//config:	The hexdump utility is used to display binary data in an ascii
-//config:	readable way. This option creates binary data from an ascii input.
-//config:	NB: this option is non-standard. It's unwise to use it in scripts
-//config:	aimed to be portable.
-//config:
 //config:config HD
 //config:	bool "hd (7.8 kb)"
 //config:	default y
@@ -38,7 +28,7 @@
 //kbuild:lib-$(CONFIG_HD) += hexdump.o
 
 //usage:#define hexdump_trivial_usage
-//usage:       "[-bcCdefnosvx" IF_FEATURE_HEXDUMP_REVERSE("R") "] [FILE]..."
+//usage:       "[-bcCdefnosvx] [FILE]..."
 //usage:#define hexdump_full_usage "\n\n"
 //usage:       "Display FILEs (or stdin) in a user specified format\n"
 //usage:     "\n	-b		1-byte octal display"
@@ -53,9 +43,6 @@
 // exactly the same help text lines in hexdump and xxd:
 //usage:     "\n	-n LENGTH	Show only first LENGTH bytes"
 //usage:     "\n	-s OFFSET	Skip OFFSET bytes"
-//usage:	IF_FEATURE_HEXDUMP_REVERSE(
-//usage:     "\n	-R		Reverse of 'hexdump -Cv'")
-// TODO: NONCOMPAT!!! move -R to xxd -r
 //usage:
 //usage:#define hd_trivial_usage
 //usage:       "FILE..."
@@ -94,7 +81,7 @@
 
 static const char add_first[] ALIGN1 = "\"%07.7_Ax\n\"";
 
-static const char hexdump_opts[] ALIGN1 = "bcdoxCe:f:n:s:v" IF_FEATURE_HEXDUMP_REVERSE("R");
+static const char hexdump_opts[] ALIGN1 = "bcdoxCe:f:n:s:v";
 
 int hexdump_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 int hexdump_main(int argc, char **argv)
@@ -102,10 +89,6 @@
 	dumper_t *dumper = alloc_dumper();
 	const char *p;
 	int ch;
-#if ENABLE_FEATURE_HEXDUMP_REVERSE
-	FILE *fp;
-	smallint rdump = 0;
-#endif
 
 	if (ENABLE_HD
 	 && (!ENABLE_HEXDUMP || !applet_name[2])
@@ -153,11 +136,6 @@
 		if (ch == 'v') {
 			dumper->dump_vflag = ALL;
 		}
-#if ENABLE_FEATURE_HEXDUMP_REVERSE
-		if (ch == 'R') {
-			rdump = 1;
-		}
-#endif
 	}
 
 	if (!dumper->fshead) {
@@ -167,40 +145,5 @@
 
 	argv += optind;
 
-#if !ENABLE_FEATURE_HEXDUMP_REVERSE
 	return bb_dump_dump(dumper, argv);
-#else
-	if (!rdump) {
-		return bb_dump_dump(dumper, argv);
-	}
-
-	/* -R: reverse of 'hexdump -Cv' */
-	fp = stdin;
-	if (!*argv) {
-		argv--;
-		goto jump_in;
-	}
-
-	do {
-		char *buf;
-		fp = xfopen_for_read(*argv);
- jump_in:
-		while ((buf = xmalloc_fgetline(fp)) != NULL) {
-			p = buf;
-			while (1) {
-				/* skip address or previous byte */
-				while (isxdigit(*p)) p++;
-				while (*p == ' ') p++;
-				/* '|' char will break the line */
-				if (!isxdigit(*p) || sscanf(p, "%x ", &ch) != 1)
-					break;
-				putchar(ch);
-			}
-			free(buf);
-		}
-		fclose(fp);
-	} while (*++argv);
-
-	fflush_stdout_and_exit(EXIT_SUCCESS);
-#endif
 }
diff --git a/util-linux/hexdump_xxd.c b/util-linux/hexdump_xxd.c
index f2d1ecb..d2f4b6e 100644
--- a/util-linux/hexdump_xxd.c
+++ b/util-linux/hexdump_xxd.c
@@ -50,6 +50,7 @@
 // exactly the same help text lines in hexdump and xxd:
 //usage:     "\n	-l LENGTH	Show only first LENGTH bytes"
 //usage:     "\n	-s OFFSET	Skip OFFSET bytes"
+//usage:     "\n	-r		Reverse (with -p, assumes no offsets in input)"
 // TODO: implement -r (see hexdump -R)
 
 #include "libbb.h"
@@ -57,6 +58,71 @@
 
 /* This is a NOEXEC applet. Be very careful! */
 
+#define OPT_l (1 << 0)
+#define OPT_s (1 << 1)
+#define OPT_a (1 << 2)
+#define OPT_p (1 << 3)
+#define OPT_r (1 << 4)
+
+static void reverse(unsigned opt, unsigned cols, const char *filename)
+{
+	FILE *fp;
+	char *buf;
+
+	fp = filename ? xfopen_for_read(filename) : stdin;
+
+	while ((buf = xmalloc_fgetline(fp)) != NULL) {
+		char *p = buf;
+		unsigned cnt = cols;
+
+		if (!(opt & OPT_p)) {
+			/* skip address */
+			while (isxdigit(*p)) p++;
+			/* NB: for xxd -r, first hex portion is address even without colon */
+			/* If it's there, skip it: */
+			if (*p == ':') p++;
+
+//TODO: seek (or zero-pad if unseekable) to the address position
+//NOTE: -s SEEK value should be added to the address before seeking
+		}
+
+		/* Process hex bytes optionally separated by whitespace */
+		do {
+			uint8_t val, c;
+
+			p = skip_whitespace(p);
+
+			c = *p++;
+			if (isdigit(c))
+				val = c - '0';
+			else if ((c|0x20) >= 'a' && (c|0x20) <= 'f')
+				val = (c|0x20) - ('a' - 10);
+			else
+				break;
+			val <<= 4;
+
+			/* Works the same with xxd V1.10:
+			 *  echo "31 09 32 0a" | xxd -r -p
+			 *  echo "31 0 9 32 0a" | xxd -r -p
+			 * thus allow whitespace even within the byte:
+			 */
+			p = skip_whitespace(p);
+
+			c = *p++;
+			if (isdigit(c))
+				val |= c - '0';
+			else if ((c|0x20) >= 'a' && (c|0x20) <= 'f')
+				val |= (c|0x20) - ('a' - 10);
+			else
+				break;
+			putchar(val);
+		} while (!(opt & OPT_p) || --cnt != 0);
+		free(buf);
+	}
+	//fclose(fp);
+	fflush_stdout_and_exit(EXIT_SUCCESS);
+}
+
 int xxd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 int xxd_main(int argc UNUSED_PARAM, char **argv)
 {
@@ -69,11 +135,7 @@
 
 	dumper = alloc_dumper();
 
-#define OPT_l (1 << 0)
-#define OPT_s (1 << 1)
-#define OPT_a (1 << 2)
-#define OPT_p (1 << 3)
-	opt = getopt32(argv, "^" "l:s:apg:+c:+" "\0" "?1" /* 1 argument max */,
+	opt = getopt32(argv, "^" "l:s:aprg:+c:+" "\0" "?1" /* 1 argument max */,
 			&opt_l, &opt_s, &bytes, &cols
 	);
 	argv += optind;
@@ -107,6 +169,10 @@
 		bb_dump_add(dumper, "\"%08.8_ax: \""); // "address: "
 	}
 
+	if (opt & OPT_r) {
+		reverse(opt, cols, argv[0]);
+	}
+
 	if (bytes < 1 || bytes >= cols) {
 		sprintf(buf, "%u/1 \"%%02x\"", cols); // cols * "xx"
 		bb_dump_add(dumper, buf);