Subject: bug#15747: Patch to optionally display raw bytes in hexadecimal

From: Nathan Trapuzzano <[email-masked]>
Date: Thu, 31 Oct 2013 21:10:56 -0400

Eli Zaretskii <[email-masked]> writes:

> Probably.
>
>> Also, what do you guys think of making it buffer-local?
>
> Definitely, IMO.

Patch attached.  I ended up naming the variable
`raw-byte-display-radix'.

>From 96cbab813bdde2b2aa7ee54bdcd191506e5e9186 Mon Sep 17 00:00:00 2001
From: Nathan Trapuzzano <[email-masked]>
Date: Thu, 25 Jul 2013 15:47:23 -0400
Subject: [PATCH] Define and implement `raw-byte-display-radix' variable.

This buffer-local variable allows raw bytes to be displayed with
hexadecimal and decimal escape sequences as opposed to traditional
octal escapes.
---
 doc/emacs/ChangeLog      |  4 ++++
 doc/emacs/display.texi   |  5 +++++
 doc/lispref/ChangeLog    |  4 ++++
 doc/lispref/display.texi | 37 +++++++++++++++++++++++++------------
 doc/lispref/text.texi    | 11 ++++++-----
 lisp/ChangeLog           |  4 ++++
 lisp/cus-start.el        |  1 +
 src/ChangeLog            | 13 +++++++++++++
 src/buffer.c             |  7 +++++++
 src/buffer.h             |  3 +++
 src/character.h          | 24 ++++++++++++++++++------
 src/dispextern.h         |  4 ++++
 src/indent.c             | 18 +++++++++++-------
 src/xdisp.c              | 14 +++++++++++++-
 14 files changed, 118 insertions(+), 31 deletions(-)

diff --git a/doc/emacs/ChangeLog b/doc/emacs/ChangeLog
index eafe608..d418eec 100644
--- a/doc/emacs/ChangeLog
+++ b/doc/emacs/ChangeLog
@@ -1,3 +1,7 @@
+2013-10-31  Nathan Trapuzzano  <[email-masked]>
+
+	* display.texi (Text Display): Document `raw-byte-display-radix'.
+
 2013-10-23  Glenn Morris  <[email-masked]>
 
 	* files.texi, glossary.texi, killing.texi, search.texi, sending.texi:
diff --git a/doc/emacs/display.texi b/doc/emacs/display.texi
index 80dffe2..74fe801 100644
--- a/doc/emacs/display.texi
+++ b/doc/emacs/display.texi
@@ -1400,6 +1400,7 @@ character, @code{U+0001}, is displayed as @samp{^A}.
 
 @cindex octal escapes
 @vindex ctl-arrow
+@vindex raw-byte-display-radix
   The raw bytes with codes @code{U+0080} (octal 200) through
 @code{U+009F} (octal 237) are displayed as @dfn{octal escape
 sequences}, with the @code{escape-glyph} face.  For instance,
@@ -1407,6 +1408,10 @@ character code @code{U+0098} (octal 230) is displayed as @samp{\230}.
 If you change the buffer-local variable @code{ctl-arrow} to
 @code{nil}, the @acronym{ASCII} control characters are also displayed
 as octal escape sequences instead of caret escape sequences.
+Moreover, setting the buffer-local variable
+@code{raw-byte-display-radix} to 16 or 10 causes Emacs to display
+hexadecimal or decimal escape sequences, respectively, rather than
+octal sequences.
 
 @vindex nobreak-char-display
 @cindex non-breaking space
diff --git a/doc/lispref/ChangeLog b/doc/lispref/ChangeLog
index a685f1f..5238ce3 100644
--- a/doc/lispref/ChangeLog
+++ b/doc/lispref/ChangeLog
@@ -1,3 +1,7 @@
+2013-10-31  Nathan Trapuzzano  <[email-masked]>
+
+	* display.texi (Usual Display): Document `raw-byte-display-radix'.
+
 2013-10-30  Xue Fuqiao  <[email-masked]>
 
 	* display.texi (Abstract Display): Improve indexing.
diff --git a/doc/lispref/display.texi b/doc/lispref/display.texi
index 07ab2c6..20318f6 100644
--- a/doc/lispref/display.texi
+++ b/doc/lispref/display.texi
@@ -5900,7 +5900,7 @@ instead of @samp{^}); e.g., the @key{DEL} character is displayed as
 @samp{^?}.
 
 If @code{ctl-arrow} is @code{nil}, these characters are displayed as
-octal escapes (see below).
+octal escapes (but see below).
 
 This rule also applies to carriage return (character code 13), if that
 character appears in the buffer.  But carriage returns usually do not
