Subject: bug#53924: 26.1; fontification sometimes fails for some characters despite available glyphs

From: "Greg A. Woods" <[email-masked]>
Date: Sat, 12 Feb 2022 22:06:09 -0800

New code, ancient bug, and a possibly related crash.....

First the ancient typo/bug:

fontset.el contains an errant definition for xlfd-regexp-spacing-subnum
(with the value '8').  git-blame suggests this typo has lurked for 20
years!

It should of course be '9':

    (defconst xlfd-regexp-spacing-subnum 9) ; fix a 20-year-old typo!

Second, an update of my `show-all-font-families', now with working
support to optionally show just the mono-spaced fonts, but be warned it
can cause crashes:

(defvar sample-text-history nil
  "History list for commands that read sample text.")
(require 'faces)
(add-to-history 'sample-text-history list-faces-sample-text)

(defvar ascii-sample-text
  ;; this default text is just ASCII
  ;; xxx should it be iso8859-1, but how to do that?
  ;; `encode-coding-string' ?
  "ABCDEFGHIJKLMNOPQRSTUVWXYZ 0123456789
abcdefghijklmnopqrstuvwxyz
`~!@#$%^&*()_+-={}|[]\\:\"\;'<>?,./"
  "all the ASCII printable characters for sample text.")

(defun show-all-font-families (&optional sample-text mono-only)
  "Show SAMPLE-TEXT (by default `list-faces-sample-text') in
'roman', 'bold', 'italic', and 'bold italic' for each font family
known to `font-family-list'.

If MONO-ONLY, include ':spacing m' in the `font-spec' parameters.
Interactively, a negative prefix does the same.

Note you can seem some strange results for fonts that don't have
each of the requested weight and slant forms.  Sometimes
proportional glyphs will even be substituted for mono-spaced
glyphs!  Sometimes a glyph from a different font (the default
font?) will be substituted even when the requested font is
available with all the requested attributes, e.g. 'office code
pro'.  (Perhaps because it is _only_ avaliable with the requested
attributes, but no others?)

