[git commit] xxd -r: handle offsets

Denys Vlasenko vda.linux at googlemail.com
Mon Aug 22 15:28:43 UTC 2022


commit: https://git.busybox.net/busybox/commit/?id=5eceafb1f812ec4dca7fdf6896cfcea6783a78b9
branch: https://git.busybox.net/busybox/commit/?id=refs/heads/master

function                                             old     new   delta
xxd_main                                            1076    1439    +363
.rodata                                           105239  105251     +12
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 2/0 up/down: 375/0)             Total: 375 bytes

Signed-off-by: Denys Vlasenko <vda.linux at googlemail.com>
---
 util-linux/hexdump_xxd.c | 58 +++++++++++++++++++++++++++++++++++++++++-------
 1 file changed, 50 insertions(+), 8 deletions(-)

diff --git a/util-linux/hexdump_xxd.c b/util-linux/hexdump_xxd.c
index dbda34bc5..6629407de 100644
--- a/util-linux/hexdump_xxd.c
+++ b/util-linux/hexdump_xxd.c
@@ -55,6 +55,7 @@
 //usage:     "\n	-r		Reverse (with -p, assumes no offsets in input)"
 
 #include "libbb.h"
+#include "common_bufsiz.h"
 #include "dump.h"
 
 /* This is a NOEXEC applet. Be very careful! */
@@ -69,10 +70,32 @@
 #define OPT_c (1 << 7)
 #define OPT_o (1 << 8)
 
-static void reverse(unsigned opt, const char *filename)
+#define fillbuf bb_common_bufsiz1
+
+static void write_zeros(off_t count)
+{
+	errno = 0;
+	do {
+		unsigned sz = count < COMMON_BUFSIZE ? (unsigned)count : COMMON_BUFSIZE;
+		if (fwrite(fillbuf, 1, sz, stdout) != sz)
+			bb_perror_msg_and_die("write error");
+		count -= sz;
+	} while (count != 0);
+}
+
+static void reverse(unsigned opt, const char *filename, char *opt_s)
 {
 	FILE *fp;
 	char *buf;
+	off_t cur, opt_s_ofs;
+
+	memset(fillbuf, 0, COMMON_BUFSIZE);
+	opt_s_ofs = cur = 0;
+	if (opt_s) {
+		opt_s_ofs = BB_STRTOOFF(opt_s, NULL, 0);
+		if (errno || opt_s_ofs < 0)
+			bb_error_msg_and_die("invalid number '%s'", opt_s);
+	}
 
 	fp = filename ? xfopen_for_read(filename) : stdin;
 
@@ -82,15 +105,31 @@ static void reverse(unsigned opt, const char *filename)
 
 		p = buf;
 		if (!(opt & OPT_p)) {
+			char *end;
+			off_t ofs;
  skip_address:
 			p = skip_whitespace(p);
-			while (isxdigit(*p)) p++;
+			ofs = BB_STRTOOFF(p, &end, 16);
+			if ((errno && errno != EINVAL)
+			 || ofs < 0
+			/* -s SEEK value should be added before seeking */
+			 || (ofs += opt_s_ofs) < 0
+			) {
+				bb_error_msg_and_die("invalid number '%s'", p);
+			}
+			if (ofs != cur) {
+				if (fseeko(stdout, ofs, SEEK_SET) != 0) {
+					if (ofs < cur)
+						bb_perror_msg_and_die("cannot seek");
+					write_zeros(ofs - cur);
+				}
+				cur = ofs;
+			}
+			p = end;
 			/* 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
+			/* But if colon is there, skip it: */
+			if (*p == ':')
+				p++;
 		}
 
 		/* Process hex bytes optionally separated by whitespace */
@@ -168,6 +207,7 @@ static void reverse(unsigned opt, const char *filename)
 				goto nibble2;
 			}
 			putchar(val);
+			cur++;
 		} /* for(;;) */
 		free(buf);
 	}
@@ -195,6 +235,8 @@ int xxd_main(int argc UNUSED_PARAM, char **argv)
 	unsigned opt;
 	int r;
 
+	setup_common_bufsiz();
+
 	dumper = alloc_dumper();
 
 	opt = getopt32(argv, "^" "l:s:apirg:+c:+o:" "\0" "?1" /* 1 argument max */,
@@ -222,7 +264,7 @@ int xxd_main(int argc UNUSED_PARAM, char **argv)
 	}
 
 	if (opt & OPT_r) {
-		reverse(opt, argv[0]);
+		reverse(opt, argv[0], opt_s);
 	}
 
 	if (opt & OPT_o) {


More information about the busybox-cvs mailing list