@@ -5910,11 +5910,14 @@ conversion (@pxref{Coding System Basics}).
 @cindex octal escapes
 @item
 @dfn{Raw bytes} are non-@acronym{ASCII} characters with codes 128
-through 255 (@pxref{Text Representations}).  These characters display
-as @dfn{octal escapes}: sequences of four glyphs, where the first
-glyph is the @acronym{ASCII} code for @samp{\}, and the others are
-digit characters representing the character code in octal.  (A display
-table can specify a glyph to use instead of @samp{\}.)
+through 255 (@pxref{Text Representations}).  By default, these
+characters display as @dfn{octal escapes}: sequences of four glyphs,
+where the first glyph is the @acronym{ASCII} code for @samp{\}, and
+the others are digit characters representing the character code in
+octal.  (A display table can specify a glyph to use instead of
+@samp{\}.)  To display these bytes as hexadecimal or decimal escape
+sequences rather than octal, set @code{raw-byte-display-radix} to 16
+or 10, respectively.
 
 @item
 Each non-@acronym{ASCII} character with code above 255 is displayed
@@ -5932,11 +5935,11 @@ table, for any character whose entry in the active display table is
 specify the characters for which you want special behavior.
 
   The following variables affect how certain characters are displayed
-on the screen.  Since they change the number of columns the characters
-occupy, they also affect the indentation functions.  They also affect
-how the mode line is displayed; if you want to force redisplay of the
-mode line using the new values, call the function
-@code{force-mode-line-update} (@pxref{Mode Line Format}).
+on the screen.  Some of these change the number of columns the
+characters occupy, which affects the indentation functions.  These
+also affect the display of characters in the mode line; if you want to
+force redisplay of the mode line using the new values, call the
+function @code{force-mode-line-update} (@pxref{Mode Line Format}).
 
 @defopt ctl-arrow
 @cindex control characters in display
@@ -5944,7 +5947,7 @@ This buffer-local variable controls how control characters are
 displayed.  If it is non-@code{nil}, they are displayed as a caret
 followed by the character: @samp{^A}.  If it is @code{nil}, they are
 displayed as octal escapes: a backslash followed by three octal
-digits, as in @samp{\001}.
+digits, as in @samp{\001} (but see below).
 @end defopt
 
 @defopt tab-width
@@ -5955,6 +5958,16 @@ is completely independent of the user-settable tab stops used by the
 command @code{tab-to-tab-stop}.  @xref{Indent Tabs}.
 @end defopt
 
+@defopt raw-byte-display-radix
+Setting this buffer-local variable to 16 or 10 will cause raw bytes to
+be displayed as hexadecimal or decimal escape sequences, respectively,
+rather than octal.  The sequences will be prepended with @samp{\} (or
+whatever glyph the current display table specifies for this purpose),
+followed by @samp{x} for hexadecimal or @samp{d} for decimal.  All
+other values cause raw bytes to be displayed with the default octal
+escapes.
+@end defopt
+
 @node Display Tables
 @subsection Display Tables
 
diff --git a/doc/lispref/text.texi b/doc/lispref/text.texi
index aa19338..06788be 100644
--- a/doc/lispref/text.texi
+++ b/doc/lispref/text.texi
@@ -2110,11 +2110,12 @@ characters from the beginning of the buffer) and a column position
 (counting screen characters from the beginning of a line).
 
   These functions count each character according to the number of
-columns it occupies on the screen.  This means control characters count
-as occupying 2 or 4 columns, depending upon the value of
-@code{ctl-arrow}, and tabs count as occupying a number of columns that
-depends on the value of @code{tab-width} and on the column where the tab
-begins.  @xref{Usual Display}.
+columns it occupies on the screen.  This means control characters
+count as occupying 2, 4, or 5 columns, depending upon the values of
+@code{ctl-arrow} and @code{raw-byte-display-radix}, and tabs count as
+occupying a number of columns that depends on the value of
+@code{tab-width} and on the column where the tab begins.  @xref{Usual
+Display}.
 
   Column number computations ignore the width of the window and the
 amount of horizontal scrolling.  Consequently, a column value can be