Also some proportional fonts will show up despite only
mono-spaced fonts being selected.  Perhaps spacing is ignored
when requesting a font?  Note even `x-list-fonts' warns that:

    Fonts Emacs can't use may or may not be excluded...

N.B. WARNING:  This code may try to display fonts that cannot be
opened and as a result will crash Emacs!  Worse yet it can get
stuck rendering fonts and if killed cause the X11 server to go
into a CPU-bound loop that may take hours to resolve!  Kill it
sooner than later!  This is particularly true for Emacs-26.1 when
the MONO-ONLY parameter is non-nil.  See also the exclusion of
the 'droid' fonts.

Derived from an example on the EmacsWiki."
  (interactive
   (list
    ;; optional `sample-text':
    (if current-prefix-arg
	(read-string
	 "Sample text:"
	 ascii-sample-text
	 'sample-text-history))
    ;; optional `mono-only':
    (cond
     ((eq current-prefix-arg '-)
      t)
     ((and (numberp current-prefix-arg)
           (< current-prefix-arg 0))
      t)
     ((and (listp current-prefix-arg)
           (numberp (car current-prefix-arg))
           (< (car current-prefix-arg) 0))
      t)
     (t nil))))
  (let ((str (if sample-text
		 sample-text
	       list-faces-sample-text))
	(font-families (cl-remove-duplicates
			(sort (font-family-list)
			      (lambda(x y) (string> (upcase x) (upcase y))))
			:test 'cl-equalp)))
    (with-help-window "*Font Families*"
      (with-current-buffer standard-output
	(dolist (ff font-families)
	  (let* ((fs (font-spec :family ff
				:weight 'medium :slant 'r
				;; xxx :spacing is confusing
				;; see below for additional test to confirm!
				(if mono-only :spacing) (if mono-only 'm)
				:registry 'iso10646-1))
		 (fl (list-fonts fs))
		 (fe (car fl))			; usually the cdr is non-scalable?
		 (xlfd (if fe
			   (font-xlfd-name fe)
			 (font-xlfd-name fs)))	; xxx font may not be useable
		 (fn (if (eq window-system 'x)
			 (condition-case nil
			     (x-resolve-font-name xlfd)
			   (error (message "Invalid font family: %s" ff)
				  nil))
		       nil))
		 (xlfd-fields (if fn
				  (x-decompose-font-name fn) ; xxx not really an X11 function
				nil))
		 )
	    ;; XXX the "droid*" fonts are broken???
	    (if (and (not (string-match "droid" ff))
		     (not (string-equal "nil" ff)) ; xxx never a useful font
		     (not (string-match "italic" ff)) ; xxx rarely in all other styles
		     (not (string-match "bold" ff)) ; xxx rarely in all other styles
		     (not (string-match ".pcf" ff)) ; xxx usually bitmap cursors
		     ;;
		     ;; xxx some fonts, e.g. "inconsolata" (which xfontsel says
		     ;; is both 'm' and 'p'), will return the "wrong" spacing in
		     ;; the XLFD if the request has a wild-card (i.e. :spacing
		     ;; unspecified), but then it can't be found with
		     ;; `font-info' with the explicit ":spacing 'p".
		     ;;
		     ;; XXX this test is needed though as it might sometimes
		     ;; prevent crashes in the case where this spacing confusion
		     ;; happens.
		     ;;
		     (condition-case nil
			 (if (font-info xlfd) ; xxx can this also return nil?
			     t
			   (message "Can't get font info for: %s" xlfd)
			   nil)
		       (error (message "Bad font: %s" xlfd)
			      nil))
		     (if mono-only
			 (if xlfd-fields
			     ;; because `x-resolve-font-name' sometimes ignores
			     ;; `:spacing' we must confirm the font matches
			     ;; xxx `xlfd-regexp-spacing-subnum' is WRONG, for 20 years!
			     (if (string-equal (aref xlfd-fields 9) "m")
				 t
			       (message "Font not actually monospaced: %s" xlfd)
			       nil)
			   ;; xxx try `font-get' on `fe', but that doesn't seem to
			   ;; work properly for getting `:spacing'!?!?!?!?
			   (if (eq window-system 'x)
			       (message "Can't resolve X font for: %s" xlfd)))
		       t))
		;; xxx XXX N.B.:  without escaping the semicolons emacs can't
		;; parse these expressions backwards!!!
		(insert "\; " ff ":" xlfd "\n\;\n"
			"\;\t" ff " (plain):\n\;\n"
			(propertize str 'font-lock-face
				    `(:family ,ff :weight medium :slant r))
			"\n\;\n\;\t" ff " [bold]:\n\;\n"
			(propertize str 'font-lock-face
				    `(:family ,ff :weight bold :slant r))
			"\n\;\n\;\t " ff " [italic]:\n\;\n"
			(propertize str 'font-lock-face
				    `(:family ,ff :weight medium :slant italic))
			"\n\;\n\;\t " ff " [bold italic]:\n\;\n"
			(propertize str 'font-lock-face
				    `(:family ,ff :weight bold :slant italic))
			"\n\;\n\n")
	      (message "Not showing font-family: %s" ff)))
	  (goto-char (point-min))
	  (setq case-fold-search t)
	  (if (fboundp 'form-feed-mode)
	      (form-feed-mode nil)))))))

Finally a crash (working on reproducing it with GDB in the source tree,
but my gdb is having trouble with the emacs .gdbinit):

Reading symbols from /usr/pkg/bin/emacs-26.1...
[New process 12954]
[New process 1873]
Core was generated by `emacs'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0  0x00007cdaa53676aa in _lwp_kill () from /usr/lib/libc.so.12
[Current thread is 1 (process 12954)]
(gdb) bt
#0  0x00007cdaa53676aa in _lwp_kill () from /usr/lib/libc.so.12
#1  0x00000000004dc6ac in terminate_due_to_signal (sig=sig@entry=11,
    backtrace_limit=backtrace_limit@entry=40) at emacs.c:394
#2  0x00000000004f2d13 in handle_fatal_signal (sig=sig@entry=11) at sysdep.c:1769
#3  0x00000000004f2f53 in deliver_thread_signal (sig=sig@entry=11,
    handler=0x4f2d05 <handle_fatal_signal>) at sysdep.c:1743
#4  0x00000000004f2fa6 in deliver_fatal_thread_signal (sig=11) at sysdep.c:1781
#5  handle_sigsegv (sig=11, siginfo=<optimized out>, arg=<optimized out>)
    at sysdep.c:1866
#6  <signal handler called>
#7  0x00000000005ad68d in fontset_find_font (fontset=fontset@entry=364, c=c@entry=180,
    face=face@entry=0x7cdaad2db4c0, charset_id=charset_id@entry=-1,
    fallback=fallback@entry=false) at fontset.c:550
#8  0x00000000005adfd3 in fontset_font (fontset=fontset@entry=364, c=c@entry=180,
    face=face@entry=0x7cdaad2db4c0, id=-1) at fontset.c:760
