diff --git a/doc/lispref/streams.texi b/doc/lispref/streams.texi index 2cd61ad04f..f171f13779 100644 --- a/doc/lispref/streams.texi +++ b/doc/lispref/streams.texi @@ -902,3 +902,11 @@ Output Variables in the C function @code{sprintf}. For further restrictions on what you can use, see the variable's documentation string. @end defvar + +@defvar integer-output-format +This variable specifies how to print integer numbers. The default is +@code{nil}, meaning use the decimal format. When bound to @code{t}, +print integers as characters when an integer represents a character +(@pxref{Basic Char Syntax}). When bound to the number @code{16}, +print non-negative integers in the hexadecimal format. +@end defvar diff --git a/etc/NEWS b/etc/NEWS index a77c1c883e..2f7d08ad08 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -1631,6 +1631,12 @@ ledit.el, lmenu.el, lucid.el and old-whitespace.el. * Lisp Changes in Emacs 28.1 +** New variable 'integer-output-format' defines the format of integers. +When this variable is bound to the value 't', integers are printed by +printing functions as characters when an integer represents a character. +When bound to the number 16, non-negative integers are printed in the +hexadecimal format. + +++ ** 'define-globalized-minor-mode' now takes a :predicate parameter. This can be used to control which major modes the minor mode should be diff --git a/src/print.c b/src/print.c index 53aa353769..a5c56c6b48 100644 --- a/src/print.c +++ b/src/print.c @@ -1908,8 +1908,31 @@ print_object (Lisp_Object obj, Lisp_Object printcharfun, bool escapeflag) { case_Lisp_Int: { - int len = sprintf (buf, "%"pI"d", XFIXNUM (obj)); - strout (buf, len, len, printcharfun); + int c; + intmax_t i; + + if (EQ (Vinteger_output_format, Qt) && CHARACTERP (obj) + && (c = XFIXNUM (obj)) && ! CHAR_BYTE8_P (c)) + { + printchar ('?', printcharfun); + if (escapeflag + && (c == ';' || c == '(' || c == ')' || c == '{' || c == '}' + || c == '[' || c == ']' || c == '\"' || c == '\'' || c == '\\')) + printchar ('\\', printcharfun); + printchar (c, printcharfun); + } + else if (INTEGERP (Vinteger_output_format) + && integer_to_intmax (Vinteger_output_format, &i) + && i == 16 && Fnatnump (obj)) + { + int len = sprintf (buf, "#x%"pI"x", (EMACS_UINT) XFIXNUM (obj)); + strout (buf, len, len, printcharfun); + } + else + { + int len = sprintf (buf, "%"pI"d", XFIXNUM (obj)); + strout (buf, len, len, printcharfun); + } } break; @@ -2247,6 +2270,13 @@ syms_of_print (void) that represents the number without losing information. */); Vfloat_output_format = Qnil; + DEFVAR_LISP ("integer-output-format", Vinteger_output_format, + doc: /* The format used to print integers. +When t, print characters from integers that represent a character. +When a number 16, print non-negative integers in the hexadecimal format. +Otherwise, by default print integers in the decimal format. */); + Vinteger_output_format = Qnil; + DEFVAR_LISP ("print-length", Vprint_length, doc: /* Maximum length of list to print before abbreviating. A value of nil means no limit. See also `eval-expression-print-length'. */); diff --git a/test/src/print-tests.el b/test/src/print-tests.el index eb9572dbdf..7b026b6b21 100644 --- a/test/src/print-tests.el +++ b/test/src/print-tests.el @@ -383,5 +383,25 @@ print-hash-table-test (let ((print-length 1)) (format "%S" h)))))) +(print-tests--deftest print-integer-output-format () + ;; Bug#44155. + (let ((integer-output-format t) + (syms (list ?? ?\; ?\( ?\) ?\{ ?\} ?\[ ?\] ?\" ?\' ?\\ ?Á))) + (should (equal (read (print-tests--prin1-to-string syms)) syms)) + (should (equal (print-tests--prin1-to-string syms) + (concat "(" (mapconcat #'prin1-char syms " ") ")")))) + (let ((integer-output-format t) + (syms (list -1 0 1 ?\120 4194175 4194176 (max-char) (1+ (max-char))))) + (should (equal (read (print-tests--prin1-to-string syms)) syms))) + (let ((integer-output-format 16) + (syms (list -1 0 1 most-positive-fixnum (1+ most-positive-fixnum)))) + (should (equal (read (print-tests--prin1-to-string syms)) syms)) + (should (equal (print-tests--prin1-to-string syms) + (concat "(" (mapconcat + (lambda (i) + (if (and (>= i 0) (<= i most-positive-fixnum)) + (format "#x%x" i) (format "%d" i))) + syms " ") ")"))))) + (provide 'print-tests) ;;; print-tests.el ends here