diff --git a/lisp/ChangeLog b/lisp/ChangeLog
index 58fd55b..a05b7d5 100644
--- a/lisp/ChangeLog
+++ b/lisp/ChangeLog
@@ -1,3 +1,7 @@
+2013-10-31  Nathan Trapuzzano  <[email-masked]>
+
+	* cus-start.el: Add customization support for `raw-byte-display-radix'.
+
 2013-10-31  Glenn Morris  <[email-masked]>
 
 	* Makefile.in (custom-deps, finder-data): No need to setq the target
diff --git a/lisp/cus-start.el b/lisp/cus-start.el
index 24f8ac3..3711db2 100644
--- a/lisp/cus-start.el
+++ b/lisp/cus-start.el
@@ -139,6 +139,7 @@ Leaving \"Default\" unchecked is equivalent with specifying a default of
 	       (const :tag "Right to Left" right-to-left)
 	       (const :tag "Dynamic, according to paragraph text" nil))
 	      "24.1")
+	     (raw-byte-display-radix display (choice (const 8) (const 10) (const 16)))
 	     ;; callint.c
 	     (mark-even-if-inactive editing-basics boolean)
 	     ;; callproc.c
diff --git a/src/ChangeLog b/src/ChangeLog
index fa468c9..09c9de0 100644
--- a/src/ChangeLog
+++ b/src/ChangeLog
@@ -1,3 +1,16 @@
+2013-10-31  Nathan Trapuzzano  <[email-masked]>
+
+	Define and implement buffer-local `raw_byte_display_radix' for
+	displaying raw bytes with hexadecimal and decimal escape
+	sequences.
+	* buffer.c (init_buffer_once):
+	* buffer.h (struct buffer):
+	* character.h (RAW_BYTE_ESCAPE_WIDTH, ASCII_CHAR_WIDTH, CHAR_WIDTH):
+	* dispextern.h (struct it): Add `raw_byte_display_format' member.
+	* indent.c (current_column, scan_for_column, string_display_width)
+	(compute_motion):
+	* xdisp.c (init_iterator, get_next_display_element):
+
 2013-10-31  Jan Djärv  <[email-masked]>
 
 	* emacs.c (main): Skip -psn args on OSX even if ! isatty (0).
diff --git a/src/buffer.c b/src/buffer.c
index e44908a..0b1e1a5 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -5175,6 +5175,7 @@ init_buffer_once (void)
   bset_truncate_lines (&buffer_defaults, Qnil);
   bset_word_wrap (&buffer_defaults, Qnil);
   bset_ctl_arrow (&buffer_defaults, Qt);
+  XSETFASTINT (BVAR (&buffer_defaults, raw_byte_display_radix), 8);
   bset_bidi_display_reordering (&buffer_defaults, Qt);
   bset_bidi_paragraph_direction (&buffer_defaults, Qnil);
   bset_cursor_type (&buffer_defaults, Qt);
@@ -5244,6 +5245,7 @@ init_buffer_once (void)
   XSETFASTINT (BVAR (&buffer_local_flags, truncate_lines), idx); ++idx;
   XSETFASTINT (BVAR (&buffer_local_flags, word_wrap), idx); ++idx;
   XSETFASTINT (BVAR (&buffer_local_flags, ctl_arrow), idx); ++idx;
+  XSETFASTINT (BVAR (&buffer_local_flags, raw_byte_display_radix), idx); ++idx;
   XSETFASTINT (BVAR (&buffer_local_flags, fill_column), idx); ++idx;
   XSETFASTINT (BVAR (&buffer_local_flags, left_margin), idx); ++idx;
   XSETFASTINT (BVAR (&buffer_local_flags, abbrev_table), idx); ++idx;
@@ -5716,6 +5718,11 @@ A value of nil means use backslash and octal digits.
 This variable does not apply to characters whose display is specified
 in the current display table (if there is one).  */);
 
+  DEFVAR_PER_BUFFER ("raw-byte-display-radix", &BVAR (current_buffer, raw_byte_display_radix),
+		     Qintegerp,
+		     doc: /* Radix for displaying raw byte escape sequences.
+Legitimate values are 8, 10, and 16.  Any other value has the same effect as 8.  */);
+
   DEFVAR_PER_BUFFER ("enable-multibyte-characters",
 		     &BVAR (current_buffer, enable_multibyte_characters),
 		     Qnil,
diff --git a/src/buffer.h b/src/buffer.h
index a36c0d1..51fce4d 100644
--- a/src/buffer.h
+++ b/src/buffer.h
@@ -584,6 +584,9 @@ struct buffer
   /* Non-nil means display ctl chars with uparrow.  */
   Lisp_Object INTERNAL_FIELD (ctl_arrow);
 
+  /* Radix to use for displaying raw byte escape sequences.  */
+  Lisp_Object INTERNAL_FIELD (raw_byte_display_radix);
+
   /* Non-nil means reorder bidirectional text for display in the
      visual order.  */
   Lisp_Object INTERNAL_FIELD (bidi_display_reordering);
diff --git a/src/character.h b/src/character.h
index d1b781c..5e011b1 100644
--- a/src/character.h
+++ b/src/character.h
@@ -561,6 +561,12 @@ sanitize_tab_width (EMACS_INT width)
   return 0 < width && width <= 1000 ? width : 8;
 }
 
+/* Return the display width of a raw byte escape sequence, including
+   '\', in buffer BUF.  */
+
+#define RAW_BYTE_ESCAPE_WIDTH(buf) \
+  ((XFASTINT (BVAR ((buf), raw_byte_display_radix))) == 10 ? 5 : 4)
+
 /* Return the width of ASCII character C.  The width is measured by
    how many columns C will occupy on the screen when displayed in the
    current buffer.  */
@@ -569,10 +575,14 @@ sanitize_tab_width (EMACS_INT width)
   (c < 0x20								\
    ? (c == '\t'								\
       ? SANE_TAB_WIDTH (current_buffer)					\
-      : (c == '\n' ? 0 : (NILP (BVAR (current_buffer, ctl_arrow)) ? 4 : 2)))	\
+      : (c == '\n' ? 0 : (NILP (BVAR (current_buffer, ctl_arrow))	\
+	 ? RAW_BYTE_ESCAPE_WIDTH (current_buffer)			\
+	 : 2)))								\
    : (c < 0x7f								\
       ? 1								\
-      : ((NILP (BVAR (current_buffer, ctl_arrow)) ? 4 : 2))))
+      : ((NILP (BVAR (current_buffer, ctl_arrow))			\
+	  ? RAW_BYTE_ESCAPE_WIDTH (current_buffer)			\
+	  : 2))))
 
 /* Return a non-outlandish value for a character width.  */
 
@@ -586,10 +596,12 @@ sanitize_char_width (EMACS_INT width)
    columns C will occupy on the screen when displayed in the current
    buffer.  */
 
-#define CHAR_WIDTH(c)		\
-  (ASCII_CHAR_P (c)		\
-   ? ASCII_CHAR_WIDTH (c)	\
-   : sanitize_char_width (XINT (CHAR_TABLE_REF (Vchar_width_table, c))))
+#define CHAR_WIDTH(c)							\
+    (ASCII_CHAR_P (c)							\
+     ? ASCII_CHAR_WIDTH (c)						\
+     : CHAR_BYTE8_P (c)							\
+       ? RAW_BYTE_ESCAPE_WIDTH (current_buffer)				\
+       : sanitize_char_width (XINT (CHAR_TABLE_REF (Vchar_width_table, c))))
 
 /* If C is a variation selector, return the index of the
    variation selector (1..256).  Otherwise, return 0.  */
diff --git a/src/dispextern.h b/src/dispextern.h
index dd64ae1..a147ea8 100644
--- a/src/dispextern.h
+++ b/src/dispextern.h
@@ -2391,6 +2391,10 @@ struct it
      where the `^' can be replaced by a display table entry.  */
   unsigned ctl_arrow_p : 1;
 
