Repository: sharkdp/hexyl (Rust)

Issue:
Argument `--display-offset` overrides position in sidebar, not offsets it.
According to the help text, `--display-offset` should add to the file position sidebar's offset:

```
    -o, --display-offset <N>    Add N bytes to the displayed file position. The N argument
                                can also include a unit (see `--length` for details)
                                A negative value is valid and calculates an offset
                                relative to the end of the file.
```

In practice, this is actually not the case. Rather than adding the offset, it sets the offset. That means that when using `--display-offset` in conjunction with `--skip`, the usual interactions between `--skip` and the position are negated entirely. For example:

```
$ dd if=/dev/urandom bs=16 count=32 2>/dev/null | hexyl --skip=0xFF --display-offset=0x100
┌────────┬─────────────────────────┬─────────────────────────┬────────┬────────┐
│00000100│ 81 6a 17 0d 78 16 6a 92 ┊ 5c 0e 99 8a bf 50 11 ea │×j•_x•j×┊\•×××P•×│
│00000110│ c4 a8 b8 90 00 93 09 b1 ┊ 6f ee e0 f2 25 2c 44 03 │××××0×_×┊o×××%,D•│
│00000120│ b3 ef 49 d4 00 ca 25 2f ┊ 0d fc e1 8b 85 7d cd 2d │××I×0×%/┊_××××}×-│
│00000130│ 28 2d f2 49 a2 90 d7 5a ┊ 3e 6b 5f ed bf 17 fd ea │(-×I×××Z┊>k_××•××│
│00000140│ a7 ed 18 93 32 4c e6 e9 ┊ db d8 71 00 52 8b 1e 67 │××•×2L××┊××q0R×•g│
│00000150│ c3 ba d0 42 2d 5d 37 94 ┊ 5f 25 4e a2 bb 36 f9 c2 │×××B-]7×┊_%N××6××│
│00000160│ cc 77 c4 7e ab 46 a9 01 ┊ dd 06 b9 1b f6 ca 76 9d │×w×~×F×•┊×•×•××v×│
│00000170│ 46 9c 1c 3b f2 a3 71 4d ┊ 19 51 1d 9a ec b1 c5 37 │F×•;××qM┊•Q•××××7│
│00000180│ 2d c0 0e b0 7b ae cb 33 ┊ a9 e0 08 fb 16 8c 77 a8 │-×•×{××3┊××•×•×w×│
│00000190│ 71 2b e9 f1 db 70 5d 42 ┊ de eb 17 82 96 df 9b cb │q+×××p]B┊××•×××××│
│000001a0│ 20 c4 9f 93 c3 10 ed 23 ┊ 1d c3 aa e0 f1 ef 52 aa │ ××××•×#┊•×××××R×│
│000001b0│ 01 1c e6 99 b4 84 73 88 ┊ 74 1d d0 53 1c 99 9b c3 │••××××s×┊t•×S•×××│
│000001c0│ 7b e2 5c 63 d9 6e 6f e8 ┊ 36 1b 8f b9 a1 25 54 14 │{×\c×no×┊6•×××%T•│
│000001d0│ 9a 25 41 d4 95 0a 04 71 ┊ bf 9c 11 ab 28 64 5a b2 │×%A××_•q┊××•×(dZ×│
│000001e0│ 69 13 ff b1 aa ad 67 85 ┊ c9 8d aa 91 6f c7 05 be │i•××××g×┊××××o×•×│
│000001f0│ bd 63 14 26 ae 1e 7f 42 ┊ 7a b1 6c b9 b1 c8 4b e9 │×c•&×••B┊z×l×××K×│
│00000200│ a6                      ┊                         │×       ┊        │
└────────┴─────────────────────────┴─────────────────────────┴────────┴────────┘
```

With those arguments, it should have started at `1FF` instead of `100`.

---

Note: I'm doing a class assignment regarding creating tests (from scratch) for an open-source command line program, and I chose hexyl for it. You might have a couple more of these on the way, depending on what the assignment brings to light :)


Discussion:
FYI @tommilligan: only if you are interested.. since you implemented `--display-offset`. Also FYI @ErichDonGubler since you implemented `--skip`.
Disclaimer: I'm interested in resolving this. Right now, I'm just going to triage things, but I hope to have a solution after I finish researching and put in my vote for my elections as a US resident.

To me, the implemented behavior is desirable, and it's the documentation that should be re-aligned with current behavior -- this looks like docs I missed updating in my PR for `--skip`. I can go into more detail (and should, since it would be good for a commit message fixing it), but being on mobile doesn't lend well to my long-windedness ATM. :)
I think you could argue this both ways if we were implementing the first hex viewer - however, the behaviour pointed out by @eth-p is the behaviour implemented by `xxd` and I think it's desirable the two should align.

