3

I want to read /dev/input/mice with hexdump. Format is described at here at StackOverflow. What I need is to read one byte (flags), print it in hex format, then read two bytes (x and y move offset) and print each in signed decimal format.

I use hexdump from util-linux 2.28 on Arch Linux.

The best I was able to put together is

 hexdump /dev/input/mice -e '/1 "%03x" 2/1 " %03d" /0 "\n"'

The format string:

  1. /1 "%03x" reads one byte and prints it as hex number
  2. 2/1 " %03d" reads one byte and prints it as signed decimal integer, repeats two times
  3. /0 "\n" reads zero bytes and prints end of line

The problem is that for value 0xFF the %d specifier prints 255 instead of -1. The format is taken from C, which extends the width properly, so how to force this proper width extension here?

  • Your example works for me, using hexdump from bsdmainutils (tried 9.0.3 and 9.0.5ubuntu). Which version are you using? – JigglyNaga Aug 19 '16 at 11:41
  • @JigglyNaga version added to question – Adam Trhon Aug 19 '16 at 11:46
  • On Debian Jessie, it returns -1; on CentOS it returns 255. On CentOS hexdump comes from util-linux. On Debian it comes from bsdmainutils. There seems to be an incompatibility between the two versions! – Stephen Harris Aug 19 '16 at 14:04
2

This appears to be a bug in that implementation of hexdump.

The function responsible is

print(struct hexdump_pr *pr, unsigned char *bp)

in hexdump-display.c. It copies the correct number of bytes into a suitable variable, so that sign-extending can happen when passing the value to printf(). But for single bytes, it doesn't bother - it just dereferences the pointer, which was to an unsigned char.

case F_INT:
        {
        short sval; /* int16_t */
        int ival;   /* int32_t */
        long long Lval; /* int64_t, int64_t */

        switch(pr->bcnt) {
        case 1:
            printf(pr->fmt, (unsigned long long) *bp);
            break;
        case 2:
            memmove(&sval, bp, sizeof(sval));
            printf(pr->fmt, (unsigned long long) sval);
            break;

To fix it, patch the function to use the same "copy to local variable" approach:

--- text-utils/hexdump-display.c.orig   2015-10-05 15:18:43.458759033 +0100
+++ text-utils/hexdump-display.c        2016-08-19 14:58:35.507705329 +0100
@@ -146,13 +146,15 @@
            }
        case F_INT:
            {
+               char cval;      /* int8_t */
                short sval;     /* int16_t */
                int ival;       /* int32_t */
                long long Lval; /* int64_t, int64_t */

                switch(pr->bcnt) {
                case 1:
-                       printf(pr->fmt, (unsigned long long) *bp);
+                       memmove(&cval, bp, sizeof(cval));
+                       printf(pr->fmt, (unsigned long long) cval);
                        break;
                case 2:
                        memmove(&sval, bp, sizeof(sval));

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.