+  /* Format string for displaying raw bytes.  Determined by
+     raw_byte_display_radix.  */
+  const char *raw_byte_display_format;
+
   /* Non-zero means that the current face has a box.  */
   unsigned face_box_p : 1;
 
diff --git a/src/indent.c b/src/indent.c
index d956e62..bf5d5eb 100644
--- a/src/indent.c
+++ b/src/indent.c
@@ -98,9 +98,9 @@ character_width (int c, struct Lisp_Char_Table *dp)
 
   /* Everybody else (control characters, metacharacters) has other
      widths.  We could return their actual widths here, but they
-     depend on things like ctl_arrow and crud like that, and they're
-     not very common at all.  So we'll just claim we don't know their
-     widths.  */
+     depend on things like ctl_arrow, raw_byte_display_radix, and crud
+     like that, and they're not very common at all.  So we'll just
+     claim we don't know their widths.  */
   else
     return 0;
 }
@@ -319,6 +319,7 @@ current_column (void)
   int c;
   int tab_width = SANE_TAB_WIDTH (current_buffer);
   bool ctl_arrow = !NILP (BVAR (current_buffer, ctl_arrow));
+  int raw_byte_escape_width = RAW_BYTE_ESCAPE_WIDTH (current_buffer);
   struct Lisp_Char_Table *dp = buffer_display_table ();
 
   if (PT == last_known_column_point
@@ -422,7 +423,7 @@ current_column (void)
 	       also takes one column.  */
 	    ++col;
 	  else
-	    col += (ctl_arrow && c < 0200) ? 2 : 4;
+	    col += (ctl_arrow && c < 0200) ? 2 : raw_byte_escape_width;
 	}
     }
 