#9  0x00000000005ae365 in face_for_char (f=0x7cdaafbf2c30,
    face=face@entry=0x7cdaad2db4c0, c=180, pos=<optimized out>, object=<optimized out>)
    at fontset.c:990
#10 0x000000000043e186 in FACE_FOR_CHAR (object=<optimized out>, pos=<optimized out>,
    character=<optimized out>, face=0x7cdaad2db4c0, f=<optimized out>)
    at dispextern.h:1818
#11 get_next_display_element (it=it@entry=0x7f7fffe82f90) at xdisp.c:7303
#12 0x000000000044790b in display_line (it=it@entry=0x7f7fffe82f90,
    cursor_vpos=cursor_vpos@entry=0) at xdisp.c:21409
#13 0x000000000044ab97 in try_window (window=window@entry=137278716771333, pos=...,
    flags=flags@entry=0) at xdisp.c:17627
#14 0x000000000045e783 in redisplay_window (window=137278716771333,
    just_this_one_p=just_this_one_p@entry=false) at xdisp.c:16811
#15 0x0000000000461d78 in redisplay_window_0 (window=window@entry=137278716771333)
    at xdisp.c:14831
#16 0x0000000000547c1f in internal_condition_case_1 (
    bfun=bfun@entry=0x461d4f <redisplay_window_0>, arg=137278716771333,
    handlers=<optimized out>, hfun=hfun@entry=0x42929e <redisplay_window_error>)
    at eval.c:1356
#17 0x000000000042d9ae in redisplay_windows (window=137278716771333) at xdisp.c:14811
#18 0x000000000044f61d in redisplay_internal () at xdisp.c:14300
#19 0x000000000045133e in redisplay () at xdisp.c:13518
#20 0x00000000004e58eb in read_char (commandflag=commandflag@entry=1,
    map=map@entry=137278680449907, prev_event=0,
    used_mouse_menu=used_mouse_menu@entry=0x7f7fffe883cb, end_time=end_time@entry=0x0)
    at keyboard.c:2480
#21 0x00000000004e8b6c in read_key_sequence (keybuf=keybuf@entry=0x7f7fffe884b0,
    prompt=prompt@entry=0, dont_downcase_last=dont_downcase_last@entry=false,
    can_return_switch_frame=can_return_switch_frame@entry=true,
--Type <RET> for more, q to quit, c to continue without paging--q
Quit
(gdb)

--
					Greg A. Woods <[email-masked]>

Kelowna, BC     +1 250 762-7675           RoboHack <[email-masked]>
Planix, Inc. <[email-masked]>     Avoncote Farms <[email-masked]>