Example: `xxd` and `hexyl` run with the same flags, showing different address output:

```bash
$ cat LICENSE-MIT | head -n1 | hexyl -s 0x10 -o 0x20
┌────────┬─────────────────────────┬─────────────────────────┬────────┬────────┐
│00000020│ 72 65 62 79 20 67 72 61 ┊ 6e 74 65 64 2c 20 66 72 │reby gra┊nted, fr│
│00000030│ 65 65 20 6f 66 20 63 68 ┊ 61 72 67 65 2c 20 74 6f │ee of ch┊arge, to│
│00000040│ 20 61 6e 79 20 70 65 72 ┊ 73 6f 6e 20 6f 62 74 61 │ any per┊son obta│
│00000050│ 69 6e 69 6e 67 20 61 20 ┊ 63 6f 70 79 0a          │ining a ┊copy_   │
└────────┴─────────────────────────┴─────────────────────────┴────────┴────────┘

$ cat LICENSE-MIT | head -n1 | xxd -s 0x10 -o 0x20
00000030: 7265 6279 2067 7261 6e74 6564 2c20 6672  reby granted, fr
00000040: 6565 206f 6620 6368 6172 6765 2c20 746f  ee of charge, to
00000050: 2061 6e79 2070 6572 736f 6e20 6f62 7461   any person obta
00000060: 696e 696e 6720 6120 636f 7079 0a         ining a copy.
```
Opened #119 
> I think you could argue this both ways if we were implementing the first hex viewer - however, the behaviour pointed out by @eth-p is the behaviour implemented by `xxd` and I think it's desirable the two should align.

I agree :+1: 



Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8bd16660..c6259502 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,9 @@
 # unreleased
 
+## Changes
+
+- Breaking change (binary): setting the `-o/--display-offset` flag no longer overrides the value set by `--skip` [#115](https://github.com/sharkdp/hexyl/issues/115). The first displayed address is now the sum of the two values - this matches the behaviour of `xxd`.
+
 ## Features
 
 - Allow relative and negative byte offsets (e.g. `hexyl --skip=-1block`), see #99 (@ErichDonGubler)
diff --git a/src/bin/hexyl.rs b/src/bin/hexyl.rs
index 597e5cd1..71042dd1 100644
--- a/src/bin/hexyl.rs
+++ b/src/bin/hexyl.rs
@@ -155,7 +155,7 @@ fn run() -> Result<(), AnyhowError> {
         })
         .transpose()?;
 
-    let default_display_offset = if let Some(ByteOffset { kind, value }) = skip_arg {
+    let skip_offset = if let Some(ByteOffset { kind, value }) = skip_arg {
         let value = value.into_inner();
         reader.seek(match kind {
             ByteOffsetKind::ForwardFromBeginning | ByteOffsetKind::ForwardFromLastOffset => {
@@ -203,7 +203,7 @@ fn run() -> Result<(), AnyhowError> {
 
     let squeeze = !matches.is_present("nosqueezing");
 
-    let display_offset = matches
+    let display_offset: u64 = matches
         .value_of("display_offset")
         .map(|s| {
             parse_byte_count(s).context(anyhow!(
@@ -212,14 +212,14 @@ fn run() -> Result<(), AnyhowError> {
             ))
         })
         .transpose()?
-        .unwrap_or(default_display_offset)
+        .unwrap_or(0)
         .into();
 
     let stdout = io::stdout();
     let mut stdout_lock = stdout.lock();
 
     let mut printer = Printer::new(&mut stdout_lock, show_color, border_style, squeeze);
-    printer.display_offset(display_offset);
+    printer.display_offset(skip_offset + display_offset);
     printer.print_all(&mut reader).map_err(|e| anyhow!(e))?;
 
     Ok(())


Test patch:
diff --git a/tests/integration_tests.rs b/tests/integration_tests.rs
index b7d642e7..78e6e5db 100644
--- a/tests/integration_tests.rs
+++ b/tests/integration_tests.rs
@@ -94,3 +94,20 @@ fn display_offset() {
              └────────┴─────────────────────────┴─────────────────────────┴────────┴────────┘\n",
         );
 }
+
+#[test]
+fn display_offset_and_skip() {
+    hexyl()
+        .arg("hello_world_elf64")
+        .arg("--color=never")
+        .arg("--display-offset=0x20")
+        .arg("--skip=0x10")
+        .arg("--length=0x10")
+        .assert()
+        .success()
+        .stdout(
+            "┌────────┬─────────────────────────┬─────────────────────────┬────────┬────────┐\n\
+             │00000030│ 02 00 3e 00 01 00 00 00 ┊ 00 10 40 00 00 00 00 00 │•0>0•000┊0•@00000│\n\
+             └────────┴─────────────────────────┴─────────────────────────┴────────┴────────┘\n",
+        );
+}