@@ -500,6 +501,7 @@ scan_for_column (ptrdiff_t *endpos, EMACS_INT *goalcol, ptrdiff_t *prevcol)
 {
   int tab_width = SANE_TAB_WIDTH (current_buffer);
   bool ctl_arrow = !NILP (BVAR (current_buffer, ctl_arrow));
+  int raw_byte_escape_width = RAW_BYTE_ESCAPE_WIDTH (current_buffer);
   struct Lisp_Char_Table *dp = buffer_display_table ();
   bool multibyte = !NILP (BVAR (current_buffer, enable_multibyte_characters));
   struct composition_it cmp_it;
@@ -657,7 +659,7 @@ scan_for_column (ptrdiff_t *endpos, EMACS_INT *goalcol, ptrdiff_t *prevcol)
 	  else if (ctl_arrow && (c < 040 || c == 0177))
 	    col += 2;
 	  else if (c < 040 || c >= 0177)
-	    col += 4;
+	    col += raw_byte_escape_width;
 	  else
 	    col++;
 	}
@@ -711,6 +713,7 @@ string_display_width (Lisp_Object string, Lisp_Object beg, Lisp_Object end)
   int c;
   int tab_width = SANE_TAB_WIDTH (current_buffer);
   bool ctl_arrow = !NILP (current_buffer->ctl_arrow);
+  int raw_byte_escape_width = RAW_BYTE_ESCAPE_WIDTH (current_buffer);
   struct Lisp_Char_Table *dp = buffer_display_table ();
   int b, e;
 
@@ -760,7 +763,7 @@ string_display_width (Lisp_Object string, Lisp_Object beg, Lisp_Object end)
 	  tab_seen = 1;
 	}
       else
-	col += (ctl_arrow && c < 0200) ? 2 : 4;
+	col += (ctl_arrow && c < 0200) ? 2 : raw_byte_escape_width;
     }
 
   if (tab_seen)
@@ -1092,6 +1095,7 @@ compute_motion (ptrdiff_t from, ptrdiff_t frombyte, EMACS_INT fromvpos,
   int c = 0;
   int tab_width = SANE_TAB_WIDTH (current_buffer);
   bool ctl_arrow = !NILP (BVAR (current_buffer, ctl_arrow));
+  int raw_byte_escape_width = RAW_BYTE_ESCAPE_WIDTH (current_buffer);
   struct Lisp_Char_Table *dp = window_display_table (win);
   EMACS_INT selective
     = (INTEGERP (BVAR (current_buffer, selective_display))
@@ -1631,7 +1635,7 @@ compute_motion (ptrdiff_t from, ptrdiff_t frombyte, EMACS_INT fromvpos,
 	      else if (VECTORP (charvec))
 		++hpos;
 	      else
-		hpos += (ctl_arrow && c < 0200) ? 2 : 4;
+		hpos += (ctl_arrow && c < 0200) ? 2 : raw_byte_escape_width;
 	    }
 	}
     }
diff --git a/src/xdisp.c b/src/xdisp.c
index 5cd2c2b..ab0d998 100644
--- a/src/xdisp.c
+++ b/src/xdisp.c
@@ -2714,6 +2714,18 @@ init_iterator (struct it *it, struct window *w,
   /* Are control characters displayed as `^C'?  */
   it->ctl_arrow_p = !NILP (BVAR (current_buffer, ctl_arrow));
 
+  switch (XFASTINT (BVAR (current_buffer, raw_byte_display_radix)))
+    {
+      case 10:
+	it->raw_byte_display_format = "d%03d";
+	break;
+      case 16:
+	it->raw_byte_display_format = "x%02X";
+	break;
+      default:
+	it->raw_byte_display_format = "%03o";
+    }
+
   /* -1 means everything between a CR and the following line end
      is invisible.  >0 means lines indented more than this value are
      invisible.  */
@@ -6863,7 +6875,7 @@ get_next_display_element (struct it *it)
 		if (CHAR_BYTE8_P (c))
 		  /* Display \200 instead of \17777600.  */
 		  c = CHAR_TO_BYTE8 (c);
-		len = sprintf (str, "%03o", c);
+		len = sprintf (str, it->raw_byte_display_format, c);
 
 		XSETINT (it->ctl_chars[0], escape_glyph);
 		for (i = 0; i < len; i++)
-- 
1.8.4.2