emacs-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

feature/tree-sitter 7ebbd4efc3 12/12: Merge branch 'master' into feature


From: Yuan Fu
Subject: feature/tree-sitter 7ebbd4efc3 12/12: Merge branch 'master' into feature/tree-sitter
Date: Wed, 5 Oct 2022 22:52:21 -0400 (EDT)

branch: feature/tree-sitter
commit 7ebbd4efc3d45403cf845d35c36c21756baeeba8
Merge: cb183f6467 95efafb726
Author: Yuan Fu <casouri@gmail.com>
Commit: Yuan Fu <casouri@gmail.com>

    Merge branch 'master' into feature/tree-sitter
---
 .dir-locals.el                                     |    8 +-
 .gitignore                                         |    2 +
 .mailmap                                           |  197 ++
 ChangeLog.3                                        |  313 +-
 GNUmakefile                                        |    2 +-
 Makefile.in                                        |  100 +-
 admin/admin.el                                     |  201 +-
 admin/automerge                                    |    2 +
 admin/cus-test.el                                  |    2 +-
 admin/emake                                        |   46 +-
 admin/gitmerge.el                                  |   31 +-
 admin/grammars/Makefile.in                         |    2 +-
 admin/make-manuals                                 |    2 +
 admin/make-tarball.txt                             |   28 +-
 admin/merge-gnulib                                 |    4 +-
 admin/notes/repo                                   |   11 +-
 admin/notes/unicode                                |    4 +
 admin/notes/www                                    |    4 +-
 admin/unidata/BidiBrackets.txt                     |    6 +-
 admin/unidata/BidiMirroring.txt                    |    8 +-
 admin/unidata/Blocks.txt                           |   21 +-
 admin/unidata/IVD_Sequences.txt                    |    6 +-
 admin/unidata/IdnaMappingTable.txt                 |  169 +-
 admin/unidata/Makefile.in                          |    3 +-
 admin/unidata/NormalizationTest.txt                |   92 +-
 admin/unidata/PropertyValueAliases.txt             |   24 +-
 admin/unidata/ScriptExtensions.txt                 |   10 +-
 admin/unidata/Scripts.txt                          |  106 +-
 admin/unidata/SpecialCasing.txt                    |   10 +-
 admin/unidata/UnicodeData.txt                      |  300 +-
 admin/unidata/confusables.txt                      |   22 +-
 admin/unidata/copyright.html                       |    6 +-
 admin/unidata/emoji-data.txt                       |   73 +-
 admin/unidata/emoji-sequences.txt                  |  343 ++-
 admin/unidata/emoji-test.txt                       |  125 +-
 admin/unidata/emoji-zwj-sequences.txt              |   13 +-
 admin/unidata/unidata-gen.el                       |   10 +-
 admin/update-copyright                             |    2 +
 admin/update_autogen                               |    4 +
 admin/upload-manuals                               |    1 +
 build-aux/config.guess                             |    4 +-
 build-aux/config.sub                               |    4 +-
 config.bat                                         |    1 +
 configure.ac                                       |   24 +-
 doc/emacs/ack.texi                                 |   13 +-
 doc/emacs/buffers.texi                             |   12 +-
 doc/emacs/commands.texi                            |   49 +-
 doc/emacs/dired.texi                               |   29 +-
 doc/emacs/emacs.texi                               |   34 +-
 doc/emacs/files.texi                               |   13 +-
 doc/emacs/frames.texi                              |   21 +-
 doc/emacs/help.texi                                |   14 +-
 doc/emacs/maintaining.texi                         |   91 +-
 doc/emacs/mark.texi                                |   43 +-
 doc/emacs/mini.texi                                |    8 +-
 doc/emacs/misc.texi                                |   29 +-
 doc/emacs/modes.texi                               |    7 +
 doc/emacs/mule.texi                                |   40 +-
 doc/emacs/package.texi                             |   14 +-
 doc/emacs/programs.texi                            |   20 +-
 doc/emacs/regs.texi                                |    3 +-
 doc/emacs/text.texi                                |   17 +-
 doc/emacs/vc1-xtra.texi                            |   19 +
 doc/lispintro/emacs-lisp-intro.texi                |    5 +-
 doc/lispref/compile.texi                           |   18 +
 doc/lispref/control.texi                           |   47 +-
 doc/lispref/display.texi                           |   40 +-
 doc/lispref/edebug.texi                            |    5 +-
 doc/lispref/eval.texi                              |    7 +-
 doc/lispref/files.texi                             |    2 +-
 doc/lispref/frames.texi                            |   32 +-
 doc/lispref/functions.texi                         |   11 +-
 doc/lispref/help.texi                              |   33 +-
 doc/lispref/internals.texi                         |    4 +-
 doc/lispref/keymaps.texi                           |    4 +
 doc/lispref/minibuf.texi                           |    3 +
 doc/lispref/nonascii.texi                          |    9 +-
 doc/lispref/os.texi                                |    3 +
 doc/lispref/sequences.texi                         |   57 +-
 doc/lispref/strings.texi                           |   24 +-
 doc/lispref/symbols.texi                           |    5 +-
 doc/lispref/variables.texi                         |   47 +-
 doc/lispref/windows.texi                           |   34 +-
 doc/man/emacsclient.1                              |   11 +-
 doc/misc/calc.texi                                 |    4 -
 doc/misc/ede.texi                                  |    5 +-
 doc/misc/ediff.texi                                |    2 +-
 doc/misc/efaq.texi                                 |  124 +-
 doc/misc/eieio.texi                                |   12 +-
 doc/misc/eshell.texi                               |  203 +-
 doc/misc/flymake.texi                              |    5 +-
 doc/misc/gnus-coding.texi                          |  227 --
 doc/misc/gnus-faq.texi                             |  296 +-
 doc/misc/gnus.texi                                 |    1 -
 doc/misc/idlwave.texi                              |    4 +-
 doc/misc/mh-e.texi                                 |   52 +-
 doc/misc/modus-themes.org                          |  109 +-
 doc/misc/org.org                                   |    4 +-
 doc/misc/rcirc.texi                                |   71 +-
 doc/misc/reftex.texi                               |   12 -
 doc/misc/semantic.texi                             |   10 +-
 doc/misc/texinfo.tex                               |  113 +-
 doc/misc/tramp.texi                                |   51 +-
 doc/misc/url.texi                                  |   36 +-
 doc/misc/viper.texi                                |    8 +-
 etc/AUTHORS                                        |   23 +-
 etc/ERC-NEWS                                       |   20 +-
 etc/HELLO                                          |    7 +-
 etc/HISTORY                                        |    2 +
 etc/NEWS                                           |  823 +++++-
 etc/NEWS.22                                        |    3 +-
 etc/NEWS.23                                        |    2 +
 etc/NEWS.28                                        |   94 +-
 etc/PROBLEMS                                       |    7 +
 etc/TODO                                           |   47 +-
 etc/images/checked.xpm                             |   19 -
 etc/images/gnus/gnus-pointer.xpm                   |    6 +-
 etc/images/gnus/gnus.xpm                           |    4 +-
 etc/images/mh-logo.xpm                             |   28 +-
 etc/images/outline-close.pbm                       |  Bin 0 -> 39 bytes
 etc/images/outline-close.svg                       |    6 +
 etc/images/outline-open.pbm                        |  Bin 0 -> 39 bytes
 etc/images/outline-open.svg                        |    4 +
 etc/images/unchecked.xpm                           |   19 -
 etc/publicsuffix.txt                               |  127 +-
 etc/refcards/orgcard.tex                           |    2 +-
 etc/themes/modus-operandi-theme.el                 |    2 +-
 etc/themes/modus-themes.el                         |   50 +-
 etc/themes/modus-vivendi-theme.el                  |    2 +-
 etc/tutorials/TUTORIAL.translators                 |    4 +
 etc/tutorials/TUTORIAL.uk                          | 1150 ++++++++
 leim/Makefile.in                                   |    2 -
 lib-src/Makefile.in                                |    8 +-
 lib-src/emacsclient.c                              |   87 +-
 lib-src/seccomp-filter.c                           |   37 +-
 lib/acl-internal.h                                 |    1 -
 lib/acl.h                                          |    1 -
 lib/assert.in.h                                    |   27 +
 lib/c-ctype.h                                      |    2 -
 lib/canonicalize-lgpl.c                            |    1 -
 lib/cloexec.h                                      |    2 -
 lib/close-stream.c                                 |    1 -
 lib/count-leading-zeros.h                          |   20 +-
 lib/count-trailing-zeros.h                         |   16 +-
 lib/diffseq.h                                      |    1 -
 lib/filevercmp.c                                   |    4 +-
 lib/fsusage.h                                      |    1 -
 lib/getloadavg.c                                   |    1 -
 lib/getrandom.c                                    |    1 -
 lib/gnulib.mk.in                                   |   39 +-
 lib/malloc/dynarray.h                              |    1 -
 lib/md5.c                                          |    1 -
 lib/mini-gmp.c                                     |    5 +-
 lib/nanosleep.c                                    |    4 +-
 lib/nstrftime.c                                    |    1 -
 lib/openat.h                                       |    1 -
 lib/pipe2.c                                        |    3 +-
 lib/rawmemchr.c                                    |    4 +-
 lib/regex_internal.h                               |    1 -
 lib/sha1.c                                         |    1 -
 lib/sha256.c                                       |    1 -
 lib/sha512.c                                       |    1 -
 lib/signal.in.h                                    |   14 +-
 lib/stdalign.in.h                                  |   24 +-
 lib/stdckdint.in.h                                 |    2 -
 lib/stdlib.in.h                                    |    6 +-
 lib/string.in.h                                    |    2 +
 lib/strtoimax.c                                    |    6 +-
 lib/sys_random.in.h                                |    2 +
 lib/sys_select.in.h                                |    9 +-
 lib/sys_stat.in.h                                  |   76 +-
 lib/tempname.c                                     |    1 -
 lib/time.in.h                                      |    4 +
 lib/time_rz.c                                      |    1 -
 lib/unistd.in.h                                    |    4 +-
 lib/utimens.c                                      |    1 -
 lib/verify.h                                       |   39 +-
 lisp/Makefile.in                                   |   30 +-
 lisp/abbrev.el                                     |   20 +-
 lisp/allout-widgets.el                             |    4 +-
 lisp/allout.el                                     |    8 -
 lisp/ansi-osc.el                                   |  203 ++
 lisp/arc-mode.el                                   |    2 -
 lisp/auth-source-pass.el                           |   10 +
 lisp/auth-source.el                                |   15 +
 lisp/autoinsert.el                                 |   41 +-
 lisp/autorevert.el                                 |    2 +-
 lisp/bookmark.el                                   |   21 +-
 lisp/buff-menu.el                                  |   18 -
 lisp/calc/calc-embed.el                            |    5 +-
 lisp/calc/calc-stuff.el                            |    8 +-
 lisp/calc/calc-yank.el                             |   12 +-
 lisp/calc/calc.el                                  |   11 +-
 lisp/calendar/cal-move.el                          |    5 +-
 lisp/calendar/timeclock.el                         |    9 -
 lisp/cedet/ede/autoconf-edit.el                    |    3 +-
 lisp/cedet/pulse.el                                |    8 +-
 lisp/cedet/semantic/db-el.el                       |    3 -
 lisp/cedet/semantic/db-file.el                     |    2 -
 lisp/cedet/semantic/ede-grammar.el                 |    7 +-
 lisp/cedet/semantic/edit.el                        |    8 -
 lisp/cedet/semantic/fw.el                          |    2 +-
 lisp/cedet/semantic/grammar.el                     |    8 +-
 lisp/cedet/semantic/lex.el                         |    2 -
 lisp/cedet/semantic/mru-bookmark.el                |    8 +-
 lisp/cedet/semantic/util-modes.el                  |   36 +-
 lisp/cedet/srecode/srt-mode.el                     |   16 +-
 lisp/char-fold.el                                  |   78 +-
 lisp/comint.el                                     |  436 ++-
 lisp/cus-dep.el                                    |    2 +-
 lisp/cus-edit.el                                   |    7 +-
 lisp/cus-start.el                                  |    1 -
 lisp/cus-theme.el                                  |    2 +-
 lisp/custom.el                                     |    9 +-
 lisp/dired-aux.el                                  |   12 +-
 lisp/dired.el                                      |   61 +-
 lisp/disp-table.el                                 |   72 +
 lisp/dnd.el                                        |   16 +-
 lisp/doc-view.el                                   |  128 +-
 lisp/ebuff-menu.el                                 |    3 -
 lisp/ecomplete.el                                  |  128 +-
 lisp/emacs-lisp/backtrace.el                       |    1 -
 lisp/emacs-lisp/benchmark.el                       |    2 +-
 lisp/emacs-lisp/byte-opt.el                        |   34 +-
 lisp/emacs-lisp/byte-run.el                        |   43 -
 lisp/emacs-lisp/bytecomp.el                        |    8 +-
 lisp/emacs-lisp/cconv.el                           |   51 +-
 lisp/emacs-lisp/checkdoc.el                        |   10 +-
 lisp/emacs-lisp/cl-extra.el                        |   10 +-
 lisp/emacs-lisp/cl-generic.el                      |   32 +-
 lisp/emacs-lisp/cl-lib.el                          |    6 -
 lisp/emacs-lisp/cl-macs.el                         |  300 +-
 lisp/emacs-lisp/comp.el                            |   51 +-
 lisp/emacs-lisp/debug.el                           |    8 +-
 lisp/emacs-lisp/easy-mmode.el                      |   12 +-
 lisp/emacs-lisp/edebug.el                          |   25 +-
 lisp/emacs-lisp/eieio-core.el                      |   18 +-
 lisp/emacs-lisp/eieio-opt.el                       |    2 +-
 lisp/emacs-lisp/eieio.el                           |   20 +-
 lisp/emacs-lisp/ert-x.el                           |   57 +-
 lisp/emacs-lisp/generate-lisp-file.el              |   14 +-
 lisp/emacs-lisp/gv.el                              |   19 +-
 lisp/emacs-lisp/icons.el                           |    6 +-
 lisp/emacs-lisp/lisp-mode.el                       |   27 +-
 lisp/emacs-lisp/loaddefs-gen.el                    |   15 +-
 lisp/emacs-lisp/macroexp.el                        |    3 +-
 lisp/emacs-lisp/nadvice.el                         |   47 +-
 lisp/emacs-lisp/oclosure.el                        |   15 +
 lisp/emacs-lisp/package.el                         |   62 +-
 lisp/emacs-lisp/re-builder.el                      |    3 +-
 lisp/emacs-lisp/regexp-opt.el                      |    1 -
 lisp/emacs-lisp/seq.el                             |   69 +-
 lisp/emacs-lisp/shortdoc.el                        |   65 +-
 lisp/emacs-lisp/subr-x.el                          |    1 +
 lisp/emacs-lisp/testcover.el                       |    3 +-
 lisp/emacs-lisp/vtable.el                          |    3 +-
 lisp/emulation/viper-macs.el                       |    5 +-
 lisp/epa-hook.el                                   |    8 +
 lisp/epa-ks.el                                     |   18 +-
 lisp/epa.el                                        |   48 +-
 lisp/erc/erc-dcc.el                                |    3 -
 lisp/erc/erc-match.el                              |   55 +-
 lisp/erc/erc-networks.el                           |    4 +-
 lisp/erc/erc.el                                    |   12 +-
 lisp/eshell/em-cmpl.el                             |   29 +-
 lisp/eshell/em-glob.el                             |    2 +-
 lisp/eshell/em-term.el                             |    2 +-
 lisp/eshell/esh-arg.el                             |   27 +-
 lisp/eshell/esh-cmd.el                             |   13 +-
 lisp/eshell/esh-io.el                              |  228 +-
 lisp/eshell/esh-mode.el                            |    4 -
 lisp/eshell/esh-proc.el                            |  169 +-
 lisp/eshell/esh-var.el                             |   35 +-
 lisp/eshell/eshell.el                              |   11 -
 lisp/faces.el                                      |   49 +-
 lisp/ffap.el                                       |   12 +-
 lisp/filenotify.el                                 |    1 +
 lisp/files.el                                      |  145 +-
 lisp/filesets.el                                   |    2 -
 lisp/find-file.el                                  |   36 +-
 lisp/find-lisp.el                                  |   77 +-
 lisp/format-spec.el                                |   17 +-
 lisp/frame.el                                      |   38 +-
 lisp/generic-x.el                                  |    7 -
 lisp/gnus/gnus-art.el                              |   16 +-
 lisp/gnus/gnus-cloud.el                            |    1 +
 lisp/gnus/gnus-cus.el                              |    4 +-
 lisp/gnus/gnus-group.el                            |   73 +-
 lisp/gnus/gnus-search.el                           |    8 +-
 lisp/gnus/gnus-srvr.el                             |    7 +-
 lisp/gnus/gnus-start.el                            |    2 -
 lisp/gnus/gnus-sum.el                              |    3 +-
 lisp/gnus/gnus.el                                  |   30 +-
 lisp/gnus/message.el                               |   56 +-
 lisp/gnus/nndiary.el                               |    6 -
 lisp/gnus/score-mode.el                            |   12 +-
 lisp/gnus/smime.el                                 |   10 +-
 lisp/help-fns.el                                   |  107 +-
 lisp/help.el                                       |   27 +-
 lisp/hexl.el                                       |    2 +-
 lisp/hilit-chg.el                                  |    1 -
 lisp/hl-line.el                                    |    6 +
 lisp/htmlfontify.el                                |    1 -
 lisp/icomplete.el                                  |   67 +-
 lisp/ido.el                                        |    7 +-
 lisp/ielm.el                                       |   36 +
 lisp/image-dired.el                                | 3080 --------------------
 lisp/image-file.el                                 |    7 +-
 lisp/image-mode.el                                 |   67 +-
 lisp/image.el                                      |   68 +-
 lisp/image/exif.el                                 |   14 +-
 lisp/image/image-crop.el                           |  452 +++
 lisp/image/image-dired-dired.el                    |  412 +++
 lisp/image/image-dired-external.el                 |  473 +++
 lisp/image/image-dired-tags.el                     |  385 +++
 lisp/image/image-dired-util.el                     |  186 ++
 lisp/image/image-dired.el                          | 2013 +++++++++++++
 lisp/image/wallpaper.el                            |  540 ++++
 lisp/imenu.el                                      |   18 +-
 lisp/indent.el                                     |   10 +-
 lisp/info.el                                       |   13 +-
 lisp/international/characters.el                   |   81 +-
 lisp/international/fontset.el                      |   38 +-
 lisp/international/latin1-disp.el                  |    4 +-
 lisp/international/mule-cmds.el                    |   55 +-
 lisp/international/mule.el                         |   17 +-
 lisp/international/quail.el                        |    6 -
 lisp/international/rfc1843.el                      |    5 +-
 lisp/international/robin.el                        |    6 -
 lisp/international/textsec-check.el                |    2 +-
 lisp/international/titdic-cnv.el                   |   10 +-
 lisp/isearch.el                                    |   23 +-
 lisp/jit-lock.el                                   |   13 +-
 lisp/language/cyrillic.el                          |   14 +-
 lisp/language/ethio-util.el                        |   43 +-
 lisp/language/indian.el                            |   30 +-
 lisp/language/lao.el                               |    6 +-
 lisp/language/misc-lang.el                         |   50 +
 lisp/ldefs-boot.el                                 |  817 ++++--
 lisp/leim/quail/hangul.el                          |    4 -
 lisp/leim/quail/indian.el                          |  114 +
 lisp/leim/quail/misc-lang.el                       |  378 +++
 lisp/leim/quail/uni-input.el                       |    4 -
 lisp/loadhist.el                                   |    7 +
 lisp/loadup.el                                     |    4 +-
 lisp/mail/emacsbug.el                              |   11 +-
 lisp/mail/feedmail.el                              |   25 +-
 lisp/mail/hashcash.el                              |   25 +-
 lisp/mail/rmail.el                                 |   30 +-
 lisp/mh-e/mh-e.el                                  |    6 +-
 lisp/mh-e/mh-funcs.el                              |    2 +-
 lisp/minibuf-eldef.el                              |    2 +
 lisp/minibuffer.el                                 |   11 +-
 lisp/mpc.el                                        |    2 -
 lisp/net/dictionary.el                             |    2 +-
 lisp/net/eww.el                                    |   55 +-
 lisp/net/ldap.el                                   |    8 +
 lisp/net/mailcap.el                                |   69 +-
 lisp/net/pop3.el                                   |    2 +
 lisp/net/rcirc.el                                  | 1491 +++++-----
 lisp/net/shr.el                                    |   78 +-
 lisp/net/sieve-manage.el                           |  125 +-
 lisp/net/sieve-mode.el                             |    8 +-
 lisp/net/sieve.el                                  |    3 +-
 lisp/net/tramp-adb.el                              |  166 +-
 lisp/net/tramp-archive.el                          |   37 +-
 lisp/net/tramp-cache.el                            |  175 +-
 lisp/net/tramp-cmds.el                             |   17 +-
 lisp/net/tramp-compat.el                           |   56 +-
 lisp/net/tramp-container.el                        |  190 ++
 lisp/net/tramp-crypt.el                            |   14 +-
 lisp/net/tramp-fuse.el                             |   12 +-
 lisp/net/tramp-gvfs.el                             |  105 +-
 lisp/net/tramp-integration.el                      |   18 +-
 lisp/net/tramp-rclone.el                           |   10 +-
 lisp/net/tramp-sh.el                               |  463 +--
 lisp/net/tramp-smb.el                              |  266 +-
 lisp/net/tramp-sshfs.el                            |    4 +
 lisp/net/tramp-sudoedit.el                         |  174 +-
 lisp/net/tramp-uu.el                               |    2 +-
 lisp/net/tramp.el                                  |  577 ++--
 lisp/obsolete/crisp.el                             |    3 -
 lisp/{ => obsolete}/linum.el                       |   11 +
 lisp/{ => obsolete}/thumbs.el                      |   23 +-
 lisp/obsolete/url-about.el                         |    2 +-
 lisp/obsolete/vc-arch.el                           |    2 +-
 lisp/obsolete/vc-mtn.el                            |    2 +-
 lisp/org/org-macro.el                              |    2 +-
 lisp/org/org-version.el                            |    4 +-
 lisp/org/org.el                                    |    2 +-
 lisp/org/ox.el                                     |    2 +-
 lisp/outline.el                                    |  371 ++-
 lisp/paren.el                                      |   19 +-
 lisp/pcmpl-git.el                                  |  110 +
 lisp/pcmpl-gnu.el                                  |   36 +-
 lisp/pcmpl-linux.el                                |   68 +
 lisp/pcmpl-rpm.el                                  |   43 +-
 lisp/pcmpl-unix.el                                 |  490 +++-
 lisp/pcmpl-x.el                                    |   43 +
 lisp/pcomplete.el                                  |  153 +-
 lisp/pixel-scroll.el                               |  117 +-
 lisp/play/gamegrid.el                              |    6 +-
 lisp/play/hanoi.el                                 |    5 +-
 lisp/printing.el                                   |   12 +-
 lisp/proced.el                                     |  159 +-
 lisp/progmodes/cc-align.el                         |   13 +-
 lisp/progmodes/cc-awk.el                           |   25 +-
 lisp/progmodes/cc-defs.el                          |   47 +-
 lisp/progmodes/cc-engine.el                        |  531 +++-
 lisp/progmodes/cc-fonts.el                         |  305 +-
 lisp/progmodes/cc-langs.el                         |  107 +-
 lisp/progmodes/cc-mode.el                          |  108 +-
 lisp/progmodes/compile.el                          |    4 +-
 lisp/progmodes/cperl-mode.el                       |   53 +-
 lisp/progmodes/elisp-mode.el                       |    4 +-
 lisp/progmodes/etags.el                            |    4 +-
 lisp/progmodes/flymake.el                          |    2 +-
 lisp/progmodes/gdb-mi.el                           |   11 +-
 lisp/progmodes/glasses.el                          |   16 +-
 lisp/progmodes/gud.el                              |  225 +-
 lisp/progmodes/hideif.el                           |    8 +-
 lisp/progmodes/hideshow.el                         |   25 +-
 lisp/progmodes/octave.el                           |    4 +-
 lisp/progmodes/perl-mode.el                        |    7 +
 lisp/progmodes/prog-mode.el                        |    8 +-
 lisp/progmodes/project.el                          |   24 +-
 lisp/progmodes/python.el                           |  436 ++-
 lisp/progmodes/sh-script.el                        |   98 +-
 lisp/progmodes/subword.el                          |    5 +-
 lisp/progmodes/xref.el                             |    2 +-
 lisp/recentf.el                                    |    3 +-
 lisp/repeat.el                                     |   72 +-
 lisp/replace.el                                    |  235 +-
 lisp/reveal.el                                     |   12 +-
 lisp/server.el                                     |   41 +-
 lisp/shell.el                                      |  281 ++
 lisp/simple.el                                     |  185 +-
 lisp/speedbar.el                                   |    7 +-
 lisp/startup.el                                    |   10 +-
 lisp/strokes.el                                    |   11 +-
 lisp/subr.el                                       |  286 +-
 lisp/t-mouse.el                                    |    7 +-
 lisp/tab-bar.el                                    |    1 +
 lisp/tab-line.el                                   |   68 +-
 lisp/tar-mode.el                                   |    2 +-
 lisp/term.el                                       |   19 +-
 lisp/term/fbterm.el                                |   27 +
 lisp/term/haiku-win.el                             |   39 +
 lisp/term/linux.el                                 |   10 +-
 lisp/term/pgtk-win.el                              |    1 +
 lisp/term/x-win.el                                 |   93 +-
 lisp/textmodes/artist.el                           |    2 -
 lisp/textmodes/emacs-authors-mode.el               |   15 +-
 lisp/textmodes/emacs-news-mode.el                  |   18 +-
 lisp/textmodes/flyspell.el                         |    8 +-
 lisp/textmodes/ispell.el                           |   13 +-
 lisp/textmodes/less-css-mode.el                    |    6 +-
 lisp/textmodes/page-ext.el                         |   54 +-
 lisp/textmodes/paragraphs.el                       |    6 +-
 lisp/textmodes/picture.el                          |  174 +-
 lisp/textmodes/reftex-global.el                    |   37 +-
 lisp/textmodes/remember.el                         |   20 +-
 lisp/textmodes/rst.el                              |   13 -
 lisp/textmodes/tex-mode.el                         |   77 +-
 lisp/time.el                                       |   10 +-
 lisp/url/url-gw.el                                 |   15 +-
 lisp/url/url-handlers.el                           |   19 +-
 lisp/url/url-misc.el                               |    8 +-
 lisp/url/url-parse.el                              |   11 -
 lisp/url/url-vars.el                               |    2 -
 lisp/url/url.el                                    |    4 +-
 lisp/vc/add-log.el                                 |    4 +-
 lisp/vc/diff-mode.el                               |   47 +-
 lisp/vc/ediff-wind.el                              |    8 -
 lisp/vc/log-edit.el                                |   20 +-
 lisp/vc/pcvs-util.el                               |    2 -
 lisp/vc/vc-bzr.el                                  |    2 +-
 lisp/vc/vc-cvs.el                                  |    2 +-
 lisp/vc/vc-dir.el                                  |   12 +-
 lisp/vc/vc-dispatcher.el                           |  276 +-
 lisp/vc/vc-git.el                                  |  214 +-
 lisp/vc/vc-hg.el                                   |    2 +-
 lisp/vc/vc-hooks.el                                |    9 +-
 lisp/vc/vc-svn.el                                  |    2 +-
 lisp/vc/vc.el                                      |  206 +-
 lisp/wdired.el                                     |    3 +-
 lisp/whitespace.el                                 |  284 +-
 lisp/wid-browse.el                                 |   18 +-
 lisp/wid-edit.el                                   |   17 +-
 lisp/window.el                                     |  155 +-
 lisp/winner.el                                     |    9 +-
 lisp/xdg.el                                        |   25 +
 m4/assert_h.m4                                     |   61 +
 m4/c-bool.m4                                       |   51 +
 m4/gettime.m4                                      |   31 +-
 m4/gnulib-common.m4                                |    4 +-
 m4/gnulib-comp.m4                                  |   11 +-
 m4/nanosleep.m4                                    |   17 +-
 m4/stdalign.m4                                     |  104 +-
 m4/time_h.m4                                       |    8 +-
 msdos/sed2v2.inp                                   |    1 +
 msdos/sedlibmk.inp                                 |   19 +-
 nextstep/Makefile.in                               |    2 +-
 nt/Makefile.in                                     |    4 +-
 src/alloc.c                                        |    2 +-
 src/bytecode.c                                     |    4 +-
 src/character.c                                    |   12 +-
 src/coding.c                                       |    6 +-
 src/comp.c                                         |   90 +-
 src/composite.c                                    |   50 +-
 src/composite.h                                    |    1 +
 src/conf_post.h                                    |    6 +-
 src/data.c                                         |    1 +
 src/dbusbind.c                                     |    4 +-
 src/dispnew.c                                      |   21 +-
 src/doc.c                                          |   17 +-
 src/dynlib.h                                       |    1 -
 src/editfns.c                                      |    9 +-
 src/emacs-module.c                                 |    1 -
 src/emacs-module.h.in                              |    3 +-
 src/emacs.c                                        |   22 +-
 src/eval.c                                         |   65 +-
 src/fileio.c                                       |   19 +-
 src/fns.c                                          |  128 +-
 src/font.c                                         |  792 ++---
 src/font.h                                         |    2 +-
 src/fontset.c                                      |    3 +-
 src/frame.c                                        |    2 +-
 src/ftcrfont.c                                     |   32 +-
 src/haiku_font_support.cc                          |  282 +-
 src/haiku_io.c                                     |    2 +
 src/haiku_support.cc                               |   53 +
 src/haiku_support.h                                |   34 +-
 src/haikufns.c                                     |    6 +-
 src/haikufont.c                                    |  109 +-
 src/haikuselect.c                                  |  129 +
 src/haikuterm.c                                    |   29 +-
 src/haikuterm.h                                    |    6 +
 src/hbfont.c                                       |    2 +-
 src/image.c                                        |   54 +-
 src/intervals.c                                    |    4 +-
 src/keyboard.c                                     |   89 +-
 src/lisp.h                                         |    3 +-
 src/lread.c                                        |   75 +-
 src/macuvs.h                                       | 1762 +++++------
 src/marker.c                                       |   18 -
 src/menu.c                                         |    4 -
 src/msdos.c                                        |    1 -
 src/nsfont.m                                       |  244 +-
 src/nsterm.m                                       |   53 +-
 src/pgtkfns.c                                      |    2 -
 src/process.c                                      |   38 +-
 src/sysdep.c                                       |    5 +-
 src/systhread.h                                    |    2 -
 src/term.c                                         |   40 +
 src/termchar.h                                     |    5 +
 src/w32.c                                          |   11 +
 src/w32.h                                          |    2 +
 src/w32fns.c                                       |   68 +-
 src/w32image.c                                     |    2 +-
 src/w32notify.c                                    |   12 +-
 src/widget.c                                       |   16 +-
 src/widgetprv.h                                    |    2 +
 src/window.c                                       |    3 +-
 src/xdisp.c                                        |   59 +-
 src/xfaces.c                                       |   21 +-
 src/xfns.c                                         |  157 +-
 src/xfont.c                                        |   34 +-
 src/xrdb.c                                         |  104 -
 src/xselect.c                                      |   46 +-
 src/xsettings.c                                    |   16 +-
 src/xsmfns.c                                       |    4 +-
 src/xterm.c                                        |  938 ++++--
 src/xterm.h                                        |   25 +-
 test/lisp/ansi-color-tests.el                      |    4 +-
 test/lisp/ansi-osc-tests.el                        |   57 +
 test/lisp/char-fold-tests.el                       |    2 +-
 test/lisp/dired-tests.el                           |    4 +-
 test/lisp/dnd-tests.el                             |   10 +-
 test/lisp/electric-tests.el                        |    2 +-
 .../warn-variable-set-nonvariable.el               |    3 -
 ...arn-wide-docstring-ignore-function-signature.el |    4 +
 test/lisp/emacs-lisp/bytecomp-tests.el             |   21 +-
 test/lisp/emacs-lisp/cconv-tests.el                |   10 +
 test/lisp/emacs-lisp/cl-extra-tests.el             |    2 +-
 test/lisp/emacs-lisp/cl-macs-tests.el              |   78 +-
 test/lisp/emacs-lisp/edebug-tests.el               |    3 +-
 test/lisp/emacs-lisp/ert-x-tests.el                |   15 +
 test/lisp/emacs-lisp/seq-tests.el                  |   21 +
 test/lisp/erc/erc-match-tests.el                   |  193 ++
 test/lisp/erc/erc-scenarios-base-reconnect.el      |   45 +-
 test/lisp/erc/erc-scenarios-base-reuse-buffers.el  |   35 +-
 test/lisp/erc/erc-tests.el                         |    2 +-
 .../erc/resources/base/assoc/samenet/chester.eld   |    2 +-
 .../erc/resources/base/assoc/samenet/tester.eld    |    2 +-
 .../erc/resources/base/assoc/samenet/tester2.eld   |    2 +-
 .../erc/resources/base/netid/samenet/chester.eld   |    2 +-
 .../erc/resources/base/netid/samenet/tester.eld    |    2 +-
 test/lisp/erc/resources/erc-d/erc-d-tests.el       |    6 +-
 test/lisp/eshell/esh-cmd-tests.el                  |   19 +
 test/lisp/eshell/esh-io-tests.el                   |  292 ++
 test/lisp/eshell/esh-proc-tests.el                 |  155 +-
 test/lisp/eshell/esh-var-tests.el                  |   12 +-
 test/lisp/eshell/eshell-tests-helpers.el           |   10 +
 test/lisp/eshell/eshell-tests.el                   |   19 -
 test/lisp/filenotify-tests.el                      |  143 +-
 test/lisp/files-tests.el                           |   47 +-
 test/lisp/format-spec-tests.el                     |   11 +
 test/lisp/help-tests.el                            |   58 +-
 test/lisp/{ => image}/image-dired-tests.el         |    0
 test/lisp/image/image-dired-util-tests.el          |   71 +
 test/lisp/image/wallpaper-tests.el                 |   86 +
 test/lisp/international/ucs-normalize-tests.el     |   89 +-
 test/lisp/md4-tests.el                             |    2 +-
 test/lisp/net/hmac-md5-tests.el                    |    2 +-
 test/lisp/net/mailcap-tests.el                     |  405 +++
 test/lisp/net/tramp-archive-tests.el               |   36 +-
 test/lisp/net/tramp-tests.el                       |  114 +-
 test/lisp/{ => obsolete}/thumbs-tests.el           |    0
 test/lisp/pcomplete-tests.el                       |  100 +
 .../cperl-mode-resources/cperl-bug-11996.pl        |    8 +
 .../cperl-mode-resources/cperl-indents.erts        |   26 +
 test/lisp/progmodes/cperl-mode-tests.el            |   45 +
 test/lisp/progmodes/hideshow-tests.el              |  106 +
 test/lisp/progmodes/python-tests.el                |   60 +-
 test/lisp/so-long-tests/so-long-tests-helpers.el   |   12 +-
 test/lisp/sort-tests.el                            |    2 +-
 test/lisp/subr-tests.el                            |   42 +-
 test/lisp/tabify-tests.el                          |    4 +-
 test/lisp/textmodes/reftex-tests.el                |  173 ++
 test/lisp/whitespace-tests.el                      |  247 ++
 test/lisp/xdg-tests.el                             |   10 +
 test/manual/BidiCharacterTest.txt                  |   12 +-
 test/{src => manual}/image-tests.el                |  127 +-
 test/src/buffer-tests.el                           |  193 ++
 test/src/casefiddle-tests.el                       |    6 +-
 test/src/comp-tests.el                             |  157 +-
 test/src/data-tests.el                             |    3 +-
 test/src/emacs-module-resources/mod-test.c         |    1 -
 test/src/eval-tests.el                             |   20 -
 test/src/fns-tests.el                              |  118 +-
 test/src/image-tests.el                            |  190 +-
 test/src/print-tests.el                            |    6 +-
 test/src/process-tests.el                          |    4 +-
 644 files changed, 28716 insertions(+), 13491 deletions(-)

diff --git a/.dir-locals.el b/.dir-locals.el
index 7812beb001..84617a7980 100644
--- a/.dir-locals.el
+++ b/.dir-locals.el
@@ -5,7 +5,9 @@
          (sentence-end-double-space . t)
          (fill-column . 70)
         (emacs-lisp-docstring-fill-column . 65)
-         (bug-reference-url-format . "https://debbugs.gnu.org/%s";)))
+         (vc-git-annotate-switches . "-w")
+         (bug-reference-url-format . "https://debbugs.gnu.org/%s";)
+        (diff-add-log-use-relative-names . t)))
  (c-mode . ((c-file-style . "GNU")
             (c-noise-macro-names . ("INLINE" "ATTRIBUTE_NO_SANITIZE_UNDEFINED" 
"UNINIT" "CALLBACK" "ALIGN_STACK"))
             (electric-quote-comment . nil)
@@ -17,7 +19,8 @@
                (electric-quote-string . nil)
               (mode . bug-reference-prog)))
  (log-edit-mode . ((log-edit-font-lock-gnu-style . t)
-                   (log-edit-setup-add-author . t)))
+                   (log-edit-setup-add-author . t)
+                  (vc-git-log-edit-summary-target-len . 50)))
  (change-log-mode . ((add-log-time-zone-rule . t)
                     (fill-column . 74)
                     (mode . bug-reference)))
@@ -26,6 +29,7 @@
                      (electric-quote-comment . nil)
                      (electric-quote-string . nil)
                     (mode . bug-reference-prog)))
+ (lisp-data-mode . ((indent-tabs-mode . nil)))
  (texinfo-mode . ((electric-quote-comment . nil)
                   (electric-quote-string . nil)
                  (mode . bug-reference-prog)))
diff --git a/.gitignore b/.gitignore
index 0ecbcd061f..c10b3b33d3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -53,6 +53,7 @@ src/emacs-module.h
 
 # C-level sources built by 'make'.
 lib/alloca.h
+lib/assert.h
 lib/byteswap.h
 lib/dirent.h
 lib/errno.h
@@ -330,3 +331,4 @@ manual/
 
 # Ignore a directory used by dap-mode.
 .vscode
+/test/gmp.h
diff --git a/.mailmap b/.mailmap
new file mode 100644
index 0000000000..8454eb9154
--- /dev/null
+++ b/.mailmap
@@ -0,0 +1,197 @@
+#
+# This list is used to fix a few misspelled names in various git
+# listings (e.g., "git log").  This can be used to fix incorrect
+# attribution, poor display, or names showing up more than once.
+# It also allows updating an old email addresses to a new one.
+#
+# See "man git-shortlog" for more information on the format.
+#
+# Keep file sorted using `M-x sort-lines'.
+#
+Aaron S. Hawley <aaron.s.hawley@gmail.com> <Aaron.Hawley@vtinfo.com>
+Aaron S. Hawley <aaron.s.hawley@gmail.com> <Aaron.S.Hawley@gmail.com>
+Aaron S. Hawley <aaron.s.hawley@gmail.com> <ashawley@burlingtontelecom.net>
+Alan Third <alan@idiocy.org>
+Alan Third <alan@idiocy.org> <alan@breton-build.holly.idiocy.org>
+Alan Third <alan@idiocy.org> Alan Third <address@hidden>
+Alan Third <alan@idiocy.org> bug-gnu-emacs@gnu.org <bug-gnu-emacs@gnu.org>
+Alex Harsanyi <AlexHarsanyi@gmail.com> <harsanyi@mac.com>
+Alexander Gramiak <agrambot@gmail.com>
+Amin Bandali <bandali@gnu.org> <mab@gnu.org>
+Andrea Corallo <akrl@sdf.org>
+Andrea Corallo <akrl@sdf.org> <akrl@sdf.com>
+Andrea Corallo <akrl@sdf.org> <andcor03@e112547.nice.arm.com>
+Andrea Corallo <akrl@sdf.org> <andrea_corallo@yahoo.it>
+Andrew G Cohen <cohen@andy.bu.edu>
+Andrew G Cohen <cohen@andy.bu.edu> <cohen@bu.edu>
+Arash Esbati <arash@gnu.org> <arash.esbati@gmail.com>
+Arash Esbati <arash@gnu.org> <esbati@gmx.de>
+Artur Malabarba <bruce.connor.am@gmail.com> <am12548@it055607.users.bris.ac.uk>
+Bastien Guerry <bzg@gnu.org>
+Bastien Guerry <bzg@gnu.org> <bastien1@free.fr>
+Bastien Guerry <bzg@gnu.org> <bzg@altern.org>
+Benjamin Schwerdtner <Benjamin.Schwerdtner@gmail.com>
+Bob Rogers <rogers@rgrjr.com> <rogers-emacs@rgrjr.homedns.org>
+Bruno Félix Rezende Ribeiro <oitofelix@gnu.org> <oitofelix@gmail.com>
+Carlos Pita <carlosjosepita@gmail.com>
+Chong Yidong <cyd@gnu.org> <cyd@stupidchicken.com>
+Christoph Scholtes <cschol2112@gmail.com>
+Christoph Scholtes <cschol2112@gmail.com> <cschol2112@googlemail.com>
+Christoph Scholtes <cschol2112@gmail.com> Christoph Scholtes <>
+Clément Pit-Claudel <clement.pitclaudel@live.com>
+Clément Pit-Claudel <clement.pitclaudel@live.com> <clement.pit@gmail.com>
+Courtney Bane <emacs-bugs-7626@cbane.org>
+Daiki Ueno <ueno@gnu.org> <ueno@unixuser.org>
+Daiki Ueno <ueno@gnu.org> Daiki Ueno <ueno@debian>
+Dan Nicolaescu <dann@ics.uci.edu> <dann@gnu.org>
+Dan Nicolaescu <dann@ics.uci.edu> <done@ece.arizona.edu>
+Daniel Colascione <dancol@dancol.org> <dan.colascione@gmail.com>
+David Abrahams <dave@boostpro.com>
+David M. Koppelman <koppel@ece.lsu.edu>
+Deniz Dogan <deniz@dogan.se> <deniz.a.m.dogan@gmail.com>
+Dick R. Chiang <dick.r.chiang@gmail.com>
+Dick R. Chiang <dick.r.chiang@gmail.com> dickmao <none>
+Earl Hyatt <ej32u@protonmail.com>
+Earl Hyatt <ej32u@protonmail.com> <okamsn@protonmail.com>
+Edward M. Reingold <reingold@emr.cs.iit.edu>
+Eli Zaretskii <eliz@gnu.org> <eliz@is.elta.co.il>
+Emilio C. Lopes <eclig@gmx.net>
+Enami Tsugutomo <tsugutomo.enami@jp.sony.com>
+Era Eriksson <era+emacs@iki.fi> <era+emacsbugs@iki.fi>
+Eric Ludlam <zappo@gnu.org>
+Eric Ludlam <zappo@gnu.org> <eric@siege-engine.com>
+Eric Ludlam <zappo@gnu.org> <ericludlam@gmail.com>
+Eric S. Raymond <esr@thyrsus.com> <esr@snark.thyrsus.com>
+Etienne Prud’homme <e.e.f.prudhomme@gmail.com>
+Fabián Ezequiel Gallina <fgallina@gnu.org> <fgallina@cuca>
+Fabián Ezequiel Gallina <fgallina@gnu.org> <galli.87@gmail.com>
+Francis Litterio <flitterio@gmail.com>
+Gabor Vida <vidagabor@gmail.com>
+Gerd Möllmann <gerd@gnu.org>
+Gerd Möllmann <gerd@gnu.org> <gerd.moellmann@gmail.com>
+Glenn Morris <rgm@gnu.org>
+Glenn Morris <rgm@gnu.org> <rgm@fencepost>
+Glenn Morris <rgm@gnu.org> <rgm@stanford.edu>
+Gnus developers <ding@gnus.org.noreply> <ding@gnus.org>
+Gregory Heytings <gregory@heytings.org> <ghe@sdf.org>
+Grégoire Jadi <daimrod@gmail.com>
+Ian Dunn <dunni@gnu.org>
+Jan Djärv <jan.h.d@swipnet.se>
+Jan Djärv <jan.h.d@swipnet.se> <jhd@f20.localdomain>
+Jason Rumney <jasonr@gnu.org> <jasonr@wanchan>
+Jeff Walsh <fejfighter@gmail.com> <jawalsh@localhost.localdomain>
+Jeff Walsh <fejfighter@gmail.com> <jeff.walsh@drtusers-MacBook-Pro.local>
+Jeff Walsh <fejfighter@gmail.com> <jewalsh@redhat.com>
+Jens Lechtenbörger <jens.lechtenboerger@fsfe.org>
+Jim Blandy <jimb@red-bean.com> <jimb@redhat.com>
+Jimmy Aguilar Mena <spacibba@aol.com>
+Joakim Verona <joakim@verona.se>
+Joakim Verona <joakim@verona.se> <root@exodia.verona.se>
+John Wiegley <johnw@newartisans.com> <jwiegley@gmail.com>
+Jose A. Ortega Ruiz <jao@gnu.org>
+João Távora <joaotavora@gmail.com>
+Julien Danjou <julien@danjou.info> <jd@dex.adm.naquadah.org>
+Julien Danjou <julien@danjou.info> Julien Danjou <jd@abydos>
+Juri Linkov <juri@linkov.net> <juri@jurta.org>
+Jérémy Compostella <jeremy.compostella@gmail.com>
+Jürgen Hötzel <juergen@archlinux.org>
+Karl Fogel <kfogel@red-bean.com> <karl.fogel@canonical.com>
+Katsumi Yamaoka <yamaoka@jpl.org> <katsumi@flagship2>
+Kaushal Modi <kaushal.modi@gmail.com>
+Kelvin White <kwhite@gnu.org>
+Kelvin White <kwhite@gnu.org> <kelvin.white77@gmail.com>
+Ken Raeburn <raeburn@raeburn.org> <raeburn@permabit.com>
+Kenichi Handa <handa@gnu.org>
+Kenichi Handa <handa@gnu.org> <handa@etlken>
+Kenichi Handa <handa@gnu.org> <handa@m17n.org>
+Kenjiro Nakayama <nakayamakenjiro@gmail.com>
+Kjartan Óli Ágústsson <kjartanoli@outlook.com>
+Károly Lőrentey <lorentey@elte.hu>
+Lars Ingebrigtsen <larsi@gnus.org>
+Lars Ingebrigtsen <larsi@gnus.org> <larsi@emkay.local>
+Lars Ingebrigtsen <larsi@gnus.org> <larsi@openbsd6.gnus.org>
+Lars Ingebrigtsen <larsi@gnus.org> <larsi@quimbies.gnus.org>
+Lars Ingebrigtsen <larsi@gnus.org> <larsi@stories.gnus.org>
+Laurence Warne <laurencewarne@gmail.com>
+Lin Sun <lin.sun@zoom.us>
+Ludovic Courtès <ludo@gnu.org>
+Luke Lee <luke.yx.lee@gmail.com>
+Martin Rudalics <rudalics@gmx.at>
+Martin Rudalics <rudalics@gmx.at> <“rudalics@gmx.at”>
+Masatake YAMATO <yamato@redhat.com> <jet@gyve.org>
+Matt Armstrong <matt@rfc20.org> <marmstrong@google.com>
+Matt Armstrong <matt@rfc20.org> <matt@mdeb>
+Mattias Engdegård <mattiase@acm.org>
+Maxim Nikulin <manikulin@gmail.com>
+Michael Albinus <michael.albinus@gmx.de> <albinus@detlef>
+Michalis V <mvar.40k@gmail.com>
+Miha Rihtaršič <miha@kamnitnik.top>
+Morgan J. Smith <Morgan.J.Smith@outlook.com>
+Nick Drozd <nicholasdrozd@gmail.com>
+Nicolas Petton <nicolas@petton.fr> <petton.nicolas@gmail.com>
+Nitish Chandra <nitishchandrachinta@gmail.com>
+Noam Postavsky <npostavs@gmail.com> <npostavs@users.sourceforge.net>
+Noam Postavsky <npostavs@gmail.com> <npostavs@users.sourceforget.net>
+Paul Eggert <eggert@cs.ucla.edu> <eggert@Penguin.CS.UCLA.EDU>
+Paul Eggert <eggert@cs.ucla.edu> <eggert@day>
+Paul Eggert <eggert@cs.ucla.edu> <eggert@twinsun.com>
+Paul Eggert <eggert@cs.ucla.edu> <eggert@union>
+Peter J. Weisberg <pj@irregularexpressions.net>
+Peter Oliver <p.d.oliver@mavit.org.uk> <bzr@mavit.org.uk>
+Peter Oliver <p.d.oliver@mavit.org.uk> <git@mavit.org.uk>
+Philip Kaludercic <philipk@posteo.net>
+Philip Kaludercic <philipk@posteo.net> <philip.kaludercic@fau.de>
+Philip Kaludercic <philipk@posteo.net> <philip@icterid>
+Philip Kaludercic <philipk@posteo.net> <philip@warpmail.net>
+Philipp Stephani <phst@google.com>
+Philipp Stephani <phst@google.com> Philipp Stephani <p.stephani2@gmail.com>
+Phillip Lord <phillip.lord@russet.org.uk> <phillip.lord@newcastle.ac.uk>
+Pierre Lorenzon <devel@pollock-nageoire.net>
+Pieter van Oostrum <pieter@vanoostrum.org> <pieter-l@vanoostrum.org>
+Pip Cet <pipcet@gmail.com>
+Po Lu <luangruo@yahoo.com>
+Po Lu <luangruo@yahoo.com> Po Lu via <emacs-devel@gnu.org>
+Przemysław Wojnowski <esperanto@cumego.com>
+Rasmus <rasmus@gmx.us>
+Richard M. Stallman <rms@gnu.org>
+Robert J. Chassell <bob@gnu.org> <bob@rattlesnake.com>
+Robert Weiner <rsw@gnu.org> <rswgnu@gmail.com>
+Roland Winkler <winkler@gnu.org> <Roland.Winkler@physik.uni-erlangen.de>
+Ronnie Schnell <ronnie@driver-aces.com>
+Ryan C. Thompson <rct@thompsonclan.org>
+Sam Steingold <sds@gnu.org> <sdsg@amazon.com>
+Simen Heggestøyl <simenheg@runbox.com>
+Simen Heggestøyl <simenheg@runbox.com> <simenheg@ifi.uio.no>
+Simen Heggestøyl <simenheg@runbox.com> <simenheg@gmail.com>
+Simon Josefsson <simon@josefsson.org> <jas@extundo.com>
+Stefan Kangas <stefankangas@gmail.com> <stefan@marxist.se>
+Stefan Monnier <monnier@iro.umontreal.ca> <monnier@IRO.UMontreal.CA>
+Stephen Berman <stephen.berman@gmx.net> <Stephen.Berman@gmx.net>
+Stephen Berman <stephen.berman@gmx.net> <Stephen.Berman@gmx.net>
+Stephen Berman <stephen.berman@gmx.net> <steve@rosalinde.fritz.box>
+Stephen Gildea <stepheng+emacs@gildea.com>
+Stephen Gildea <stepheng+emacs@gildea.com> <gildea@stop.mail-abuse.org>
+Stephen Gildea <stepheng+emacs@gildea.com> 
<stepheng+git-config-global@gildea.com>
+Stephen Gildea <stepheng+emacs@gildea.com> <stepheng+savannah@gildea.com>
+Tassilo Horn <tsdh@gnu.org> <tassilo@member.fsf.org>
+Ted Zlatanov <tzz@lifelogs.com>
+Thien-Thi Nguyen <ttn@gnu.org> <ttn@gnuvola.org>
+Thierry Volpiatto <thievol@posteo.net> <thierry.volpiatto@gmail.com>
+Tino Calancha <ccalancha@suse.com> <f92capac@gmail.com>
+Tino Calancha <ccalancha@suse.com> <tino.calancha@gmail.com>
+Tom Tromey <tom@tromey.com> <tromey@redhat.com>
+Ulf Jasper <ulf.jasper@web.de> Ulf Jasper <>
+Ulf Jasper <ulf.jasper@web.de> Ulf Jasper <ulf@uthinkpad>
+Ulrich Müller <ulm@gentoo.org>
+Vinicius Jose Latorre <viniciusjl@ig.com.br> <viniciusjl.gnu@gmail.com>
+Vladimir Nikishkin <lockywolf@gmail.com> 
<for.emacs-table.el-environment-patch_2022-05-09@lockywolf.net>
+Werner Lemberg <wl@gnu.org>
+Wolfgang Scherer <wolfgang.scherer@gmx.de> <Wolfgang.Scherer@gmx.de>
+Xi Lu <lx@shellcodes.org>
+Xue Fuqiao <xfq.free@gmail.com> <xfq@gnu.org>
+Yilkal Argaw <yilkalargawworkneh@gmail.com>
+Yuuki Harano <masm+github@masm11.me> <masm@masm11.ddo.jp>
+Óscar Fuentes <ofv@wanadoo.es>
+İ. Göktuğ Kayaalp <self@gkayaalp.com>
+Łukasz Stelmach <stlman@poczta.fm> <l.stelmach@samsung.com>
+Łukasz Stelmach <stlman@poczta.fm> <lukasz.stelmach@iem.pw.edu.pl>
diff --git a/ChangeLog.3 b/ChangeLog.3
index 700a210f35..a09dc29cbe 100644
--- a/ChangeLog.3
+++ b/ChangeLog.3
@@ -1,3 +1,314 @@
+2022-09-06  Stefan Kangas  <stefankangas@gmail.com>
+
+       * doc/misc/idlwave.texi (Troubleshooting): Don't say "Emacsen".
+
+2022-09-06  Stefan Kangas  <stefankangas@gmail.com>
+
+       Don't mention very old Emacs versions in docs
+
+       * doc/misc/mh-e.texi (Conventions):
+       * doc/misc/reftex.texi (Problems and Work-Arounds):
+       * doc/misc/viper.texi (Loading Viper): Delete references to
+       very old versions of Emacs.
+
+2022-09-05  Stefan Kangas  <stefankangas@gmail.com>
+
+       * lisp/server.el: Improve Commentary.
+
+2022-09-05  Gregory Heytings  <gregory@heytings.org>
+
+       Explain how the font appearance can be fine-tuned in fbterm.
+
+       * doc/misc/efaq.texi (Emacs in a Linux console): Briefly document
+       Xft font specifications with which the font appearance can be
+       fine-tuned.
+
+2022-09-04  Kyle Meyer  <kyle@kyleam.com>
+
+       Update to Org 9.5.5
+
+2022-09-03  Stefan Monnier  <monnier@iro.umontreal.ca>
+
+       * lisp/emacs-lisp/comp.el (comp-run-async-workers): Fail more gracefully
+
+       Otherwise Emacs may fail to start if it can't find a writable
+       `~/.emacs.d/eln-cache` directory.
+       Fixes bug#57562.  See also Debian's bug #1017739.
+
+2022-09-03  Stefan Kangas  <stefankangas@gmail.com>
+
+       Update acknowledgments
+
+       * doc/emacs/ack.texi (Acknowledgments): Update.
+       * doc/emacs/emacs.texi (Acknowledgments): Add several names from
+       Author: headers.
+
+2022-09-01  Stefan Kangas  <stefankangas@gmail.com>
+
+       Make some versions in docs match package version
+
+       * doc/emacs/misc.texi (Interactive Shell): Bump Emacs version.
+       * doc/misc/ediff.texi:
+       * doc/misc/flymake.texi:
+       * doc/misc/viper.texi: Fix version to match package.
+       * lisp/emulation/viper.el: Make version match variable.
+
+2022-09-01  Stefan Kangas  <stefankangas@gmail.com>
+
+       Minor doc fix; improve sorting of VC backends
+
+       * doc/emacs/maintaining.texi (Version Control Systems): Minor doc fix;
+       rearrange list to put git, cvs and subversion at the top.
+
+2022-09-01  Eli Zaretskii  <eliz@gnu.org>
+
+       Clarify the doc string of 'set-face-attribute'
+
+       * lisp/faces.el (set-face-attribute): Clarify the issue with
+       resetting attribute values to 'unspecified' for future frames.
+       (Bug#57499)
+
+2022-08-30  Gregory Heytings  <gregory@heytings.org>
+
+       Enable 256 colors in fbterm.
+
+       * lisp/term/fbterm.el: New file.
+
+       * doc/misc/efaq.texi (Emacs in a Linux console): Document the TERM
+       environment variable with which the new file is used.
+
+2022-08-30  Eli Zaretskii  <eliz@gnu.org>
+
+       One more fix for find-file.el
+
+       * lisp/find-file.el (ff-get-file-name): Use 'expand-file-name'
+       instead of 'concat', which doesn't DTRT with absolute file names.
+       (ff-other-file-alist): Yet another doc fix.  (Bug#57325)
+
+2022-08-29  Gregory Heytings  <gregory@heytings.org>
+
+       Recommend using fbterm in the Linux console.
+
+       * doc/misc/efaq.texi (Emacs in a Linux console): New node.
+       (Common requests): Entry for the new node.
+
+       * etc/PROBLEMS (Linux console problems...): Mention the new FAQ node.
+
+2022-08-29  Eli Zaretskii  <eliz@gnu.org>
+
+       * lisp/find-file.el (ff-other-file-alist): Doc fix.  (Bug#57325)
+
+2022-08-28  Eli Zaretskii  <eliz@gnu.org>
+
+       * lisp/info.el (Info-mode): Support the Linux console better.
+
+2022-08-28  Eli Zaretskii  <eliz@gnu.org>
+
+       Improve the documentation of glyphless-character display
+
+       * lisp/international/characters.el (glyphless-char-display-control):
+       * src/xdisp.c (syms_of_xdisp) <glyphless-char-display>: Mention
+       the 'glyphless-char' face in the doc string.
+
+       * doc/lispref/display.texi (Glyphless Chars): Index
+       'glyphless-char' face.
+
+2022-08-27  Eli Zaretskii  <eliz@gnu.org>
+
+       Fix documentation of 'glyphless-char-display'
+
+       * src/xdisp.c (syms_of_xdisp)<glyphless-char-display>: Doc fix.
+       (gui_produce_glyphs, lookup_glyphless_char_display): Fix
+       indentation.
+
+2022-08-25  Robert Pluim  <rpluim@gmail.com>
+
+       Treat smtp-auth method from auth-info as a symbol
+
+       The lookup of the SMTP auth method is done based on symbols, but
+       sometimes the requested value comes from `auth-info', in which case it
+       is a string, so call `intern-soft' to convert it to a symbol (which
+       does nothing if it's already a symbol).
+
+       * lisp/mail/smtpmail.el (smtpmail-try-auth-methods): Call
+       `intern-soft' on the smtp-auth key's value.  (Bug#57373)
+
+       Do not merge to master
+
+2022-08-25  Stefan Kangas  <stefankangas@gmail.com>
+
+       * lisp/wdired.el: Improve "Commentary" section.
+
+       * lisp/wdired.el: Doc fix; don't mention obsolete variable.
+
+       * lisp/progmodes/etags.el (next-file): Minor doc fix.
+
+2022-08-25  Andreas Schwab  <schwab@suse.de>
+
+       * configure.ac: Move AC_LANG_PUSH/POP out of AC_CACHE_CHECK.  
(Bug#57380)
+
+       (cherry picked from commit ce82300221f270241fdda1f5dfb567bdb1208543)
+
+2022-08-21  Kyle Meyer  <kyle@kyleam.com>
+
+       Update to Org 9.5.4-19-g4dff42
+
+2022-08-21  Eli Zaretskii  <eliz@gnu.org>
+
+       * lisp/find-file.el (ff-other-file-alist): Doc fix.  (Bug#57325)
+
+2022-08-19  Stefan Kangas  <stefankangas@gmail.com>
+
+       Resurrect obsoletion warning for two functions
+
+       These were supposed to have been deleted, but never were.  Resurrect
+       their obsoletion warning and let's delete them in Emacs 29 instead.
+
+       * lisp/subr.el (process-filter-multibyte-p)
+       (set-process-filter-multibyte): Resurrect obsoletion warning.
+       * etc/NEWS: Don't announce their deletion.
+
+2022-08-19  Alan Mackenzie  <acm@muc.de>
+
+       * src/window.c (select_window): Fix assert for buffer = non-active 
minibuffer
+
+2022-08-19  Gerd Möllmann  <gerd@gnu.org>
+
+       Find libgccjit on macOS with Homebrew differently
+
+       * configure.ac (MAC_LIBS): Find libgccjit's directory slightly
+       differently for brew installations.
+
+2022-08-18  Stefan Kangas  <stefankangas@gmail.com>
+
+       Improve image-mode-as-hex docstring
+
+       * lisp/image-mode.el: Fix typos.
+       (image-mode-as-hex): Doc fix; say that it uses 'hexl-mode' and reflow.
+
+2022-08-18  Stefan Kangas  <stefankangas@gmail.com>
+
+       * lisp/image-mode.el (image-mode-as-hex): Fix toggle instructions.
+
+       * lisp/image-mode.el: Improve commentary.
+
+2022-08-18  Colin Woodbury  <colin@fosskers.ca>
+
+       cl-reduce doc string improvement
+
+       * lisp/emacs-lisp/cl-seq.el (cl-reduce): Explain what happens when
+       using :from-end (bug#57273).
+
+2022-08-18  Paul Eggert  <eggert@cs.ucla.edu>
+
+       Backport tempname changes from master (bug#57129)
+
+       * lib/tempname.c: Backport from master, which uses current Gnulib.
+
+2022-08-16  Stefan Kangas  <stefankangas@gmail.com>
+
+       Revert "; * doc/lispintro/emacs-lisp-intro.texi: Fix typo."
+
+       This reverts commit 9d0dba44da7ac83d018fff3c26d33dac12ebd806.
+
+       This was not a typo, but incorrectly matching parens in Info-mode.
+
+2022-08-16  Stefan Kangas  <stefankangas@gmail.com>
+
+       * doc/misc/gnus.texi (Article Washing): Fix Links URL.
+
+2022-08-12  Stefan Kangas  <stefan@marxist.se>
+
+       Delete references to deleted library hilit19.el
+
+       * doc/misc/gnus.texi (Compatibility):
+       * lisp/progmodes/f90.el:
+       * lisp/ps-print.el:
+       * lisp/vc/ediff.el: Delete references to hilit19.el.
+
+2022-08-12  Stefan Kangas  <stefan@marxist.se>
+
+       Delete stale comments from Lisp Intro manual
+
+       * doc/lispintro/emacs-lisp-intro.texi (Args as Variable or List)
+       (print-elements-of-list, Miscellaneous): Delete some references to
+       Emacs 22.
+
+2022-08-11  Stefan Kangas  <stefan@marxist.se>
+
+       Don't list Emacs as requirement for built-in package
+
+       * doc/misc/htmlfontify.texi (Requirements): Don't list Emacs as
+       requirement for built-in package.
+
+2022-08-11  YAMAMOTO Mitsuharu  <mituharu@math.s.chiba-u.ac.jp>
+
+       Fix wrong metrics for bitmap-only fonts with HarfBuzz 5
+
+       * src/ftcrfont.c (ftcrhbfont_begin_hb_font): Always use the standard
+       position unit value on HarfBuzz 5 and later regardless of whether the
+       font is bitmap-only or not.  (Bug#57066)
+
+2022-08-09  Stefan Kangas  <stefan@marxist.se>
+
+       Improve wording when documenting other TRAMP syntaxes
+
+       * doc/misc/tramp.texi (Change file name syntax): Improve wording.
+       (Bug#57061)
+
+2022-08-08  Stefan Kangas  <stefan@marxist.se>
+
+       * lisp/vc/diff-mode.el: Don't mention XEmacs.
+
+2022-08-08  Stefan Kangas  <stefan@marxist.se>
+
+       Don't mention XEmacs toolbar in ediff manual
+
+       * doc/misc/ediff.texi (Other Session Commands): Don't mention XEmacs
+       specific toolbar support for now.  This can be changed back once the
+       toolbar is ported to Emacs.
+
+2022-08-06  Eli Zaretskii  <eliz@gnu.org>
+
+       * etc/PROBLEMS: Problems with Alacritty and Emoji.  (Bug#56952)
+
+2022-08-06  Yuga Ego  <yet@ego.team>
+
+       Link from (emacs)Init Syntax to (elisp)Introduction
+
+       * doc/emacs/custom.texi (Init Syntax): Link to the ELisp manual 
(Bug#56870)
+
+2022-08-06  Stefan Kangas  <stefan@marxist.se>
+
+       Don't mention removed XEmacs support in reftex manual
+
+       * doc/misc/reftex.texi (Installation, Imprint): Don't mention
+       removed XEmacs support.
+
+2022-08-06  Stefan Kangas  <stefan@marxist.se>
+
+       Don't mention removed XEmacs support in idlwave manual
+
+       * doc/misc/idlwave.texi (Lesson I---Development Cycle)
+       (Syntax Highlighting, Windows and macOS, Troubleshooting): Delete
+       most references to XEmacs.  Support for it was deleted in 28.1.
+
+2022-08-05  Stefan Kangas  <stefan@marxist.se>
+
+       * lisp/play/fortune.el: Doc fixes.
+
+2022-08-04  Stefan Kangas  <stefan@marxist.se>
+
+       * doc/lispref/loading.texi (Autoload by Prefix): Fix typo.
+
+2022-08-03  Philipp Stephani  <phst@google.com>
+
+       * lisp/uniquify.el (uniquify-buffer-name-style): Quote apostrophe.
+
+2022-08-02  Stefan Kangas  <stefan@marxist.se>
+
+       * lisp/term.el: Doc fix; don't mention rlogin.
+
 2022-07-31  Eli Zaretskii  <eliz@gnu.org>
 
        * src/lisp.h (CHECK_INTEGER): Fix the predicate.  (Bug#56856)
@@ -236607,7 +236918,7 @@
 
 This file records repository revisions from
 commit 9d56a21e6a696ad19ac65c4b405aeca44785884a (exclusive) to
-commit 78759ddcb0fc7dd75a7a8edfb2c19dc2f1d86ee2 (inclusive).
+commit ddabb03a0176beb4b7fc8d4f2267d459fd2ebded (inclusive).
 See ChangeLog.2 for earlier changes.
 
 ;; Local Variables:
diff --git a/GNUmakefile b/GNUmakefile
index 8eb61dc0ad..05edbe099b 100644
--- a/GNUmakefile
+++ b/GNUmakefile
@@ -115,7 +115,7 @@ endif
 
 # 'make bootstrap' in a fresh checkout needn't run 'configure' twice.
 bootstrap: Makefile
-       $(MAKE) -f Makefile all
+       $(MAKE) -f Makefile bootstrap-all
 
 .PHONY: bootstrap default $(ORDINARY_GOALS)
 
diff --git a/Makefile.in b/Makefile.in
index d288bacb9d..741a4c5538 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -366,7 +366,67 @@ endif
 
 gsettings_SCHEMAS = etc/org.gnu.emacs.defaults.gschema.xml
 
-all: ${SUBDIR} info $(gsettings_SCHEMAS:.xml=.valid) src-depending-on-lisp
+all:
+       $(MAKE) actual-all || $(MAKE) advice-on-failure make-target=all 
exit-status=$$?
+       $(MAKE) sanity-check make-target=all
+
+# This target is used by the 'bootstrap' target in GNUmakefile, instead of 
'all'.
+bootstrap-all:
+       $(MAKE) actual-all || $(MAKE) advice-on-failure make-target=bootstrap 
exit-status=$$?
+       $(MAKE) sanity-check make-target=bootstrap
+
+.PHONY: bootstrap-all actual-all advice-on-failure sanity-check
+
+actual-all: ${SUBDIR} info $(gsettings_SCHEMAS:.xml=.valid) 
src-depending-on-lisp
+
+# ADVICE-ON-FAILURE-BEGIN:all
+# You could try to:
+# - run "make bootstrap", which might fix the problem
+# - run "make V=1", which displays the full commands invoked by make,
+#   to further investigate the problem
+# ADVICE-ON-FAILURE-END:all
+
+# ADVICE-ON-FAILURE-BEGIN:bootstrap
+# You could try to:
+# - run "make extraclean" and run "make" again (or, equivalently, run
+#   "make bootstrap configure=default"), to rebuild Emacs with the
+#   default configuration options, which might fix the problem
+# - run "git clean -fdx" and run "make bootstrap" again, which might
+#   fix the problem if "make bootstrap configure=default" did not
+#   !BEWARE! "git clean -fdx" deletes all files that are not under
+#   !BEWARE! version control, which means that all changes to such
+#   !BEWARE! files will be lost and cannot be restored later
+# - run "make V=1", which displays the full commands invoked by make,
+#   to further investigate the problem
+# - report the problem and ask for help by sending an email to
+#   bug-gnu-emacs@gnu.org, mentioning at least the build error
+#   message, the platform, and the repository revision displayed by
+#   "git rev-parse HEAD"
+# ADVICE-ON-FAILURE-END:bootstrap
+
+advice-on-failure:
+       @echo >&2 '***'
+       @echo >&2 '*** '"\"make ${make-target}\" failed with exit status 
${exit-status}."
+       @echo >&2 '***'
+       @cat Makefile | \
+         sed -n '/^# ADVICE-ON-FAILURE-BEGIN:${make-target}/,$${p;/^# 
ADVICE-ON-FAILURE-END:${make-target}/q};' | \
+         sed 's/^# /*** /' | grep -v '^*** ADVICE-ON-FAILURE-' >&2
+       @echo >&2 '***'
+       @exit ${exit-status}
+
+sanity-check:
+       @v=$$(src/emacs${EXEEXT} --batch --eval \
+         '(progn (defun f (n) (if (= 0 n) 1 (* n (f (- n 1))))) (princ (f 
10)))' \
+         2> /dev/null); \
+       [ "X$$v" = "X3628800" ] && exit 0; \
+       echo >&2 '***'; \
+       echo >&2 '*** '"\"make ${make-target}\" succeeded, but Emacs is not 
functional."; \
+       echo >&2 '***'; \
+       cat Makefile | \
+         sed -n '/^# ADVICE-ON-FAILURE-BEGIN:${make-target}/,$${p;/^# 
ADVICE-ON-FAILURE-END:${make-target}/q};' | \
+         sed 's/^# /*** /' | grep -v '^*** ADVICE-ON-FAILURE-' >&2; \
+       echo >&2 '***'; \
+       exit 1
 
 .PHONY: all ${SUBDIR} blessmail epaths-force epaths-force-w32 
epaths-force-ns-self-contained etc-emacsver
 
@@ -522,7 +582,7 @@ $(srcdir)/configure: $(srcdir)/configure.ac 
$(srcdir)/m4/*.m4
 ## don't have to duplicate the list of utilities to install in
 ## this Makefile as well.
 
-install: all install-arch-indep install-etcdoc install-arch-dep 
install-$(NTDIR) blessmail install-eln install-gsettings-schemas
+install: actual-all install-arch-indep install-etcdoc install-arch-dep 
install-$(NTDIR) blessmail install-eln install-gsettings-schemas
        @true
 
 ## Ensure that $subdir contains a subdirs.el file.
@@ -638,8 +698,8 @@ install-arch-indep: lisp install-info install-man 
${INSTALL_ARCH_INDEP_EXTRA}
          [ -d $${dir} ] || exit 1 ; \
          dest="$$1" ; shift ; \
          if [ -d "$${dest}" ]; then \
-           exp_dest=`cd "$${dest}" && /bin/pwd`; \
-           [ "$$exp_dest" = "`cd $${dir} && /bin/pwd`" ] && continue ; \
+           exp_dest=`cd "$${dest}" && pwd -P`; \
+           [ "$$exp_dest" = "`cd $${dir} && pwd -P`" ] && continue ; \
          else true; \
          fi; \
          rm -rf "$${dest}" ; \
@@ -695,8 +755,8 @@ install-arch-indep: lisp install-info install-man 
${INSTALL_ARCH_INDEP_EXTRA}
 install-etcdoc: src install-arch-indep
        -unset CDPATH; \
        umask 022; ${MKDIR_P} "$(DESTDIR)${etcdocdir}" ; \
-       exp_etcdocdir=`cd "$(DESTDIR)${etcdocdir}"; /bin/pwd`; \
-       if [ "`cd ./etc; /bin/pwd`" != "$$exp_etcdocdir" ]; \
+       exp_etcdocdir=`cd "$(DESTDIR)${etcdocdir}"; pwd -P`; \
+       if [ "`cd ./etc; pwd -P`" != "$$exp_etcdocdir" ]; \
        then \
           docfile="DOC"; \
           printf 'Copying %s to %s ...\n' "etc/$$docfile" \
@@ -711,9 +771,9 @@ install-etcdoc: src install-arch-indep
 install-info: info
        umask 022; ${MKDIR_P} "$(DESTDIR)${infodir}"
        -unset CDPATH; \
-       thisdir=`/bin/pwd`; \
-       exp_infodir=`cd "$(DESTDIR)${infodir}" && /bin/pwd`; \
-       if [ "`cd ${srcdir}/info && /bin/pwd`" = "$$exp_infodir" ]; then \
+       thisdir=`pwd -P`; \
+       exp_infodir=`cd "$(DESTDIR)${infodir}" && pwd -P`; \
+       if [ "`cd ${srcdir}/info && pwd -P`" = "$$exp_infodir" ]; then \
          true; \
        else \
           [ -f "$(DESTDIR)${infodir}/dir" ] || \
@@ -742,7 +802,7 @@ install-info: info
 ## but not sure if portable.
 install-man:
        umask 022; ${MKDIR_P} "$(DESTDIR)${man1dir}"
-       thisdir=`/bin/pwd`; \
+       thisdir=`pwd -P`; \
        cd ${mansrcdir}; \
        for page in *.1; do \
          test "$$page" = ChangeLog.1 && continue; \
@@ -809,7 +869,7 @@ install-etc:
          ${srcdir}/etc/emacs.service > $${tmp}; \
        $(INSTALL_DATA) $${tmp} 
"$(DESTDIR)$(systemdunitdir)/${EMACS_NAME}.service"; \
        rm -f $${tmp}
-       thisdir=`/bin/pwd`; \
+       thisdir=`pwd -P`; \
        cd ${iconsrcdir} || exit 1; umask 022 ; \
        for dir in */*/apps */*/mimetypes; do \
          [ -d $${dir} ] || continue ; \
@@ -844,10 +904,10 @@ uninstall: uninstall-$(NTDIR) uninstall-doc 
uninstall-gsettings-schemas
        rm -f "$(DESTDIR)$(includedir)/emacs-module.h"
        $(MAKE) -C lib-src uninstall
        -unset CDPATH; \
-       for dir in "$(DESTDIR)${lispdir}" "$(DESTDIR)${etcdir}" ; do    \
+       for dir in "$(DESTDIR)${lispdir}" "$(DESTDIR)${etcdir}" 
"$(ELN_DESTDIR)" ; do   \
          if [ -d "$${dir}" ]; then                     \
-           case `cd "$${dir}" ; /bin/pwd` in           \
-             "`cd ${srcdir} ; /bin/pwd`"* ) ;;         \
+           case `cd "$${dir}" ; pwd -P` in             \
+             "`cd ${srcdir} ; pwd -P`"* ) ;;           \
              * ) rm -rf "$${dir}" ;;                   \
            esac ;                                      \
            case "$${dir}" in                           \
@@ -858,7 +918,7 @@ uninstall: uninstall-$(NTDIR) uninstall-doc 
uninstall-gsettings-schemas
          fi ;                                          \
        done
        -rm -rf "$(DESTDIR)${libexecdir}/emacs/${version}"
-       thisdir=`/bin/pwd`; \
+       thisdir=`pwd -P`; \
        (info_misc=`MAKEFLAGS= $(MAKE) --no-print-directory -s -C doc/misc 
echo-info`; \
         if cd "$(DESTDIR)${infodir}"; then \
           for elt in ${INFO_NONMISC} $${info_misc}; do \
@@ -1170,7 +1230,11 @@ check-info: info
 ### This first cleans the lisp subdirectory, removing all compiled
 ### Lisp files.  Then re-run make to build all the files anew.
 
-.PHONY: bootstrap
+.PHONY: bootstrap actual-bootstrap
+
+bootstrap:
+       $(MAKE) actual-bootstrap || $(MAKE) advice-on-failure 
make-target=bootstrap exit-status=$$?
+       $(MAKE) sanity-check make-target=bootstrap
 
 # Without a 'configure' variable, bootstrapping does the following:
 #  * Remove files to start from a bootstrap-clean slate.
@@ -1181,7 +1245,7 @@ check-info: info
 #  * Remove files to start from an extraclean slate.
 #  * Do the actual build, during which the 'configure' variable is
 #    used (see the Makefile goal in GNUmakefile).
-bootstrap:
+actual-bootstrap:
 ifndef configure
        $(MAKE) bootstrap-clean
        cd $(srcdir) && ./autogen.sh autoconf
@@ -1189,7 +1253,7 @@ ifndef configure
 else
        $(MAKE) extraclean
 endif
-       $(MAKE) all
+       $(MAKE) actual-all
 
 .PHONY: ChangeLog change-history change-history-commit change-history-nocommit
 .PHONY: preferred-branch-is-current unchanged-history-files
diff --git a/admin/admin.el b/admin/admin.el
index c84287a702..6a67f172e2 100644
--- a/admin/admin.el
+++ b/admin/admin.el
@@ -124,9 +124,6 @@ Root must be the root of an Emacs source tree."
   ;; Major version only.
   (when (string-match "\\([0-9]\\{2,\\}\\)" version)
     (let ((newmajor (match-string 1 version)))
-      (set-version-in-file root "src/msdos.c" newmajor
-                           (rx (and "Vwindow_system_version" (1+ not-newline)
-                                    ?\( (submatch (1+ (in "0-9"))) ?\))))
       (set-version-in-file root "etc/refcards/ru-refcard.tex" newmajor
                            "\\\\newcommand{\\\\versionemacs}\\[0\\]\
 {\\([0-9]\\{2,\\}\\)}.+%.+version of Emacs")))
@@ -781,6 +778,204 @@ Optional argument TYPE is type of output (nil means all)."
     (if (member type (list nil m))
        (make-manuals-dist--1 root m))))
 
+(defvar admin--org-export-headers-format "\
+#+title: GNU Emacs %s NEWS -- history of user-visible changes
+#+author:
+#+options: author:nil creator:nil toc:2 num:3 *:nil \\n:t ^:nil tex:nil
+#+language: en
+#+HTML_LINK_HOME: /software/emacs
+#+HTML_LINK_UP: /software/emacs
+#+html_head_extra: <link rel=\"stylesheet\" type=\"text/css\" 
href=\"/mini.css\" media=\"handheld\" />
+#+html_head_extra: <link rel=\"stylesheet\" type=\"text/css\" 
href=\"/layout.min.css\" media=\"screen\" />
+#+html_head_extra: <link rel=\"stylesheet\" type=\"text/css\" 
href=\"/print.min.css\" media=\"print\" />
+
+#+BEGIN_EXPORT html
+<div style=\"float:right;margin-left:1em;padding:3px;border:0px 
solid;text-align:center\">
+<a href=\"/graphics/gnu-head.jpg\">
+<img src=\"/graphics/gnu-head-sm.jpg\" alt=\" [image of the head
+of a GNU] \" width=\"129\" height=\"122\"/>
+</a>
+</div>
+#+END_EXPORT\n\n")
+
+(defvar admin--org-html-postamble "
+<p>
+Return to the <a href=\"/software/emacs/emacs.html\">GNU Emacs home page</a>.
+</p>
+
+<div id=\"footer\">
+<div class=\"unprintable\">
+
+<p>
+Please send FSF &amp; GNU inquiries to
+<a href=\"mailto:gnu@gnu.org\";>&lt;gnu@gnu.org&gt;</a>.
+There are also <a href=\"/contact/\">other ways to contact</a>
+the FSF.
+Broken links and other corrections or suggestions can be sent to
+<a href=\"mailto:bug-gnu-emacs@gnu.org\";>&lt;bug-gnu-emacs@gnu.org&gt;</a>.
+</p>
+</div>
+
+<p>
+    Copyright &copy; %s Free Software Foundation, Inc.
+</p>
+
+<p>This page is licensed under
+a <a href=\"https://creativecommons.org/licenses/by-sa/4.0\";>CC-BY-SA</a>
+license.</p>
+
+<!--#include virtual=\"/server/bottom-notes.html\" -->
+
+<p class=\"unprintable\">
+Updated:
+<!-- timestamp start -->
+$Date: %s $
+<!-- timestamp end -->
+</p>
+</div>
+</div>")
+
+(defun admin--require-external-package (pkg)
+  (package-initialize)
+  (require pkg nil t)
+  (unless (featurep pkg)
+    (when (yes-or-no-p (format "Package \"%s\" is missing.  Install now?" pkg))
+      (package-install pkg)
+      (require pkg nil t))))
+
+(defvar org-html-postamble)
+(defvar org-html-mathjax-template)
+(defun make-news-html-file (root version)
+  "Convert the NEWS file into an HTML file."
+  (interactive (let ((root
+                      (if noninteractive
+                          (or (pop command-line-args-left)
+                              default-directory)
+                        (read-directory-name "Emacs root directory: "
+                                             source-directory nil t))))
+                 (list root
+                       (read-string "Major version number: "
+                                    (number-to-string emacs-major-version)))))
+  (unless (file-exists-p (expand-file-name "src/emacs.c" root))
+    (user-error "%s doesn't seem to be the root of an Emacs source tree" root))
+  (admin--require-external-package 'htmlize)
+  (let* ((newsfile (expand-file-name "etc/NEWS" root))
+         (orgfile (expand-file-name (format "etc/NEWS.%s.org" version) root))
+         (html (format "%s.html" (file-name-base orgfile)))
+         (copyright-years (format-time-string "%Y")))
+    (delete-file orgfile)
+    (copy-file newsfile orgfile t)
+    (find-file orgfile)
+
+    ;; Find the copyright range.
+    (goto-char (point-min))
+    (re-search-forward "^Copyright (C) \\([0-9-]+\\) Free Software Foundation, 
Inc.")
+    (setq copyright-years (match-string 1))
+
+    ;; Delete some unnecessary stuff.
+    (replace-regexp-in-region "^---$" "" (point-min) (point-max))
+    (replace-regexp-in-region "^\\+\\+\\+$" "" (point-min) (point-max))
+    (dolist (str '("\n"
+                   "GNU Emacs NEWS -- history of user-visible changes."
+                   "Temporary note:"
+                   "+++ indicates that all relevant manuals in doc/ have been 
updated."
+                   "--- means no change in the manuals is needed."
+                   "When you add a new item, use the appropriate mark if you 
are sure it"
+                   "applies, and please also update docstrings as needed."
+                   "You can narrow news to a specific version by calling 
'view-emacs-news'"
+                   "with a prefix argument or by typing 'C-u C-h C-n'."))
+      (replace-string-in-region str "" (point-min) (point-max)))
+
+    ;; Escape some characters.
+    (replace-regexp-in-region (rx "$") "@@html:&dollar;@@" (point-min) 
(point-max))
+
+    ;; Use Org-mode markers for 'symbols', 'C-x k', etc.
+    (replace-regexp-in-region
+     (rx (or (: (group (in " \t\n("))
+                "'"
+                (group (+ (or (not (in "'\n"))
+                              (: "'" (not (in " .,\t\n)"))))))
+                "'"
+                (group (in ",.;:!? \t\n)")))
+             ;; Buffer names, e.g. "*scratch*".
+             (: "\""
+                (group-n 2 "*" (+ (not (in "*\""))) "*")
+                "\"")))
+     "\\1~\\2~\\3" (point-min) (point-max))
+
+    ;; Format code blocks.
+    (while (re-search-forward "^    " nil t)
+      (let ((elisp-block (looking-at "(")))
+        (backward-paragraph)
+        (insert (if elisp-block
+                    "\n#+BEGIN_SRC emacs-lisp"
+                  "\n#+BEGIN_EXAMPLE"))
+        (forward-paragraph)
+        (insert (if elisp-block
+                    "#+END_SRC\n"
+                  "#+END_EXAMPLE\n"))))
+
+    ;; Delete buffer local variables.
+    (goto-char (point-max))
+    (when (re-search-backward "Local variables:")
+      (forward-line -1)
+      (delete-region (point) (point-max)))
+
+    ;; Insert Org-mode export headers.
+    (goto-char (point-min))
+    (insert (format admin--org-export-headers-format version))
+    (org-mode)
+    (save-buffer)
+
+    ;; Make everything one level lower.
+    (goto-char (point-min))
+    (while (re-search-forward (rx bol (group (+ "*")) " ") nil t)
+      (replace-match "*\\1" nil nil nil 1))
+
+    ;; Insert anchors for different versions.
+    (goto-char (point-min))
+    (let (last-major last-minor)
+      (while (re-search-forward (rx bol "** " (+ (not "\n")) "in Emacs "
+                                    (group digit digit) "." (group digit)
+                                    eol)
+                                nil t)
+        (unless (and (equal (match-string 1) last-major)
+                     (equal (match-string 2) last-minor))
+          (setq last-major (match-string 1))
+          (setq last-minor (match-string 2))
+          (forward-line -1)
+          (insert (format
+                   (concat
+                    "#+HTML: <p>&nbsp;</p>\n"
+                    "* Changes in Emacs %s.%s\n"
+                    ;; Add anchor to allow linking to
+                    ;; e.g. "NEWS.28.html#28.1".
+                    ":PROPERTIES:\n"
+                    ":CUSTOM_ID: %s.%s\n"
+                    ":END:\n")
+                   last-major last-minor
+                   last-major last-minor)))))
+
+    (save-buffer)
+
+    ;; Make the HTML export.
+    (let* ((org-html-postamble
+            (format admin--org-html-postamble
+                    copyright-years
+                    ;; e.g. "2022/09/13 09:13:13"
+                    (format-time-string "%Y/%m/%d %H:%m:%S")))
+           (org-html-mathjax-template "")
+           (htmlize-output-type 'css))
+      (org-html-export-as-html))
+
+    ;; Write HTML to file.
+    (let ((html (expand-file-name html (expand-file-name "etc" root))))
+      (write-file html)
+      (unless noninteractive
+        (find-file html)
+        (html-mode))
+      (message "Successfully exported HTML to %s" html))))
+
 
 ;; Stuff to check new `defcustom's got :version tags.
 ;; Adapted from check-declare.el.
diff --git a/admin/automerge b/admin/automerge
index 9919186736..c7c17dfb5e 100755
--- a/admin/automerge
+++ b/admin/automerge
@@ -35,6 +35,8 @@
 ## it with the -d option in the repository directory, in case a pull
 ## updates this script while it is working.
 
+set -o nounset
+
 die ()                 # write error to stderr and exit
 {
     [ $# -gt 0 ] && echo "$PN: $*" >&2
diff --git a/admin/cus-test.el b/admin/cus-test.el
index 5894abed3d..22d5a3a151 100644
--- a/admin/cus-test.el
+++ b/admin/cus-test.el
@@ -272,7 +272,7 @@ currently defined groups."
        (if group
            (memq symbol groups)
          (or
-          ;; (user-variable-p symbol)
+           ;; (custom-variable-p symbol)
           (get symbol 'standard-value)
           ;; (get symbol 'saved-value)
           (get symbol 'custom-type)))
diff --git a/admin/emake b/admin/emake
index 8b2114b3f8..e2f38501e9 100755
--- a/admin/emake
+++ b/admin/emake
@@ -20,7 +20,20 @@ if [ -f /proc/cpuinfo ]; then
        sed 's/^[0-9]*/+/')))
 fi
 
-make FAST=true -j$cores "$@" 2>&1 | \
+NOCOLOR=0
+NOCHECK=0
+FASTOPT="FAST=true"
+QUIETER=0
+while :
+do
+    [[ "X$1" == "X--no-color" ]] && { NOCOLOR=1; shift; continue; }
+    [[ "X$1" == "X--no-check" ]] && { NOCHECK=1; shift; continue; }
+    [[ "X$1" == "X--no-fast" ]] && { FASTOPT=""; shift; continue; }
+    [[ "X$1" == "X--quieter" ]] && { QUIETER=1; shift; continue; }
+    break
+done
+
+make $FASTOPT -j$cores "$@" 2>&1 | \
 sed -u 's# \.\./\.\./# #
 s# \.\./# #
 s#^Configuring local git # Configuring local git #
@@ -30,6 +43,7 @@ s#^Configured for # Configured for #
 s#^./temacs.*#  \\& #
 s#^make.*Error#  \\& #
 s#^Dumping under the name.*#  \\& #
+:a;/\\$/N;s/\\\n//;ta
 ' | \
 grep -E --line-buffered -v "^make|\
 ^Loading|\
@@ -82,16 +96,38 @@ The GNU allocators don't work|\
 ^\^\(\(|\
 ^ANCIENT=yes make|\
 ^touch -t|\
-^'build-aux/git-hooks\
+^'build-aux/git-hooks|\
+^GNUmakefile:[0-9]*: There seems to be no |\
+^GNUmakefile:[0-9]*: Running |\
+^GNUmakefile:[0-9]*: No Makefile|\
+^rm -f |\
+^rm -rf|\
+^find \. |\
+^rm -fr deps|\
+^if test -f \./\.gdbinit|\
+^true|\
+^for file in |\
+^rmdir|\
+^\[ \"\.\" = \"\.\" \]\
 " | \
 while read
 do
   C=""
-  [[ "X${REPLY:0:1}" != "X " ]] && C="\033[1;31m"
-  [[ "X${REPLY:0:3}" == "X   " ]] && C="\033[1;31m"
-  [[ "X$C" == "X" ]] && printf "%s\n" "$REPLY" || printf "$C%s\033[0m\n" 
"$REPLY"
+  (($NOCOLOR == 0)) && [[ "X${REPLY:0:1}" != "X " ]] && C="\033[1;31m"
+  (($NOCOLOR == 0)) && [[ "X${REPLY:0:3}" == "X   " ]] && C="\033[1;31m"
+  if (($QUIETER == 0))
+  then
+      [[ "X$C" == "X" ]] && printf "%s\n" "$REPLY" || printf "$C%s\033[0m\n" 
"$REPLY"
+  else
+      [[ "X$C" == "X" ]] && printf "%-80s\r" "$REPLY" || printf 
"$C%-80s\033[0m\n" "$REPLY"
+  fi
 done
 
+# If make failed, exit now with its error code.
+((${PIPESTATUS[0]} != 0)) && exit ${PIPESTATUS[0]}
+
+(($NOCHECK == 1)) && exit 0
+
 # Run a "make check" on all test files belonging to files that have
 # changed since last time.
 make -j$cores check-maybe 2>&1 | \
diff --git a/admin/gitmerge.el b/admin/gitmerge.el
index 25bed949ad..ddd3e18442 100644
--- a/admin/gitmerge.el
+++ b/admin/gitmerge.el
@@ -97,11 +97,14 @@ If nil, the function `gitmerge-default-branch' guesses.")
 
 (defvar gitmerge-mode-map
   (let ((map (make-keymap)))
-    (define-key map [(l)] 'gitmerge-show-log)
-    (define-key map [(d)] 'gitmerge-show-diff)
-    (define-key map [(f)] 'gitmerge-show-files)
-    (define-key map [(s)] 'gitmerge-toggle-skip)
-    (define-key map [(m)] 'gitmerge-start-merge)
+    (define-key map [(l)] #'gitmerge-show-log)
+    (define-key map [(d)] #'gitmerge-show-diff)
+    (define-key map [(f)] #'gitmerge-show-files)
+    (define-key map [(s)] #'gitmerge-toggle-skip)
+    (define-key map [(m)] #'gitmerge-start-merge)
+    ;; For convenience:
+    (define-key map [(n)] #'next-line)
+    (define-key map [(p)] #'previous-line)
     map)
   "Keymap for gitmerge major mode.")
 
@@ -631,12 +634,18 @@ Branch FROM will be prepended to the list."
       (with-current-buffer
          (gitmerge-setup-log-buffer gitmerge--commits gitmerge--from)
        (goto-char (point-min))
-       (insert (propertize "Commands: " 'font-lock-face 'bold)
-               "(s) Toggle skip, (l) Show log, (d) Show diff, "
-               "(f) Show files, (m) Start merge\n"
-               (propertize "Flags:    " 'font-lock-face 'bold)
-               "(C) Detected backport (cherry-mark), (R) Matches skip "
-               "regexp, (M) Manually picked\n\n")
+        (insert (substitute-command-keys
+                 (concat
+                  (propertize "Commands: " 'font-lock-face 'bold)
+                  "\\<gitmerge-mode-map>"
+                  "(\\[gitmerge-toggle-skip]) Toggle skip, "
+                  "(\\[gitmerge-show-log]) Show log, "
+                  "(\\[gitmerge-show-diff]) Show diff, "
+                  "(\\[gitmerge-show-files]) Show files, "
+                  "(\\[gitmerge-start-merge]) Start merge\n"
+                  (propertize "Flags:    " 'font-lock-face 'bold)
+                  "(C) Detected backport (cherry-mark), (R) Matches skip "
+                  "regexp, (M) Manually picked\n\n")))
        (gitmerge-mode)
        (pop-to-buffer (current-buffer))
        (if noninteractive (gitmerge-start-merge))))))
diff --git a/admin/grammars/Makefile.in b/admin/grammars/Makefile.in
index 4ca88982cd..178c79b7a0 100644
--- a/admin/grammars/Makefile.in
+++ b/admin/grammars/Makefile.in
@@ -35,7 +35,7 @@ unexport EMACSDATA EMACSDOC EMACSLOADPATH EMACSPATH
 
 EMACS = ${top_builddir}/src/emacs
 emacs = "${EMACS}" -batch --no-site-file --no-site-lisp \
-  --eval '(setq max-specpdl-size 5000)' --eval '(setq load-prefer-newer t)'
+  --eval '(setq load-prefer-newer t)'
 
 make_bovine = ${emacs} -l semantic/bovine/grammar -f bovine-batch-make-parser
 make_wisent = ${emacs} -l semantic/wisent/grammar -f wisent-batch-make-parser
diff --git a/admin/make-manuals b/admin/make-manuals
index 8085412cc8..cb0c00a423 100755
--- a/admin/make-manuals
+++ b/admin/make-manuals
@@ -33,6 +33,8 @@
 
 ### Code:
 
+set -o nounset
+
 die ()                          # write error to stderr and exit
 {
     [ $# -gt 0 ] && echo "$PN: $@" >&2
diff --git a/admin/make-tarball.txt b/admin/make-tarball.txt
index a60fead267..9a406b24fa 100644
--- a/admin/make-tarball.txt
+++ b/admin/make-tarball.txt
@@ -52,10 +52,12 @@ General steps (for each step, check for possible errors):
       ./autogen.sh
       ./configure --with-native-compilation && make
 
-    For a release (as opposed to pretest), delete any left-over "---"
-    and "+++" markers from etc/NEWS, as well as the "Temporary note"
-    section at the beginning of that file, and commit etc/NEWS if it
-    was modified.
+    For a release (as opposed to pretest), visit etc/NEWS and use the
+    "M-x emacs-news-delete-temporary-markers" command to delete any
+    left-over "---" and "+++" markers from etc/NEWS, as well as the
+    "Temporary note" section at the beginning of that file, and commit
+    etc/NEWS if it was modified.  For a bug fix release (e.g. 28.2),
+    delete any empty headlines too.
 
 2.  Regenerate the versioned ChangeLog.N and etc/AUTHORS files.
 
@@ -118,12 +120,13 @@ General steps (for each step, check for possible errors):
 
     Set the version number to that of the actual release (commit in
     one, as described above).  Pick a date about a week from now when
-    you intend to make the release.  Use M-x add-release-logs to add
-    entries to etc/HISTORY and the ChangeLog file.  It's best not to
-    commit these files until the release is actually made.  Merge the
-    entries from (unversioned) ChangeLog into the top of the current
-    versioned ChangeLog.N and commit that along with etc/HISTORY.
-    Then you can tag that commit as the release.
+    you intend to make the release.  Use M-x add-release-logs from
+    admin/admin.el to add entries to etc/HISTORY and the ChangeLog
+    file.  It's best not to commit these files until the release is
+    actually made.  Merge the entries from (unversioned) ChangeLog
+    into the top of the current versioned ChangeLog.N and commit that
+    along with etc/HISTORY.  Then you can tag that commit as the
+    release.
 
     Alternatively, you can commit and tag with the RC tag right away,
     and delay the final tagging until you actually decide to make a
@@ -163,7 +166,10 @@ General steps (for each step, check for possible errors):
 
     Commit ChangeLog.N, etc/AUTHORS, lisp/ldefs-boot.el, and the files
     changed by M-x set-version.  Note that the set-version changes
-    should be committed separately, as described in step 3 above.
+    should be committed separately, as described in step 3 above, to
+    avoid them being merged to master.  The lisp/ldefs-boot.el file
+    should not be merged to master either, so it could be added to the
+    same commit or committed separately.
 
     The easiest way of doing that is "C-x v d ROOT-DIR RET", then go
     to the first modified file, press 'M' to mark all modified files,
diff --git a/admin/merge-gnulib b/admin/merge-gnulib
index 4dd6a4d222..d3c5520ad0 100755
--- a/admin/merge-gnulib
+++ b/admin/merge-gnulib
@@ -43,7 +43,7 @@ GNULIB_MODULES='
   nanosleep nproc nstrftime
   pathmax pipe2 pselect pthread_sigmask
   qcopy-acl readlink readlinkat regex
-  sig2str sigdescr_np socklen stat-time std-gnu11 stdalign stddef stdio
+  sig2str sigdescr_np socklen stat-time std-gnu11 stdalign stdbool stddef stdio
   stpcpy strnlen strtoimax symlink sys_stat sys_time
   tempname time time_r time_rz timegm timer-time timespec-add timespec-sub
   update-copyright unlocked-io utimensat
@@ -54,7 +54,7 @@ AVOIDED_MODULES='
   btowc chmod close crypto/af_alg dup fchdir fstat langinfo lock
   mbrtowc mbsinit memchr mkdir msvc-inval msvc-nothrow nl_langinfo
   openat-die opendir pthread-h raise
-  save-cwd select setenv sigprocmask stat stdarg stdbool
+  save-cwd select setenv sigprocmask stat stdarg
   threadlib tzset unsetenv utime utime-h
   wchar wcrtomb wctype-h
 '
diff --git a/admin/notes/repo b/admin/notes/repo
index f6004a97db..c2d7f993a0 100644
--- a/admin/notes/repo
+++ b/admin/notes/repo
@@ -124,6 +124,11 @@ This ChangeLog file is not put into the repository.
 'make change-history' copies all newer ChangeLog entries into the
 start of the newest ChangeLog history file.  These ChangeLog entries
 are thereafter considered to be old, so later uses of 'make ChangeLog'
-and/or 'make change-history' will no longer copy the entries.  To
-alter ChangeLog history, run 'make change-history', then edit
-the ChangeLog history files manually and commit your changes.
+and/or 'make change-history' will no longer copy the entries.
+
+To alter ChangeLog history, run 'make change-history' and commit the
+changes made by that command.  Then edit the ChangeLog history files
+manually and commit those changes in a second, distinct commit.
+Altering ChangeLog history like this can make things harder for those
+who handle merging branches and Emacs releases, so reserve it for
+correcting more serious mistakes.
diff --git a/admin/notes/unicode b/admin/notes/unicode
index f699f4fb1c..014bfb9b0d 100644
--- a/admin/notes/unicode
+++ b/admin/notes/unicode
@@ -103,6 +103,10 @@ modified to follow suit.  If there's trailing whitespace in
 BidiCharacterTest.txt, it should be removed before committing the new
 version.
 
+src/macuvs.h is a generated file, but if it has changed as a result
+of the updates, please commit it as well (see
+admin/unidata/Makefile.in for an explanation).
+
 Visit "emoji-data.txt" with the rebuilt Emacs, and check that an
 appropriate font is being used for the emoji (by default Emacs uses
 "Noto Color Emoji").  Running the following command in that buffer
diff --git a/admin/notes/www b/admin/notes/www
index 61a80e3d19..d1a8f0637f 100644
--- a/admin/notes/www
+++ b/admin/notes/www
@@ -26,7 +26,7 @@ more specialized, alternative to M-x vc-dir.
 * Manual pages
 
 The scripts admin/make-manuals, admin/upload-manuals can be used to do
-a complete update of the on-line manual pages (eg after a release).
+a complete update of the on-line manual pages (e.g. after a release).
 
 * Renaming pages, redirects
 
@@ -99,7 +99,7 @@ https://lists.gnu.org/r/bug-gnulib/2012-12/msg00072.html
 To use something other than CVS, convert the web-pages CVS repository
 to the other VCS, then set up a two-way sync between them.
 It needs to be two-way in case eg GNU webmasters make a change to the CVS.
-Ref eg
+Ref e.g.
 https://github.com/mikjo/bigitr
 https://lists.gnu.org/r/savannah-hackers-public/2013-04/msg00022.html
 
diff --git a/admin/unidata/BidiBrackets.txt b/admin/unidata/BidiBrackets.txt
index 89698f588a..e138e7f5be 100644
--- a/admin/unidata/BidiBrackets.txt
+++ b/admin/unidata/BidiBrackets.txt
@@ -1,6 +1,6 @@
-# BidiBrackets-14.0.0.txt
-# Date: 2021-06-30, 23:59:00 GMT [AG, LI, KW]
-# © 2021 Unicode®, Inc.
+# BidiBrackets-15.0.0.txt
+# Date: 2022-05-03, 18:42:00 GMT [AG, LI, KW]
+# © 2022 Unicode®, Inc.
 # Unicode and the Unicode Logo are registered trademarks of Unicode, Inc. in 
the U.S. and other countries.
 # For terms of use, see https://www.unicode.org/terms_of_use.html
 #
diff --git a/admin/unidata/BidiMirroring.txt b/admin/unidata/BidiMirroring.txt
index bd8e2c5d00..5861d6e7f4 100644
--- a/admin/unidata/BidiMirroring.txt
+++ b/admin/unidata/BidiMirroring.txt
@@ -1,6 +1,6 @@
-# BidiMirroring-14.0.0.txt
-# Date: 2021-08-08, 22:55:00 GMT [KW, RP]
-# © 2021 Unicode®, Inc.
+# BidiMirroring-15.0.0.txt
+# Date: 2022-05-03, 18:47:00 GMT [KW, RP]
+# © 2022 Unicode®, Inc.
 # For terms of use, see https://www.unicode.org/terms_of_use.html
 #
 # Unicode Character Database
@@ -15,7 +15,7 @@
 # value, for which there is another Unicode character that typically has a 
glyph
 # that is the mirror image of the original character's glyph.
 #
-# The repertoire covered by the file is Unicode 14.0.0.
+# The repertoire covered by the file is Unicode 15.0.0.
 #
 # The file contains a list of lines with mappings from one code point
 # to another one for character-based mirroring.
diff --git a/admin/unidata/Blocks.txt b/admin/unidata/Blocks.txt
index cc5d61988b..12684594c9 100644
--- a/admin/unidata/Blocks.txt
+++ b/admin/unidata/Blocks.txt
@@ -1,10 +1,10 @@
-# Blocks-14.0.0.txt
-# Date: 2021-01-22, 23:29:00 GMT [KW]
-# © 2021 Unicode®, Inc.
-# For terms of use, see http://www.unicode.org/terms_of_use.html
+# Blocks-15.0.0.txt
+# Date: 2022-01-28, 20:58:00 GMT [KW]
+# © 2022 Unicode®, Inc.
+# For terms of use, see https://www.unicode.org/terms_of_use.html
 #
 # Unicode Character Database
-# For documentation, see http://www.unicode.org/reports/tr44/
+# For documentation, see https://www.unicode.org/reports/tr44/
 #
 # Format:
 # Start Code..End Code; Block Name
@@ -15,7 +15,7 @@
 #         and underbars are ignored.
 #         For example, "Latin Extended-A" and "latin extended a" are 
equivalent.
 #         For more information on the comparison of property values,
-#            see UAX #44: http://www.unicode.org/reports/tr44/
+#            see UAX #44: https://www.unicode.org/reports/tr44/
 #
 #  All block ranges start with a value where (cp MOD 16) = 0,
 #  and end with a value where (cp MOD 16) = 15. In other words,
@@ -241,6 +241,7 @@ FFF0..FFFF; Specials
 10D00..10D3F; Hanifi Rohingya
 10E60..10E7F; Rumi Numeral Symbols
 10E80..10EBF; Yezidi
+10EC0..10EFF; Arabic Extended-C
 10F00..10F2F; Old Sogdian
 10F30..10F6F; Sogdian
 10F70..10FAF; Old Uyghur
@@ -272,11 +273,13 @@ FFF0..FFFF; Specials
 11A50..11AAF; Soyombo
 11AB0..11ABF; Unified Canadian Aboriginal Syllabics Extended-A
 11AC0..11AFF; Pau Cin Hau
+11B00..11B5F; Devanagari Extended-A
 11C00..11C6F; Bhaiksuki
 11C70..11CBF; Marchen
 11D00..11D5F; Masaram Gondi
 11D60..11DAF; Gunjala Gondi
 11EE0..11EFF; Makasar
+11F00..11F5F; Kawi
 11FB0..11FBF; Lisu Supplement
 11FC0..11FFF; Tamil Supplement
 12000..123FF; Cuneiform
@@ -284,7 +287,7 @@ FFF0..FFFF; Specials
 12480..1254F; Early Dynastic Cuneiform
 12F90..12FFF; Cypro-Minoan
 13000..1342F; Egyptian Hieroglyphs
-13430..1343F; Egyptian Hieroglyph Format Controls
+13430..1345F; Egyptian Hieroglyph Format Controls
 14400..1467F; Anatolian Hieroglyphs
 16800..16A3F; Bamum Supplement
 16A40..16A6F; Mro
@@ -309,6 +312,7 @@ FFF0..FFFF; Specials
 1D000..1D0FF; Byzantine Musical Symbols
 1D100..1D1FF; Musical Symbols
 1D200..1D24F; Ancient Greek Musical Notation
+1D2C0..1D2DF; Kaktovik Numerals
 1D2E0..1D2FF; Mayan Numerals
 1D300..1D35F; Tai Xuan Jing Symbols
 1D360..1D37F; Counting Rod Numerals
@@ -316,9 +320,11 @@ FFF0..FFFF; Specials
 1D800..1DAAF; Sutton SignWriting
 1DF00..1DFFF; Latin Extended-G
 1E000..1E02F; Glagolitic Supplement
+1E030..1E08F; Cyrillic Extended-D
 1E100..1E14F; Nyiakeng Puachue Hmong
 1E290..1E2BF; Toto
 1E2C0..1E2FF; Wancho
+1E4D0..1E4FF; Nag Mundari
 1E7E0..1E7FF; Ethiopic Extended-B
 1E800..1E8DF; Mende Kikakui
 1E900..1E95F; Adlam
@@ -348,6 +354,7 @@ FFF0..FFFF; Specials
 2CEB0..2EBEF; CJK Unified Ideographs Extension F
 2F800..2FA1F; CJK Compatibility Ideographs Supplement
 30000..3134F; CJK Unified Ideographs Extension G
+31350..323AF; CJK Unified Ideographs Extension H
 E0000..E007F; Tags
 E0100..E01EF; Variation Selectors Supplement
 F0000..FFFFF; Supplementary Private Use Area-A
diff --git a/admin/unidata/IVD_Sequences.txt b/admin/unidata/IVD_Sequences.txt
index 886d8519ab..86a4ab5138 100644
--- a/admin/unidata/IVD_Sequences.txt
+++ b/admin/unidata/IVD_Sequences.txt
@@ -2,6 +2,9 @@
 #
 # History:
 #
+# 2022-09-13 Registration of additional sequences in the Adobe-Japan1
+#            collection.
+#
 # 2020-11-06 Registration of additional sequences in the MSARG
 #            collection.
 #
@@ -32,7 +35,7 @@
 # For more details on the IVD, see UTS #37:
 # https://www.unicode.org/reports/tr37/
 #
-# Copyright 2006-2020 Unicode, Inc.
+# Copyright 2006-2022 Unicode, Inc.
 # For terms of use, see: https://www.unicode.org/copyright.html#8
 #
 3402 E0100; Adobe-Japan1; CID+13698
@@ -39337,4 +39340,5 @@ FA29 E0100; Adobe-Japan1; CID+8687
 2EB71 E0101; Moji_Joho; MJ059252
 2EB79 E0100; Moji_Joho; MJ059255
 2EB79 E0101; Moji_Joho; MJ059256
+31350 E0100; Adobe-Japan1; CID+19130
 # EOF
diff --git a/admin/unidata/IdnaMappingTable.txt 
b/admin/unidata/IdnaMappingTable.txt
index 1b862827ef..e4c0611792 100644
--- a/admin/unidata/IdnaMappingTable.txt
+++ b/admin/unidata/IdnaMappingTable.txt
@@ -1,13 +1,13 @@
 # IdnaMappingTable.txt
-# Date: 2021-07-10, 00:49:51 GMT
-# © 2021 Unicode®, Inc.
+# Date: 2022-05-02, 19:29:26 GMT
+# © 2022 Unicode®, Inc.
 # Unicode and the Unicode Logo are registered trademarks of Unicode, Inc. in 
the U.S. and other countries.
-# For terms of use, see http://www.unicode.org/terms_of_use.html
+# For terms of use, see https://www.unicode.org/terms_of_use.html
 #
 # Unicode IDNA Compatible Preprocessing for UTS #46
-# Version: 14.0.0
+# Version: 15.0.0
 #
-# For documentation and usage, see http://www.unicode.org/reports/tr46
+# For documentation and usage, see https://www.unicode.org/reports/tr46
 #
 0000..002C    ; disallowed_STD3_valid                  # 1.1  
<control-0000>..COMMA
 002D..002E    ; valid                                  # 1.1  
HYPHEN-MINUS..FULL STOP
@@ -1278,7 +1278,8 @@
 0CE6..0CEF    ; valid                                  # 1.1  KANNADA DIGIT 
ZERO..KANNADA DIGIT NINE
 0CF0          ; disallowed                             # NA   <reserved-0CF0>
 0CF1..0CF2    ; valid                                  # 5.0  KANNADA SIGN 
JIHVAMULIYA..KANNADA SIGN UPADHMANIYA
-0CF3..0CFF    ; disallowed                             # NA   
<reserved-0CF3>..<reserved-0CFF>
+0CF3          ; valid                                  # 15.0 KANNADA SIGN 
COMBINING ANUSVARA ABOVE RIGHT
+0CF4..0CFF    ; disallowed                             # NA   
<reserved-0CF4>..<reserved-0CFF>
 0D00          ; valid                                  # 10.0 MALAYALAM SIGN 
COMBINING ANUSVARA ABOVE
 0D01          ; valid                                  # 7.0  MALAYALAM SIGN 
CANDRABINDU
 0D02..0D03    ; valid                                  # 1.1  MALAYALAM SIGN 
ANUSVARA..MALAYALAM SIGN VISARGA
@@ -1386,7 +1387,8 @@
 0EC6          ; valid                                  # 1.1  LAO KO LA
 0EC7          ; disallowed                             # NA   <reserved-0EC7>
 0EC8..0ECD    ; valid                                  # 1.1  LAO TONE MAI 
EK..LAO NIGGAHITA
-0ECE..0ECF    ; disallowed                             # NA   
<reserved-0ECE>..<reserved-0ECF>
+0ECE          ; valid                                  # 15.0 LAO YAMAKKAN
+0ECF          ; disallowed                             # NA   <reserved-0ECF>
 0ED0..0ED9    ; valid                                  # 1.1  LAO DIGIT 
ZERO..LAO DIGIT NINE
 0EDA..0EDB    ; disallowed                             # NA   
<reserved-0EDA>..<reserved-0EDB>
 0EDC          ; mapped                 ; 0EAB 0E99     # 1.1  LAO HO NO
@@ -6206,7 +6208,8 @@ FFFE..FFFF    ; disallowed                             # 
1.1  <noncharacter-FFFE
 10EAD         ; valid                  ;      ; NV8    # 13.0 YEZIDI 
HYPHENATION MARK
 10EAE..10EAF  ; disallowed                             # NA   
<reserved-10EAE>..<reserved-10EAF>
 10EB0..10EB1  ; valid                                  # 13.0 YEZIDI LETTER 
LAM WITH DOT ABOVE..YEZIDI LETTER YOT WITH CIRCUMFLEX ABOVE
-10EB2..10EFF  ; disallowed                             # NA   
<reserved-10EB2>..<reserved-10EFF>
+10EB2..10EFC  ; disallowed                             # NA   
<reserved-10EB2>..<reserved-10EFC>
+10EFD..10EFF  ; valid                                  # 15.0 ARABIC SMALL LOW 
WORD SAKTA..ARABIC SMALL LOW WORD MADDA
 10F00..10F1C  ; valid                                  # 11.0 OLD SOGDIAN 
LETTER ALEPH..OLD SOGDIAN LETTER FINAL TAW WITH VERTICAL TAIL
 10F1D..10F26  ; valid                  ;      ; NV8    # 11.0 OLD SOGDIAN 
NUMBER ONE..OLD SOGDIAN FRACTION ONE HALF
 10F27         ; valid                                  # 11.0 OLD SOGDIAN 
LIGATURE AYIN-DALETH
@@ -6271,7 +6274,8 @@ FFFE..FFFF    ; disallowed                             # 
1.1  <noncharacter-FFFE
 11213..11237  ; valid                                  # 7.0  KHOJKI LETTER 
NYA..KHOJKI SIGN SHADDA
 11238..1123D  ; valid                  ;      ; NV8    # 7.0  KHOJKI 
DANDA..KHOJKI ABBREVIATION SIGN
 1123E         ; valid                                  # 9.0  KHOJKI SIGN SUKUN
-1123F..1127F  ; disallowed                             # NA   
<reserved-1123F>..<reserved-1127F>
+1123F..11241  ; valid                                  # 15.0 KHOJKI LETTER 
QA..KHOJKI VOWEL SIGN VOCALIC R
+11242..1127F  ; disallowed                             # NA   
<reserved-11242>..<reserved-1127F>
 11280..11286  ; valid                                  # 8.0  MULTANI LETTER 
A..MULTANI LETTER GA
 11287         ; disallowed                             # NA   <reserved-11287>
 11288         ; valid                                  # 8.0  MULTANI LETTER 
GHA
@@ -6443,7 +6447,9 @@ FFFE..FFFF    ; disallowed                             # 
1.1  <noncharacter-FFFE
 11AA3..11AAF  ; disallowed                             # NA   
<reserved-11AA3>..<reserved-11AAF>
 11AB0..11ABF  ; valid                                  # 14.0 CANADIAN 
SYLLABICS NATTILIK HI..CANADIAN SYLLABICS SPA
 11AC0..11AF8  ; valid                                  # 7.0  PAU CIN HAU 
LETTER PA..PAU CIN HAU GLOTTAL STOP FINAL
-11AF9..11BFF  ; disallowed                             # NA   
<reserved-11AF9>..<reserved-11BFF>
+11AF9..11AFF  ; disallowed                             # NA   
<reserved-11AF9>..<reserved-11AFF>
+11B00..11B09  ; valid                  ;      ; NV8    # 15.0 DEVANAGARI HEAD 
MARK..DEVANAGARI SIGN MINDU
+11B0A..11BFF  ; disallowed                             # NA   
<reserved-11B0A>..<reserved-11BFF>
 11C00..11C08  ; valid                                  # 9.0  BHAIKSUKI LETTER 
A..BHAIKSUKI LETTER VOCALIC L
 11C09         ; disallowed                             # NA   <reserved-11C09>
 11C0A..11C36  ; valid                                  # 9.0  BHAIKSUKI LETTER 
E..BHAIKSUKI VOWEL SIGN VOCALIC L
@@ -6489,7 +6495,15 @@ FFFE..FFFF    ; disallowed                             # 
1.1  <noncharacter-FFFE
 11DAA..11EDF  ; disallowed                             # NA   
<reserved-11DAA>..<reserved-11EDF>
 11EE0..11EF6  ; valid                                  # 11.0 MAKASAR LETTER 
KA..MAKASAR VOWEL SIGN O
 11EF7..11EF8  ; valid                  ;      ; NV8    # 11.0 MAKASAR 
PASSIMBANG..MAKASAR END OF SECTION
-11EF9..11FAF  ; disallowed                             # NA   
<reserved-11EF9>..<reserved-11FAF>
+11EF9..11EFF  ; disallowed                             # NA   
<reserved-11EF9>..<reserved-11EFF>
+11F00..11F10  ; valid                                  # 15.0 KAWI SIGN 
CANDRABINDU..KAWI LETTER O
+11F11         ; disallowed                             # NA   <reserved-11F11>
+11F12..11F3A  ; valid                                  # 15.0 KAWI LETTER 
KA..KAWI VOWEL SIGN VOCALIC R
+11F3B..11F3D  ; disallowed                             # NA   
<reserved-11F3B>..<reserved-11F3D>
+11F3E..11F42  ; valid                                  # 15.0 KAWI VOWEL SIGN 
E..KAWI CONJOINER
+11F43..11F4F  ; valid                  ;      ; NV8    # 15.0 KAWI DANDA..KAWI 
PUNCTUATION CLOSING SPIRAL
+11F50..11F59  ; valid                                  # 15.0 KAWI DIGIT 
ZERO..KAWI DIGIT NINE
+11F5A..11FAF  ; disallowed                             # NA   
<reserved-11F5A>..<reserved-11FAF>
 11FB0         ; valid                                  # 13.0 LISU LETTER YHA
 11FB1..11FBF  ; disallowed                             # NA   
<reserved-11FB1>..<reserved-11FBF>
 11FC0..11FF1  ; valid                  ;      ; NV8    # 12.0 TAMIL FRACTION 
ONE THREE-HUNDRED-AND-TWENTIETH..TAMIL SIGN VAKAIYARAA
@@ -6511,9 +6525,11 @@ FFFE..FFFF    ; disallowed                             # 
1.1  <noncharacter-FFFE
 12FF1..12FF2  ; valid                  ;      ; NV8    # 14.0 CYPRO-MINOAN 
SIGN CM301..CYPRO-MINOAN SIGN CM302
 12FF3..12FFF  ; disallowed                             # NA   
<reserved-12FF3>..<reserved-12FFF>
 13000..1342E  ; valid                                  # 5.2  EGYPTIAN 
HIEROGLYPH A001..EGYPTIAN HIEROGLYPH AA032
-1342F         ; disallowed                             # NA   <reserved-1342F>
+1342F         ; valid                                  # 15.0 EGYPTIAN 
HIEROGLYPH V011D
 13430..13438  ; disallowed                             # 12.0 EGYPTIAN 
HIEROGLYPH VERTICAL JOINER..EGYPTIAN HIEROGLYPH END SEGMENT
-13439..143FF  ; disallowed                             # NA   
<reserved-13439>..<reserved-143FF>
+13439..1343F  ; disallowed                             # 15.0 EGYPTIAN 
HIEROGLYPH INSERT AT MIDDLE..EGYPTIAN HIEROGLYPH END WALLED ENCLOSURE
+13440..13455  ; valid                                  # 15.0 EGYPTIAN 
HIEROGLYPH MIRROR HORIZONTALLY..EGYPTIAN HIEROGLYPH MODIFIER DAMAGED
+13456..143FF  ; disallowed                             # NA   
<reserved-13456>..<reserved-143FF>
 14400..14646  ; valid                                  # 8.0  ANATOLIAN 
HIEROGLYPH A001..ANATOLIAN HIEROGLYPH A530
 14647..167FF  ; disallowed                             # NA   
<reserved-14647>..<reserved-167FF>
 16800..16A38  ; valid                                  # 6.0  BAMUM LETTER 
PHASE-A NGKUE MFON..BAMUM LETTER PHASE-F VUEQ
@@ -6615,9 +6631,13 @@ FFFE..FFFF    ; disallowed                             # 
1.1  <noncharacter-FFFE
 1B000..1B001  ; valid                                  # 6.0  KATAKANA LETTER 
ARCHAIC E..HIRAGANA LETTER ARCHAIC YE
 1B002..1B11E  ; valid                                  # 10.0 HENTAIGANA 
LETTER A-1..HENTAIGANA LETTER N-MU-MO-2
 1B11F..1B122  ; valid                                  # 14.0 HIRAGANA LETTER 
ARCHAIC WU..KATAKANA LETTER ARCHAIC WU
-1B123..1B14F  ; disallowed                             # NA   
<reserved-1B123>..<reserved-1B14F>
+1B123..1B131  ; disallowed                             # NA   
<reserved-1B123>..<reserved-1B131>
+1B132         ; valid                                  # 15.0 HIRAGANA LETTER 
SMALL KO
+1B133..1B14F  ; disallowed                             # NA   
<reserved-1B133>..<reserved-1B14F>
 1B150..1B152  ; valid                                  # 12.0 HIRAGANA LETTER 
SMALL WI..HIRAGANA LETTER SMALL WO
-1B153..1B163  ; disallowed                             # NA   
<reserved-1B153>..<reserved-1B163>
+1B153..1B154  ; disallowed                             # NA   
<reserved-1B153>..<reserved-1B154>
+1B155         ; valid                                  # 15.0 KATAKANA LETTER 
SMALL KO
+1B156..1B163  ; disallowed                             # NA   
<reserved-1B156>..<reserved-1B163>
 1B164..1B167  ; valid                                  # 12.0 KATAKANA LETTER 
SMALL WI..KATAKANA LETTER SMALL N
 1B168..1B16F  ; disallowed                             # NA   
<reserved-1B168>..<reserved-1B16F>
 1B170..1B2FB  ; valid                                  # 10.0 NUSHU 
CHARACTER-1B170..NUSHU CHARACTER-1B2FB
@@ -6668,7 +6688,9 @@ FFFE..FFFF    ; disallowed                             # 
1.1  <noncharacter-FFFE
 1D1E9..1D1EA  ; valid                  ;      ; NV8    # 14.0 MUSICAL SYMBOL 
SORI..MUSICAL SYMBOL KORON
 1D1EB..1D1FF  ; disallowed                             # NA   
<reserved-1D1EB>..<reserved-1D1FF>
 1D200..1D245  ; valid                  ;      ; NV8    # 4.1  GREEK VOCAL 
NOTATION SYMBOL-1..GREEK MUSICAL LEIMMA
-1D246..1D2DF  ; disallowed                             # NA   
<reserved-1D246>..<reserved-1D2DF>
+1D246..1D2BF  ; disallowed                             # NA   
<reserved-1D246>..<reserved-1D2BF>
+1D2C0..1D2D3  ; valid                  ;      ; NV8    # 15.0 KAKTOVIK NUMERAL 
ZERO..KAKTOVIK NUMERAL NINETEEN
+1D2D4..1D2DF  ; disallowed                             # NA   
<reserved-1D2D4>..<reserved-1D2DF>
 1D2E0..1D2F3  ; valid                  ;      ; NV8    # 11.0 MAYAN NUMERAL 
ZERO..MAYAN NUMERAL NINETEEN
 1D2F4..1D2FF  ; disallowed                             # NA   
<reserved-1D2F4>..<reserved-1D2FF>
 1D300..1D356  ; valid                  ;      ; NV8    # 4.0  MONOGRAM FOR 
EARTH..TETRAGRAM FOR FOSTERING
@@ -7701,7 +7723,9 @@ FFFE..FFFF    ; disallowed                             # 
1.1  <noncharacter-FFFE
 1DAA1..1DAAF  ; valid                                  # 8.0  SIGNWRITING 
ROTATION MODIFIER-2..SIGNWRITING ROTATION MODIFIER-16
 1DAB0..1DEFF  ; disallowed                             # NA   
<reserved-1DAB0>..<reserved-1DEFF>
 1DF00..1DF1E  ; valid                                  # 14.0 LATIN SMALL 
LETTER FENG DIGRAPH WITH TRILL..LATIN SMALL LETTER S WITH CURL
-1DF1F..1DFFF  ; disallowed                             # NA   
<reserved-1DF1F>..<reserved-1DFFF>
+1DF1F..1DF24  ; disallowed                             # NA   
<reserved-1DF1F>..<reserved-1DF24>
+1DF25..1DF2A  ; valid                                  # 15.0 LATIN SMALL 
LETTER D WITH MID-HEIGHT LEFT HOOK..LATIN SMALL LETTER T WITH MID-HEIGHT LEFT 
HOOK
+1DF2B..1DFFF  ; disallowed                             # NA   
<reserved-1DF2B>..<reserved-1DFFF>
 1E000..1E006  ; valid                                  # 9.0  COMBINING 
GLAGOLITIC LETTER AZU..COMBINING GLAGOLITIC LETTER ZHIVETE
 1E007         ; disallowed                             # NA   <reserved-1E007>
 1E008..1E018  ; valid                                  # 9.0  COMBINING 
GLAGOLITIC LETTER ZEMLJA..COMBINING GLAGOLITIC LETTER HERU
@@ -7711,7 +7735,72 @@ FFFE..FFFF    ; disallowed                             # 
1.1  <noncharacter-FFFE
 1E023..1E024  ; valid                                  # 9.0  COMBINING 
GLAGOLITIC LETTER YU..COMBINING GLAGOLITIC LETTER SMALL YUS
 1E025         ; disallowed                             # NA   <reserved-1E025>
 1E026..1E02A  ; valid                                  # 9.0  COMBINING 
GLAGOLITIC LETTER YO..COMBINING GLAGOLITIC LETTER FITA
-1E02B..1E0FF  ; disallowed                             # NA   
<reserved-1E02B>..<reserved-1E0FF>
+1E02B..1E02F  ; disallowed                             # NA   
<reserved-1E02B>..<reserved-1E02F>
+1E030         ; mapped                 ; 0430          # 15.0 MODIFIER LETTER 
CYRILLIC SMALL A
+1E031         ; mapped                 ; 0431          # 15.0 MODIFIER LETTER 
CYRILLIC SMALL BE
+1E032         ; mapped                 ; 0432          # 15.0 MODIFIER LETTER 
CYRILLIC SMALL VE
+1E033         ; mapped                 ; 0433          # 15.0 MODIFIER LETTER 
CYRILLIC SMALL GHE
+1E034         ; mapped                 ; 0434          # 15.0 MODIFIER LETTER 
CYRILLIC SMALL DE
+1E035         ; mapped                 ; 0435          # 15.0 MODIFIER LETTER 
CYRILLIC SMALL IE
+1E036         ; mapped                 ; 0436          # 15.0 MODIFIER LETTER 
CYRILLIC SMALL ZHE
+1E037         ; mapped                 ; 0437          # 15.0 MODIFIER LETTER 
CYRILLIC SMALL ZE
+1E038         ; mapped                 ; 0438          # 15.0 MODIFIER LETTER 
CYRILLIC SMALL I
+1E039         ; mapped                 ; 043A          # 15.0 MODIFIER LETTER 
CYRILLIC SMALL KA
+1E03A         ; mapped                 ; 043B          # 15.0 MODIFIER LETTER 
CYRILLIC SMALL EL
+1E03B         ; mapped                 ; 043C          # 15.0 MODIFIER LETTER 
CYRILLIC SMALL EM
+1E03C         ; mapped                 ; 043E          # 15.0 MODIFIER LETTER 
CYRILLIC SMALL O
+1E03D         ; mapped                 ; 043F          # 15.0 MODIFIER LETTER 
CYRILLIC SMALL PE
+1E03E         ; mapped                 ; 0440          # 15.0 MODIFIER LETTER 
CYRILLIC SMALL ER
+1E03F         ; mapped                 ; 0441          # 15.0 MODIFIER LETTER 
CYRILLIC SMALL ES
+1E040         ; mapped                 ; 0442          # 15.0 MODIFIER LETTER 
CYRILLIC SMALL TE
+1E041         ; mapped                 ; 0443          # 15.0 MODIFIER LETTER 
CYRILLIC SMALL U
+1E042         ; mapped                 ; 0444          # 15.0 MODIFIER LETTER 
CYRILLIC SMALL EF
+1E043         ; mapped                 ; 0445          # 15.0 MODIFIER LETTER 
CYRILLIC SMALL HA
+1E044         ; mapped                 ; 0446          # 15.0 MODIFIER LETTER 
CYRILLIC SMALL TSE
+1E045         ; mapped                 ; 0447          # 15.0 MODIFIER LETTER 
CYRILLIC SMALL CHE
+1E046         ; mapped                 ; 0448          # 15.0 MODIFIER LETTER 
CYRILLIC SMALL SHA
+1E047         ; mapped                 ; 044B          # 15.0 MODIFIER LETTER 
CYRILLIC SMALL YERU
+1E048         ; mapped                 ; 044D          # 15.0 MODIFIER LETTER 
CYRILLIC SMALL E
+1E049         ; mapped                 ; 044E          # 15.0 MODIFIER LETTER 
CYRILLIC SMALL YU
+1E04A         ; mapped                 ; A689          # 15.0 MODIFIER LETTER 
CYRILLIC SMALL DZZE
+1E04B         ; mapped                 ; 04D9          # 15.0 MODIFIER LETTER 
CYRILLIC SMALL SCHWA
+1E04C         ; mapped                 ; 0456          # 15.0 MODIFIER LETTER 
CYRILLIC SMALL BYELORUSSIAN-UKRAINIAN I
+1E04D         ; mapped                 ; 0458          # 15.0 MODIFIER LETTER 
CYRILLIC SMALL JE
+1E04E         ; mapped                 ; 04E9          # 15.0 MODIFIER LETTER 
CYRILLIC SMALL BARRED O
+1E04F         ; mapped                 ; 04AF          # 15.0 MODIFIER LETTER 
CYRILLIC SMALL STRAIGHT U
+1E050         ; mapped                 ; 04CF          # 15.0 MODIFIER LETTER 
CYRILLIC SMALL PALOCHKA
+1E051         ; mapped                 ; 0430          # 15.0 CYRILLIC 
SUBSCRIPT SMALL LETTER A
+1E052         ; mapped                 ; 0431          # 15.0 CYRILLIC 
SUBSCRIPT SMALL LETTER BE
+1E053         ; mapped                 ; 0432          # 15.0 CYRILLIC 
SUBSCRIPT SMALL LETTER VE
+1E054         ; mapped                 ; 0433          # 15.0 CYRILLIC 
SUBSCRIPT SMALL LETTER GHE
+1E055         ; mapped                 ; 0434          # 15.0 CYRILLIC 
SUBSCRIPT SMALL LETTER DE
+1E056         ; mapped                 ; 0435          # 15.0 CYRILLIC 
SUBSCRIPT SMALL LETTER IE
+1E057         ; mapped                 ; 0436          # 15.0 CYRILLIC 
SUBSCRIPT SMALL LETTER ZHE
+1E058         ; mapped                 ; 0437          # 15.0 CYRILLIC 
SUBSCRIPT SMALL LETTER ZE
+1E059         ; mapped                 ; 0438          # 15.0 CYRILLIC 
SUBSCRIPT SMALL LETTER I
+1E05A         ; mapped                 ; 043A          # 15.0 CYRILLIC 
SUBSCRIPT SMALL LETTER KA
+1E05B         ; mapped                 ; 043B          # 15.0 CYRILLIC 
SUBSCRIPT SMALL LETTER EL
+1E05C         ; mapped                 ; 043E          # 15.0 CYRILLIC 
SUBSCRIPT SMALL LETTER O
+1E05D         ; mapped                 ; 043F          # 15.0 CYRILLIC 
SUBSCRIPT SMALL LETTER PE
+1E05E         ; mapped                 ; 0441          # 15.0 CYRILLIC 
SUBSCRIPT SMALL LETTER ES
+1E05F         ; mapped                 ; 0443          # 15.0 CYRILLIC 
SUBSCRIPT SMALL LETTER U
+1E060         ; mapped                 ; 0444          # 15.0 CYRILLIC 
SUBSCRIPT SMALL LETTER EF
+1E061         ; mapped                 ; 0445          # 15.0 CYRILLIC 
SUBSCRIPT SMALL LETTER HA
+1E062         ; mapped                 ; 0446          # 15.0 CYRILLIC 
SUBSCRIPT SMALL LETTER TSE
+1E063         ; mapped                 ; 0447          # 15.0 CYRILLIC 
SUBSCRIPT SMALL LETTER CHE
+1E064         ; mapped                 ; 0448          # 15.0 CYRILLIC 
SUBSCRIPT SMALL LETTER SHA
+1E065         ; mapped                 ; 044A          # 15.0 CYRILLIC 
SUBSCRIPT SMALL LETTER HARD SIGN
+1E066         ; mapped                 ; 044B          # 15.0 CYRILLIC 
SUBSCRIPT SMALL LETTER YERU
+1E067         ; mapped                 ; 0491          # 15.0 CYRILLIC 
SUBSCRIPT SMALL LETTER GHE WITH UPTURN
+1E068         ; mapped                 ; 0456          # 15.0 CYRILLIC 
SUBSCRIPT SMALL LETTER BYELORUSSIAN-UKRAINIAN I
+1E069         ; mapped                 ; 0455          # 15.0 CYRILLIC 
SUBSCRIPT SMALL LETTER DZE
+1E06A         ; mapped                 ; 045F          # 15.0 CYRILLIC 
SUBSCRIPT SMALL LETTER DZHE
+1E06B         ; mapped                 ; 04AB          # 15.0 MODIFIER LETTER 
CYRILLIC SMALL ES WITH DESCENDER
+1E06C         ; mapped                 ; A651          # 15.0 MODIFIER LETTER 
CYRILLIC SMALL YERU WITH BACK YER
+1E06D         ; mapped                 ; 04B1          # 15.0 MODIFIER LETTER 
CYRILLIC SMALL STRAIGHT U WITH STROKE
+1E06E..1E08E  ; disallowed                             # NA   
<reserved-1E06E>..<reserved-1E08E>
+1E08F         ; valid                                  # 15.0 COMBINING 
CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I
+1E090..1E0FF  ; disallowed                             # NA   
<reserved-1E090>..<reserved-1E0FF>
 1E100..1E12C  ; valid                                  # 12.0 NYIAKENG PUACHUE 
HMONG LETTER MA..NYIAKENG PUACHUE HMONG LETTER W
 1E12D..1E12F  ; disallowed                             # NA   
<reserved-1E12D>..<reserved-1E12F>
 1E130..1E13D  ; valid                                  # 12.0 NYIAKENG PUACHUE 
HMONG TONE-B..NYIAKENG PUACHUE HMONG SYLLABLE LENGTHENER
@@ -7726,7 +7815,9 @@ FFFE..FFFF    ; disallowed                             # 
1.1  <noncharacter-FFFE
 1E2C0..1E2F9  ; valid                                  # 12.0 WANCHO LETTER 
AA..WANCHO DIGIT NINE
 1E2FA..1E2FE  ; disallowed                             # NA   
<reserved-1E2FA>..<reserved-1E2FE>
 1E2FF         ; valid                  ;      ; NV8    # 12.0 WANCHO NGUN SIGN
-1E300..1E7DF  ; disallowed                             # NA   
<reserved-1E300>..<reserved-1E7DF>
+1E300..1E4CF  ; disallowed                             # NA   
<reserved-1E300>..<reserved-1E4CF>
+1E4D0..1E4F9  ; valid                                  # 15.0 NAG MUNDARI 
LETTER O..NAG MUNDARI DIGIT NINE
+1E4FA..1E7DF  ; disallowed                             # NA   
<reserved-1E4FA>..<reserved-1E7DF>
 1E7E0..1E7E6  ; valid                                  # 14.0 ETHIOPIC 
SYLLABLE HHYA..ETHIOPIC SYLLABLE HHYO
 1E7E7         ; disallowed                             # NA   <reserved-1E7E7>
 1E7E8..1E7EB  ; valid                                  # 14.0 ETHIOPIC 
SYLLABLE GURAGE HHWA..ETHIOPIC SYLLABLE HHWE
@@ -8213,7 +8304,8 @@ FFFE..FFFF    ; disallowed                             # 
1.1  <noncharacter-FFFE
 1F6D3..1F6D4  ; valid                  ;      ; NV8    # 10.0 STUPA..PAGODA
 1F6D5         ; valid                  ;      ; NV8    # 12.0 HINDU TEMPLE
 1F6D6..1F6D7  ; valid                  ;      ; NV8    # 13.0 HUT..ELEVATOR
-1F6D8..1F6DC  ; disallowed                             # NA   
<reserved-1F6D8>..<reserved-1F6DC>
+1F6D8..1F6DB  ; disallowed                             # NA   
<reserved-1F6D8>..<reserved-1F6DB>
+1F6DC         ; valid                  ;      ; NV8    # 15.0 WIRELESS
 1F6DD..1F6DF  ; valid                  ;      ; NV8    # 14.0 PLAYGROUND 
SLIDE..RING BUOY
 1F6E0..1F6EC  ; valid                  ;      ; NV8    # 7.0  HAMMER AND 
WRENCH..AIRPLANE ARRIVING
 1F6ED..1F6EF  ; disallowed                             # NA   
<reserved-1F6ED>..<reserved-1F6EF>
@@ -8225,10 +8317,13 @@ FFFE..FFFF    ; disallowed                             
# 1.1  <noncharacter-FFFE
 1F6FB..1F6FC  ; valid                  ;      ; NV8    # 13.0 PICKUP 
TRUCK..ROLLER SKATE
 1F6FD..1F6FF  ; disallowed                             # NA   
<reserved-1F6FD>..<reserved-1F6FF>
 1F700..1F773  ; valid                  ;      ; NV8    # 6.0  ALCHEMICAL 
SYMBOL FOR QUINTESSENCE..ALCHEMICAL SYMBOL FOR HALF OUNCE
-1F774..1F77F  ; disallowed                             # NA   
<reserved-1F774>..<reserved-1F77F>
+1F774..1F776  ; valid                  ;      ; NV8    # 15.0 LOT OF 
FORTUNE..LUNAR ECLIPSE
+1F777..1F77A  ; disallowed                             # NA   
<reserved-1F777>..<reserved-1F77A>
+1F77B..1F77F  ; valid                  ;      ; NV8    # 15.0 HAUMEA..ORCUS
 1F780..1F7D4  ; valid                  ;      ; NV8    # 7.0  BLACK 
LEFT-POINTING ISOSCELES RIGHT TRIANGLE..HEAVY TWELVE POINTED PINWHEEL STAR
 1F7D5..1F7D8  ; valid                  ;      ; NV8    # 11.0 CIRCLED 
TRIANGLE..NEGATIVE CIRCLED SQUARE
-1F7D9..1F7DF  ; disallowed                             # NA   
<reserved-1F7D9>..<reserved-1F7DF>
+1F7D9         ; valid                  ;      ; NV8    # 15.0 NINE POINTED 
WHITE STAR
+1F7DA..1F7DF  ; disallowed                             # NA   
<reserved-1F7DA>..<reserved-1F7DF>
 1F7E0..1F7EB  ; valid                  ;      ; NV8    # 12.0 LARGE ORANGE 
CIRCLE..LARGE BROWN SQUARE
 1F7EC..1F7EF  ; disallowed                             # NA   
<reserved-1F7EC>..<reserved-1F7EF>
 1F7F0         ; valid                  ;      ; NV8    # 14.0 HEAVY EQUALS SIGN
@@ -8295,30 +8390,37 @@ FFFE..FFFF    ; disallowed                             
# 1.1  <noncharacter-FFFE
 1FA6E..1FA6F  ; disallowed                             # NA   
<reserved-1FA6E>..<reserved-1FA6F>
 1FA70..1FA73  ; valid                  ;      ; NV8    # 12.0 BALLET 
SHOES..SHORTS
 1FA74         ; valid                  ;      ; NV8    # 13.0 THONG SANDAL
-1FA75..1FA77  ; disallowed                             # NA   
<reserved-1FA75>..<reserved-1FA77>
+1FA75..1FA77  ; valid                  ;      ; NV8    # 15.0 LIGHT BLUE 
HEART..PINK HEART
 1FA78..1FA7A  ; valid                  ;      ; NV8    # 12.0 DROP OF 
BLOOD..STETHOSCOPE
 1FA7B..1FA7C  ; valid                  ;      ; NV8    # 14.0 X-RAY..CRUTCH
 1FA7D..1FA7F  ; disallowed                             # NA   
<reserved-1FA7D>..<reserved-1FA7F>
 1FA80..1FA82  ; valid                  ;      ; NV8    # 12.0 YO-YO..PARACHUTE
 1FA83..1FA86  ; valid                  ;      ; NV8    # 13.0 
BOOMERANG..NESTING DOLLS
-1FA87..1FA8F  ; disallowed                             # NA   
<reserved-1FA87>..<reserved-1FA8F>
+1FA87..1FA88  ; valid                  ;      ; NV8    # 15.0 MARACAS..FLUTE
+1FA89..1FA8F  ; disallowed                             # NA   
<reserved-1FA89>..<reserved-1FA8F>
 1FA90..1FA95  ; valid                  ;      ; NV8    # 12.0 RINGED 
PLANET..BANJO
 1FA96..1FAA8  ; valid                  ;      ; NV8    # 13.0 MILITARY 
HELMET..ROCK
 1FAA9..1FAAC  ; valid                  ;      ; NV8    # 14.0 MIRROR 
BALL..HAMSA
-1FAAD..1FAAF  ; disallowed                             # NA   
<reserved-1FAAD>..<reserved-1FAAF>
+1FAAD..1FAAF  ; valid                  ;      ; NV8    # 15.0 FOLDING HAND 
FAN..KHANDA
 1FAB0..1FAB6  ; valid                  ;      ; NV8    # 13.0 FLY..FEATHER
 1FAB7..1FABA  ; valid                  ;      ; NV8    # 14.0 LOTUS..NEST WITH 
EGGS
-1FABB..1FABF  ; disallowed                             # NA   
<reserved-1FABB>..<reserved-1FABF>
+1FABB..1FABD  ; valid                  ;      ; NV8    # 15.0 HYACINTH..WING
+1FABE         ; disallowed                             # NA   <reserved-1FABE>
+1FABF         ; valid                  ;      ; NV8    # 15.0 GOOSE
 1FAC0..1FAC2  ; valid                  ;      ; NV8    # 13.0 ANATOMICAL 
HEART..PEOPLE HUGGING
 1FAC3..1FAC5  ; valid                  ;      ; NV8    # 14.0 PREGNANT 
MAN..PERSON WITH CROWN
-1FAC6..1FACF  ; disallowed                             # NA   
<reserved-1FAC6>..<reserved-1FACF>
+1FAC6..1FACD  ; disallowed                             # NA   
<reserved-1FAC6>..<reserved-1FACD>
+1FACE..1FACF  ; valid                  ;      ; NV8    # 15.0 MOOSE..DONKEY
 1FAD0..1FAD6  ; valid                  ;      ; NV8    # 13.0 
BLUEBERRIES..TEAPOT
 1FAD7..1FAD9  ; valid                  ;      ; NV8    # 14.0 POURING 
LIQUID..JAR
-1FADA..1FADF  ; disallowed                             # NA   
<reserved-1FADA>..<reserved-1FADF>
+1FADA..1FADB  ; valid                  ;      ; NV8    # 15.0 GINGER ROOT..PEA 
POD
+1FADC..1FADF  ; disallowed                             # NA   
<reserved-1FADC>..<reserved-1FADF>
 1FAE0..1FAE7  ; valid                  ;      ; NV8    # 14.0 MELTING 
FACE..BUBBLES
-1FAE8..1FAEF  ; disallowed                             # NA   
<reserved-1FAE8>..<reserved-1FAEF>
+1FAE8         ; valid                  ;      ; NV8    # 15.0 SHAKING FACE
+1FAE9..1FAEF  ; disallowed                             # NA   
<reserved-1FAE9>..<reserved-1FAEF>
 1FAF0..1FAF6  ; valid                  ;      ; NV8    # 14.0 HAND WITH INDEX 
FINGER AND THUMB CROSSED..HEART HANDS
-1FAF7..1FAFF  ; disallowed                             # NA   
<reserved-1FAF7>..<reserved-1FAFF>
+1FAF7..1FAF8  ; valid                  ;      ; NV8    # 15.0 LEFTWARDS 
PUSHING HAND..RIGHTWARDS PUSHING HAND
+1FAF9..1FAFF  ; disallowed                             # NA   
<reserved-1FAF9>..<reserved-1FAFF>
 1FB00..1FB92  ; valid                  ;      ; NV8    # 13.0 BLOCK 
SEXTANT-1..UPPER HALF INVERSE MEDIUM SHADE AND LOWER HALF BLOCK
 1FB93         ; disallowed                             # NA   <reserved-1FB93>
 1FB94..1FBCA  ; valid                  ;      ; NV8    # 13.0 LEFT HALF 
INVERSE MEDIUM SHADE AND RIGHT HALF BLOCK..WHITE UP-POINTING CHEVRON
@@ -8341,7 +8443,8 @@ FFFE..FFFF    ; disallowed                             # 
1.1  <noncharacter-FFFE
 2A6E0..2A6FF  ; disallowed                             # NA   
<reserved-2A6E0>..<reserved-2A6FF>
 2A700..2B734  ; valid                                  # 5.2  CJK UNIFIED 
IDEOGRAPH-2A700..CJK UNIFIED IDEOGRAPH-2B734
 2B735..2B738  ; valid                                  # 14.0 CJK UNIFIED 
IDEOGRAPH-2B735..CJK UNIFIED IDEOGRAPH-2B738
-2B739..2B73F  ; disallowed                             # NA   
<reserved-2B739>..<reserved-2B73F>
+2B739         ; valid                                  # 15.0 CJK UNIFIED 
IDEOGRAPH-2B739
+2B73A..2B73F  ; disallowed                             # NA   
<reserved-2B73A>..<reserved-2B73F>
 2B740..2B81D  ; valid                                  # 6.0  CJK UNIFIED 
IDEOGRAPH-2B740..CJK UNIFIED IDEOGRAPH-2B81D
 2B81E..2B81F  ; disallowed                             # NA   
<reserved-2B81E>..<reserved-2B81F>
 2B820..2CEA1  ; valid                                  # 8.0  CJK UNIFIED 
IDEOGRAPH-2B820..CJK UNIFIED IDEOGRAPH-2CEA1
@@ -8883,7 +8986,9 @@ FFFE..FFFF    ; disallowed                             # 
1.1  <noncharacter-FFFE
 2FA1E..2FFFD  ; disallowed                             # NA   
<reserved-2FA1E>..<reserved-2FFFD>
 2FFFE..2FFFF  ; disallowed                             # 2.0  
<noncharacter-2FFFE>..<noncharacter-2FFFF>
 30000..3134A  ; valid                                  # 13.0 CJK UNIFIED 
IDEOGRAPH-30000..CJK UNIFIED IDEOGRAPH-3134A
-3134B..3FFFD  ; disallowed                             # NA   
<reserved-3134B>..<reserved-3FFFD>
+3134B..3134F  ; disallowed                             # NA   
<reserved-3134B>..<reserved-3134F>
+31350..323AF  ; valid                                  # 15.0 CJK UNIFIED 
IDEOGRAPH-31350..CJK UNIFIED IDEOGRAPH-323AF
+323B0..3FFFD  ; disallowed                             # NA   
<reserved-323B0>..<reserved-3FFFD>
 3FFFE..3FFFF  ; disallowed                             # 2.0  
<noncharacter-3FFFE>..<noncharacter-3FFFF>
 40000..4FFFD  ; disallowed                             # NA   
<reserved-40000>..<reserved-4FFFD>
 4FFFE..4FFFF  ; disallowed                             # 2.0  
<noncharacter-4FFFE>..<noncharacter-4FFFF>
diff --git a/admin/unidata/Makefile.in b/admin/unidata/Makefile.in
index 4b3e72f013..f3e653879c 100644
--- a/admin/unidata/Makefile.in
+++ b/admin/unidata/Makefile.in
@@ -138,7 +138,8 @@ gen-clean:
        rm -f ${unidir}/charscript.el*
        rm -f ${unidir}/emoji-zwj.el*
        rm -f ${unifiles} ${unidir}/charprop.el
-       rm -f ${unidir}/emoji-labels.el*
+       rm -f ${unidir}/emoji-labels.el ${unidir}/idna-mapping.el \
+               ${unidir}/uni-confusable.el ${unidir}/uni-scripts.el
 ## ref: https://lists.gnu.org/r/emacs-devel/2013-11/msg01029.html
 
 maintainer-clean: gen-clean distclean
diff --git a/admin/unidata/NormalizationTest.txt 
b/admin/unidata/NormalizationTest.txt
index 302c35f37c..e75b4801c9 100644
--- a/admin/unidata/NormalizationTest.txt
+++ b/admin/unidata/NormalizationTest.txt
@@ -1,11 +1,11 @@
-# NormalizationTest-14.0.0.txt
-# Date: 2021-05-28, 21:49:12 GMT
-# © 2021 Unicode®, Inc.
+# NormalizationTest-15.0.0.txt
+# Date: 2022-04-02, 01:29:09 GMT
+# © 2022 Unicode®, Inc.
 # Unicode and the Unicode Logo are registered trademarks of Unicode, Inc. in 
the U.S. and other countries.
-# For terms of use, see http://www.unicode.org/terms_of_use.html
+# For terms of use, see https://www.unicode.org/terms_of_use.html
 #
 # Unicode Character Database
-#   For documentation, see http://www.unicode.org/reports/tr44/
+#   For documentation, see https://www.unicode.org/reports/tr44/
 #
 # Normalization Test Suite
 # Format:
@@ -16208,6 +16208,68 @@ FFEE;FFEE;FFEE;25CB;25CB; # (○; ○; ○; ○; ○; ) 
HALFWIDTH WHITE CIRCLE
 1D7FD;1D7FD;1D7FD;0037;0037; # (𝟽; 𝟽; 𝟽; 7; 7; ) MATHEMATICAL MONOSPACE DIGIT 
SEVEN
 1D7FE;1D7FE;1D7FE;0038;0038; # (𝟾; 𝟾; 𝟾; 8; 8; ) MATHEMATICAL MONOSPACE DIGIT 
EIGHT
 1D7FF;1D7FF;1D7FF;0039;0039; # (𝟿; 𝟿; 𝟿; 9; 9; ) MATHEMATICAL MONOSPACE DIGIT 
NINE
+1E030;1E030;1E030;0430;0430; # (𞀰; 𞀰; 𞀰; а; а; ) MODIFIER LETTER CYRILLIC 
SMALL A
+1E031;1E031;1E031;0431;0431; # (𞀱; 𞀱; 𞀱; б; б; ) MODIFIER LETTER CYRILLIC 
SMALL BE
+1E032;1E032;1E032;0432;0432; # (𞀲; 𞀲; 𞀲; в; в; ) MODIFIER LETTER CYRILLIC 
SMALL VE
+1E033;1E033;1E033;0433;0433; # (𞀳; 𞀳; 𞀳; г; г; ) MODIFIER LETTER CYRILLIC 
SMALL GHE
+1E034;1E034;1E034;0434;0434; # (𞀴; 𞀴; 𞀴; д; д; ) MODIFIER LETTER CYRILLIC 
SMALL DE
+1E035;1E035;1E035;0435;0435; # (𞀵; 𞀵; 𞀵; е; е; ) MODIFIER LETTER CYRILLIC 
SMALL IE
+1E036;1E036;1E036;0436;0436; # (𞀶; 𞀶; 𞀶; ж; ж; ) MODIFIER LETTER CYRILLIC 
SMALL ZHE
+1E037;1E037;1E037;0437;0437; # (𞀷; 𞀷; 𞀷; з; з; ) MODIFIER LETTER CYRILLIC 
SMALL ZE
+1E038;1E038;1E038;0438;0438; # (𞀸; 𞀸; 𞀸; и; и; ) MODIFIER LETTER CYRILLIC 
SMALL I
+1E039;1E039;1E039;043A;043A; # (𞀹; 𞀹; 𞀹; к; к; ) MODIFIER LETTER CYRILLIC 
SMALL KA
+1E03A;1E03A;1E03A;043B;043B; # (𞀺; 𞀺; 𞀺; л; л; ) MODIFIER LETTER CYRILLIC 
SMALL EL
+1E03B;1E03B;1E03B;043C;043C; # (𞀻; 𞀻; 𞀻; м; м; ) MODIFIER LETTER CYRILLIC 
SMALL EM
+1E03C;1E03C;1E03C;043E;043E; # (𞀼; 𞀼; 𞀼; о; о; ) MODIFIER LETTER CYRILLIC 
SMALL O
+1E03D;1E03D;1E03D;043F;043F; # (𞀽; 𞀽; 𞀽; п; п; ) MODIFIER LETTER CYRILLIC 
SMALL PE
+1E03E;1E03E;1E03E;0440;0440; # (𞀾; 𞀾; 𞀾; р; р; ) MODIFIER LETTER CYRILLIC 
SMALL ER
+1E03F;1E03F;1E03F;0441;0441; # (𞀿; 𞀿; 𞀿; с; с; ) MODIFIER LETTER CYRILLIC 
SMALL ES
+1E040;1E040;1E040;0442;0442; # (𞁀; 𞁀; 𞁀; т; т; ) MODIFIER LETTER CYRILLIC 
SMALL TE
+1E041;1E041;1E041;0443;0443; # (𞁁; 𞁁; 𞁁; у; у; ) MODIFIER LETTER CYRILLIC 
SMALL U
+1E042;1E042;1E042;0444;0444; # (𞁂; 𞁂; 𞁂; ф; ф; ) MODIFIER LETTER CYRILLIC 
SMALL EF
+1E043;1E043;1E043;0445;0445; # (𞁃; 𞁃; 𞁃; х; х; ) MODIFIER LETTER CYRILLIC 
SMALL HA
+1E044;1E044;1E044;0446;0446; # (𞁄; 𞁄; 𞁄; ц; ц; ) MODIFIER LETTER CYRILLIC 
SMALL TSE
+1E045;1E045;1E045;0447;0447; # (𞁅; 𞁅; 𞁅; ч; ч; ) MODIFIER LETTER CYRILLIC 
SMALL CHE
+1E046;1E046;1E046;0448;0448; # (𞁆; 𞁆; 𞁆; ш; ш; ) MODIFIER LETTER CYRILLIC 
SMALL SHA
+1E047;1E047;1E047;044B;044B; # (𞁇; 𞁇; 𞁇; ы; ы; ) MODIFIER LETTER CYRILLIC 
SMALL YERU
+1E048;1E048;1E048;044D;044D; # (𞁈; 𞁈; 𞁈; э; э; ) MODIFIER LETTER CYRILLIC 
SMALL E
+1E049;1E049;1E049;044E;044E; # (𞁉; 𞁉; 𞁉; ю; ю; ) MODIFIER LETTER CYRILLIC 
SMALL YU
+1E04A;1E04A;1E04A;A689;A689; # (𞁊; 𞁊; 𞁊; ꚉ; ꚉ; ) MODIFIER LETTER CYRILLIC 
SMALL DZZE
+1E04B;1E04B;1E04B;04D9;04D9; # (𞁋; 𞁋; 𞁋; ә; ә; ) MODIFIER LETTER CYRILLIC 
SMALL SCHWA
+1E04C;1E04C;1E04C;0456;0456; # (𞁌; 𞁌; 𞁌; і; і; ) MODIFIER LETTER CYRILLIC 
SMALL BYELORUSSIAN-UKRAINIAN I
+1E04D;1E04D;1E04D;0458;0458; # (𞁍; 𞁍; 𞁍; ј; ј; ) MODIFIER LETTER CYRILLIC 
SMALL JE
+1E04E;1E04E;1E04E;04E9;04E9; # (𞁎; 𞁎; 𞁎; ө; ө; ) MODIFIER LETTER CYRILLIC 
SMALL BARRED O
+1E04F;1E04F;1E04F;04AF;04AF; # (𞁏; 𞁏; 𞁏; ү; ү; ) MODIFIER LETTER CYRILLIC 
SMALL STRAIGHT U
+1E050;1E050;1E050;04CF;04CF; # (𞁐; 𞁐; 𞁐; ӏ; ӏ; ) MODIFIER LETTER CYRILLIC 
SMALL PALOCHKA
+1E051;1E051;1E051;0430;0430; # (𞁑; 𞁑; 𞁑; а; а; ) CYRILLIC SUBSCRIPT SMALL 
LETTER A
+1E052;1E052;1E052;0431;0431; # (𞁒; 𞁒; 𞁒; б; б; ) CYRILLIC SUBSCRIPT SMALL 
LETTER BE
+1E053;1E053;1E053;0432;0432; # (𞁓; 𞁓; 𞁓; в; в; ) CYRILLIC SUBSCRIPT SMALL 
LETTER VE
+1E054;1E054;1E054;0433;0433; # (𞁔; 𞁔; 𞁔; г; г; ) CYRILLIC SUBSCRIPT SMALL 
LETTER GHE
+1E055;1E055;1E055;0434;0434; # (𞁕; 𞁕; 𞁕; д; д; ) CYRILLIC SUBSCRIPT SMALL 
LETTER DE
+1E056;1E056;1E056;0435;0435; # (𞁖; 𞁖; 𞁖; е; е; ) CYRILLIC SUBSCRIPT SMALL 
LETTER IE
+1E057;1E057;1E057;0436;0436; # (𞁗; 𞁗; 𞁗; ж; ж; ) CYRILLIC SUBSCRIPT SMALL 
LETTER ZHE
+1E058;1E058;1E058;0437;0437; # (𞁘; 𞁘; 𞁘; з; з; ) CYRILLIC SUBSCRIPT SMALL 
LETTER ZE
+1E059;1E059;1E059;0438;0438; # (𞁙; 𞁙; 𞁙; и; и; ) CYRILLIC SUBSCRIPT SMALL 
LETTER I
+1E05A;1E05A;1E05A;043A;043A; # (𞁚; 𞁚; 𞁚; к; к; ) CYRILLIC SUBSCRIPT SMALL 
LETTER KA
+1E05B;1E05B;1E05B;043B;043B; # (𞁛; 𞁛; 𞁛; л; л; ) CYRILLIC SUBSCRIPT SMALL 
LETTER EL
+1E05C;1E05C;1E05C;043E;043E; # (𞁜; 𞁜; 𞁜; о; о; ) CYRILLIC SUBSCRIPT SMALL 
LETTER O
+1E05D;1E05D;1E05D;043F;043F; # (𞁝; 𞁝; 𞁝; п; п; ) CYRILLIC SUBSCRIPT SMALL 
LETTER PE
+1E05E;1E05E;1E05E;0441;0441; # (𞁞; 𞁞; 𞁞; с; с; ) CYRILLIC SUBSCRIPT SMALL 
LETTER ES
+1E05F;1E05F;1E05F;0443;0443; # (𞁟; 𞁟; 𞁟; у; у; ) CYRILLIC SUBSCRIPT SMALL 
LETTER U
+1E060;1E060;1E060;0444;0444; # (𞁠; 𞁠; 𞁠; ф; ф; ) CYRILLIC SUBSCRIPT SMALL 
LETTER EF
+1E061;1E061;1E061;0445;0445; # (𞁡; 𞁡; 𞁡; х; х; ) CYRILLIC SUBSCRIPT SMALL 
LETTER HA
+1E062;1E062;1E062;0446;0446; # (𞁢; 𞁢; 𞁢; ц; ц; ) CYRILLIC SUBSCRIPT SMALL 
LETTER TSE
+1E063;1E063;1E063;0447;0447; # (𞁣; 𞁣; 𞁣; ч; ч; ) CYRILLIC SUBSCRIPT SMALL 
LETTER CHE
+1E064;1E064;1E064;0448;0448; # (𞁤; 𞁤; 𞁤; ш; ш; ) CYRILLIC SUBSCRIPT SMALL 
LETTER SHA
+1E065;1E065;1E065;044A;044A; # (𞁥; 𞁥; 𞁥; ъ; ъ; ) CYRILLIC SUBSCRIPT SMALL 
LETTER HARD SIGN
+1E066;1E066;1E066;044B;044B; # (𞁦; 𞁦; 𞁦; ы; ы; ) CYRILLIC SUBSCRIPT SMALL 
LETTER YERU
+1E067;1E067;1E067;0491;0491; # (𞁧; 𞁧; 𞁧; ґ; ґ; ) CYRILLIC SUBSCRIPT SMALL 
LETTER GHE WITH UPTURN
+1E068;1E068;1E068;0456;0456; # (𞁨; 𞁨; 𞁨; і; і; ) CYRILLIC SUBSCRIPT SMALL 
LETTER BYELORUSSIAN-UKRAINIAN I
+1E069;1E069;1E069;0455;0455; # (𞁩; 𞁩; 𞁩; ѕ; ѕ; ) CYRILLIC SUBSCRIPT SMALL 
LETTER DZE
+1E06A;1E06A;1E06A;045F;045F; # (𞁪; 𞁪; 𞁪; џ; џ; ) CYRILLIC SUBSCRIPT SMALL 
LETTER DZHE
+1E06B;1E06B;1E06B;04AB;04AB; # (𞁫; 𞁫; 𞁫; ҫ; ҫ; ) MODIFIER LETTER CYRILLIC 
SMALL ES WITH DESCENDER
+1E06C;1E06C;1E06C;A651;A651; # (𞁬; 𞁬; 𞁬; ꙑ; ꙑ; ) MODIFIER LETTER CYRILLIC 
SMALL YERU WITH BACK YER
+1E06D;1E06D;1E06D;04B1;04B1; # (𞁭; 𞁭; 𞁭; ұ; ұ; ) MODIFIER LETTER CYRILLIC 
SMALL STRAIGHT U WITH STROKE
 1EE00;1EE00;1EE00;0627;0627; # (𞸀; 𞸀; 𞸀; ا; ا; ) ARABIC MATHEMATICAL ALEF
 1EE01;1EE01;1EE01;0628;0628; # (𞸁; 𞸁; 𞸁; ب; ب; ) ARABIC MATHEMATICAL BEH
 1EE02;1EE02;1EE02;062C;062C; # (𞸂; 𞸂; 𞸂; ج; ج; ) ARABIC MATHEMATICAL JEEM
@@ -18496,6 +18558,12 @@ FFEE;FFEE;FFEE;25CB;25CB; # (○; ○; ○; ○; ○; ) 
HALFWIDTH WHITE CIRCLE
 0061 10EAB 0315 0300 05AE 0062;0061 05AE 10EAB 0300 0315 0062;0061 05AE 10EAB 
0300 0315 0062;0061 05AE 10EAB 0300 0315 0062;0061 05AE 10EAB 0300 0315 0062; # 
(a◌𐺫◌̕◌̀◌֮b; a◌֮◌𐺫◌̀◌̕b; a◌֮◌𐺫◌̀◌̕b; a◌֮◌𐺫◌̀◌̕b; a◌֮◌𐺫◌̀◌̕b; ) LATIN SMALL 
LETTER A, YEZIDI COMBINING HAMZA MARK, COMBINING COMMA ABOVE RIGHT, COMBINING 
GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
 0061 0315 0300 05AE 10EAC 0062;00E0 05AE 10EAC 0315 0062;0061 05AE 0300 10EAC 
0315 0062;00E0 05AE 10EAC 0315 0062;0061 05AE 0300 10EAC 0315 0062; # 
(a◌̕◌̀◌֮◌𐺬b; à◌֮◌𐺬◌̕b; a◌֮◌̀◌𐺬◌̕b; à◌֮◌𐺬◌̕b; a◌֮◌̀◌𐺬◌̕b; ) LATIN SMALL LETTER 
A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, 
YEZIDI COMBINING MADDA MARK, LATIN SMALL LETTER B
 0061 10EAC 0315 0300 05AE 0062;0061 05AE 10EAC 0300 0315 0062;0061 05AE 10EAC 
0300 0315 0062;0061 05AE 10EAC 0300 0315 0062;0061 05AE 10EAC 0300 0315 0062; # 
(a◌𐺬◌̕◌̀◌֮b; a◌֮◌𐺬◌̀◌̕b; a◌֮◌𐺬◌̀◌̕b; a◌֮◌𐺬◌̀◌̕b; a◌֮◌𐺬◌̀◌̕b; ) LATIN SMALL 
LETTER A, YEZIDI COMBINING MADDA MARK, COMBINING COMMA ABOVE RIGHT, COMBINING 
GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 059A 0316 1DFA 10EFD 0062;0061 1DFA 0316 10EFD 059A 0062;0061 1DFA 0316 
10EFD 059A 0062;0061 1DFA 0316 10EFD 059A 0062;0061 1DFA 0316 10EFD 059A 0062; 
# (a◌֚◌̖◌᷺◌𐻽b; a◌᷺◌̖◌𐻽◌֚b; a◌᷺◌̖◌𐻽◌֚b; a◌᷺◌̖◌𐻽◌֚b; a◌᷺◌̖◌𐻽◌֚b; ) LATIN SMALL 
LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, COMBINING DOT 
BELOW LEFT, ARABIC SMALL LOW WORD SAKTA, LATIN SMALL LETTER B
+0061 10EFD 059A 0316 1DFA 0062;0061 1DFA 10EFD 0316 059A 0062;0061 1DFA 10EFD 
0316 059A 0062;0061 1DFA 10EFD 0316 059A 0062;0061 1DFA 10EFD 0316 059A 0062; # 
(a◌𐻽◌֚◌̖◌᷺b; a◌᷺◌𐻽◌̖◌֚b; a◌᷺◌𐻽◌̖◌֚b; a◌᷺◌𐻽◌̖◌֚b; a◌᷺◌𐻽◌̖◌֚b; ) LATIN SMALL 
LETTER A, ARABIC SMALL LOW WORD SAKTA, HEBREW ACCENT YETIV, COMBINING GRAVE 
ACCENT BELOW, COMBINING DOT BELOW LEFT, LATIN SMALL LETTER B
+0061 059A 0316 1DFA 10EFE 0062;0061 1DFA 0316 10EFE 059A 0062;0061 1DFA 0316 
10EFE 059A 0062;0061 1DFA 0316 10EFE 059A 0062;0061 1DFA 0316 10EFE 059A 0062; 
# (a◌֚◌̖◌᷺◌𐻾b; a◌᷺◌̖◌𐻾◌֚b; a◌᷺◌̖◌𐻾◌֚b; a◌᷺◌̖◌𐻾◌֚b; a◌᷺◌̖◌𐻾◌֚b; ) LATIN SMALL 
LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, COMBINING DOT 
BELOW LEFT, ARABIC SMALL LOW WORD QASR, LATIN SMALL LETTER B
+0061 10EFE 059A 0316 1DFA 0062;0061 1DFA 10EFE 0316 059A 0062;0061 1DFA 10EFE 
0316 059A 0062;0061 1DFA 10EFE 0316 059A 0062;0061 1DFA 10EFE 0316 059A 0062; # 
(a◌𐻾◌֚◌̖◌᷺b; a◌᷺◌𐻾◌̖◌֚b; a◌᷺◌𐻾◌̖◌֚b; a◌᷺◌𐻾◌̖◌֚b; a◌᷺◌𐻾◌̖◌֚b; ) LATIN SMALL 
LETTER A, ARABIC SMALL LOW WORD QASR, HEBREW ACCENT YETIV, COMBINING GRAVE 
ACCENT BELOW, COMBINING DOT BELOW LEFT, LATIN SMALL LETTER B
+0061 059A 0316 1DFA 10EFF 0062;0061 1DFA 0316 10EFF 059A 0062;0061 1DFA 0316 
10EFF 059A 0062;0061 1DFA 0316 10EFF 059A 0062;0061 1DFA 0316 10EFF 059A 0062; 
# (a◌֚◌̖◌᷺◌𐻿b; a◌᷺◌̖◌𐻿◌֚b; a◌᷺◌̖◌𐻿◌֚b; a◌᷺◌̖◌𐻿◌֚b; a◌᷺◌̖◌𐻿◌֚b; ) LATIN SMALL 
LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, COMBINING DOT 
BELOW LEFT, ARABIC SMALL LOW WORD MADDA, LATIN SMALL LETTER B
+0061 10EFF 059A 0316 1DFA 0062;0061 1DFA 10EFF 0316 059A 0062;0061 1DFA 10EFF 
0316 059A 0062;0061 1DFA 10EFF 0316 059A 0062;0061 1DFA 10EFF 0316 059A 0062; # 
(a◌𐻿◌֚◌̖◌᷺b; a◌᷺◌𐻿◌̖◌֚b; a◌᷺◌𐻿◌̖◌֚b; a◌᷺◌𐻿◌̖◌֚b; a◌᷺◌𐻿◌̖◌֚b; ) LATIN SMALL 
LETTER A, ARABIC SMALL LOW WORD MADDA, HEBREW ACCENT YETIV, COMBINING GRAVE 
ACCENT BELOW, COMBINING DOT BELOW LEFT, LATIN SMALL LETTER B
 0061 059A 0316 1DFA 10F46 0062;0061 1DFA 0316 10F46 059A 0062;0061 1DFA 0316 
10F46 059A 0062;0061 1DFA 0316 10F46 059A 0062;0061 1DFA 0316 10F46 059A 0062; 
# (a◌֚◌̖◌᷺◌𐽆b; a◌᷺◌̖◌𐽆◌֚b; a◌᷺◌̖◌𐽆◌֚b; a◌᷺◌̖◌𐽆◌֚b; a◌᷺◌̖◌𐽆◌֚b; ) LATIN SMALL 
LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, COMBINING DOT 
BELOW LEFT, SOGDIAN COMBINING DOT BELOW, LATIN SMALL LETTER B
 0061 10F46 059A 0316 1DFA 0062;0061 1DFA 10F46 0316 059A 0062;0061 1DFA 10F46 
0316 059A 0062;0061 1DFA 10F46 0316 059A 0062;0061 1DFA 10F46 0316 059A 0062; # 
(a◌𐽆◌֚◌̖◌᷺b; a◌᷺◌𐽆◌̖◌֚b; a◌᷺◌𐽆◌̖◌֚b; a◌᷺◌𐽆◌̖◌֚b; a◌᷺◌𐽆◌̖◌֚b; ) LATIN SMALL 
LETTER A, SOGDIAN COMBINING DOT BELOW, HEBREW ACCENT YETIV, COMBINING GRAVE 
ACCENT BELOW, COMBINING DOT BELOW LEFT, LATIN SMALL LETTER B
 0061 059A 0316 1DFA 10F47 0062;0061 1DFA 0316 10F47 059A 0062;0061 1DFA 0316 
10F47 059A 0062;0061 1DFA 0316 10F47 059A 0062;0061 1DFA 0316 10F47 059A 0062; 
# (a◌֚◌̖◌᷺◌𐽇b; a◌᷺◌̖◌𐽇◌֚b; a◌᷺◌̖◌𐽇◌֚b; a◌᷺◌̖◌𐽇◌֚b; a◌᷺◌̖◌𐽇◌֚b; ) LATIN SMALL 
LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, COMBINING DOT 
BELOW LEFT, SOGDIAN COMBINING TWO DOTS BELOW, LATIN SMALL LETTER B
@@ -18640,6 +18708,10 @@ FFEE;FFEE;FFEE;25CB;25CB; # (○; ○; ○; ○; ○; ) 
HALFWIDTH WHITE CIRCLE
 0061 11D45 05B0 094D 3099 0062;0061 3099 11D45 094D 05B0 0062;0061 3099 11D45 
094D 05B0 0062;0061 3099 11D45 094D 05B0 0062;0061 3099 11D45 094D 05B0 0062; # 
(a◌𑵅◌ְ◌्◌゙b; a◌゙◌𑵅◌्◌ְb; a◌゙◌𑵅◌्◌ְb; a◌゙◌𑵅◌्◌ְb; a◌゙◌𑵅◌्◌ְb; ) LATIN SMALL 
LETTER A, MASARAM GONDI VIRAMA, HEBREW POINT SHEVA, DEVANAGARI SIGN VIRAMA, 
COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, LATIN SMALL LETTER B
 0061 05B0 094D 3099 11D97 0062;0061 3099 094D 11D97 05B0 0062;0061 3099 094D 
11D97 05B0 0062;0061 3099 094D 11D97 05B0 0062;0061 3099 094D 11D97 05B0 0062; 
# (a◌ְ◌्◌゙◌𑶗b; a◌゙◌्◌𑶗◌ְb; a◌゙◌्◌𑶗◌ְb; a◌゙◌्◌𑶗◌ְb; a◌゙◌्◌𑶗◌ְb; ) LATIN SMALL 
LETTER A, HEBREW POINT SHEVA, DEVANAGARI SIGN VIRAMA, COMBINING 
KATAKANA-HIRAGANA VOICED SOUND MARK, GUNJALA GONDI VIRAMA, LATIN SMALL LETTER B
 0061 11D97 05B0 094D 3099 0062;0061 3099 11D97 094D 05B0 0062;0061 3099 11D97 
094D 05B0 0062;0061 3099 11D97 094D 05B0 0062;0061 3099 11D97 094D 05B0 0062; # 
(a◌𑶗◌ְ◌्◌゙b; a◌゙◌𑶗◌्◌ְb; a◌゙◌𑶗◌्◌ְb; a◌゙◌𑶗◌्◌ְb; a◌゙◌𑶗◌्◌ְb; ) LATIN SMALL 
LETTER A, GUNJALA GONDI VIRAMA, HEBREW POINT SHEVA, DEVANAGARI SIGN VIRAMA, 
COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, LATIN SMALL LETTER B
+0061 05B0 094D 3099 11F41 0062;0061 3099 094D 11F41 05B0 0062;0061 3099 094D 
11F41 05B0 0062;0061 3099 094D 11F41 05B0 0062;0061 3099 094D 11F41 05B0 0062; 
# (a◌ְ◌्◌゙𑽁b; a◌゙◌्𑽁◌ְb; a◌゙◌्𑽁◌ְb; a◌゙◌्𑽁◌ְb; a◌゙◌्𑽁◌ְb; ) LATIN SMALL LETTER 
A, HEBREW POINT SHEVA, DEVANAGARI SIGN VIRAMA, COMBINING KATAKANA-HIRAGANA 
VOICED SOUND MARK, KAWI SIGN KILLER, LATIN SMALL LETTER B
+0061 11F41 05B0 094D 3099 0062;0061 3099 11F41 094D 05B0 0062;0061 3099 11F41 
094D 05B0 0062;0061 3099 11F41 094D 05B0 0062;0061 3099 11F41 094D 05B0 0062; # 
(a𑽁◌ְ◌्◌゙b; a◌゙𑽁◌्◌ְb; a◌゙𑽁◌्◌ְb; a◌゙𑽁◌्◌ְb; a◌゙𑽁◌्◌ְb; ) LATIN SMALL LETTER A, 
KAWI SIGN KILLER, HEBREW POINT SHEVA, DEVANAGARI SIGN VIRAMA, COMBINING 
KATAKANA-HIRAGANA VOICED SOUND MARK, LATIN SMALL LETTER B
+0061 05B0 094D 3099 11F42 0062;0061 3099 094D 11F42 05B0 0062;0061 3099 094D 
11F42 05B0 0062;0061 3099 094D 11F42 05B0 0062;0061 3099 094D 11F42 05B0 0062; 
# (a◌ְ◌्◌゙◌𑽂b; a◌゙◌्◌𑽂◌ְb; a◌゙◌्◌𑽂◌ְb; a◌゙◌्◌𑽂◌ְb; a◌゙◌्◌𑽂◌ְb; ) LATIN SMALL 
LETTER A, HEBREW POINT SHEVA, DEVANAGARI SIGN VIRAMA, COMBINING 
KATAKANA-HIRAGANA VOICED SOUND MARK, KAWI CONJOINER, LATIN SMALL LETTER B
+0061 11F42 05B0 094D 3099 0062;0061 3099 11F42 094D 05B0 0062;0061 3099 11F42 
094D 05B0 0062;0061 3099 11F42 094D 05B0 0062;0061 3099 11F42 094D 05B0 0062; # 
(a◌𑽂◌ְ◌्◌゙b; a◌゙◌𑽂◌्◌ְb; a◌゙◌𑽂◌्◌ְb; a◌゙◌𑽂◌्◌ְb; a◌゙◌𑽂◌्◌ְb; ) LATIN SMALL 
LETTER A, KAWI CONJOINER, HEBREW POINT SHEVA, DEVANAGARI SIGN VIRAMA, COMBINING 
KATAKANA-HIRAGANA VOICED SOUND MARK, LATIN SMALL LETTER B
 0061 16FF0 0334 16AF0 0062;0061 0334 16AF0 16FF0 0062;0061 0334 16AF0 16FF0 
0062;0061 0334 16AF0 16FF0 0062;0061 0334 16AF0 16FF0 0062; # (a𖿰◌̴◌𖫰b; 
a◌̴◌𖫰𖿰b; a◌̴◌𖫰𖿰b; a◌̴◌𖫰𖿰b; a◌̴◌𖫰𖿰b; ) LATIN SMALL LETTER A, VIETNAMESE 
ALTERNATE READING MARK CA, COMBINING TILDE OVERLAY, BASSA VAH COMBINING HIGH 
TONE, LATIN SMALL LETTER B
 0061 16AF0 16FF0 0334 0062;0061 16AF0 0334 16FF0 0062;0061 16AF0 0334 16FF0 
0062;0061 16AF0 0334 16FF0 0062;0061 16AF0 0334 16FF0 0062; # (a◌𖫰𖿰◌̴b; 
a◌𖫰◌̴𖿰b; a◌𖫰◌̴𖿰b; a◌𖫰◌̴𖿰b; a◌𖫰◌̴𖿰b; ) LATIN SMALL LETTER A, BASSA VAH COMBINING 
HIGH TONE, VIETNAMESE ALTERNATE READING MARK CA, COMBINING TILDE OVERLAY, LATIN 
SMALL LETTER B
 0061 16FF0 0334 16AF1 0062;0061 0334 16AF1 16FF0 0062;0061 0334 16AF1 16FF0 
0062;0061 0334 16AF1 16FF0 0062;0061 0334 16AF1 16FF0 0062; # (a𖿰◌̴◌𖫱b; 
a◌̴◌𖫱𖿰b; a◌̴◌𖫱𖿰b; a◌̴◌𖫱𖿰b; a◌̴◌𖫱𖿰b; ) LATIN SMALL LETTER A, VIETNAMESE 
ALTERNATE READING MARK CA, COMBINING TILDE OVERLAY, BASSA VAH COMBINING LOW 
TONE, LATIN SMALL LETTER B
@@ -18812,6 +18884,8 @@ FFEE;FFEE;FFEE;25CB;25CB; # (○; ○; ○; ○; ○; ) HALFWIDTH 
WHITE CIRCLE
 0061 1E029 0315 0300 05AE 0062;0061 05AE 1E029 0300 0315 0062;0061 05AE 1E029 
0300 0315 0062;0061 05AE 1E029 0300 0315 0062;0061 05AE 1E029 0300 0315 0062; # 
(a◌𞀩◌̕◌̀◌֮b; a◌֮◌𞀩◌̀◌̕b; a◌֮◌𞀩◌̀◌̕b; a◌֮◌𞀩◌̀◌̕b; a◌֮◌𞀩◌̀◌̕b; ) LATIN SMALL 
LETTER A, COMBINING GLAGOLITIC LETTER IOTATED BIG YUS, COMBINING COMMA ABOVE 
RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
 0061 0315 0300 05AE 1E02A 0062;00E0 05AE 1E02A 0315 0062;0061 05AE 0300 1E02A 
0315 0062;00E0 05AE 1E02A 0315 0062;0061 05AE 0300 1E02A 0315 0062; # 
(a◌̕◌̀◌֮◌𞀪b; à◌֮◌𞀪◌̕b; a◌֮◌̀◌𞀪◌̕b; à◌֮◌𞀪◌̕b; a◌֮◌̀◌𞀪◌̕b; ) LATIN SMALL LETTER 
A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, 
COMBINING GLAGOLITIC LETTER FITA, LATIN SMALL LETTER B
 0061 1E02A 0315 0300 05AE 0062;0061 05AE 1E02A 0300 0315 0062;0061 05AE 1E02A 
0300 0315 0062;0061 05AE 1E02A 0300 0315 0062;0061 05AE 1E02A 0300 0315 0062; # 
(a◌𞀪◌̕◌̀◌֮b; a◌֮◌𞀪◌̀◌̕b; a◌֮◌𞀪◌̀◌̕b; a◌֮◌𞀪◌̀◌̕b; a◌֮◌𞀪◌̀◌̕b; ) LATIN SMALL 
LETTER A, COMBINING GLAGOLITIC LETTER FITA, COMBINING COMMA ABOVE RIGHT, 
COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 0315 0300 05AE 1E08F 0062;00E0 05AE 1E08F 0315 0062;0061 05AE 0300 1E08F 
0315 0062;00E0 05AE 1E08F 0315 0062;0061 05AE 0300 1E08F 0315 0062; # 
(a◌̕◌̀◌֮◌𞂏b; à◌֮◌𞂏◌̕b; a◌֮◌̀◌𞂏◌̕b; à◌֮◌𞂏◌̕b; a◌֮◌̀◌𞂏◌̕b; ) LATIN SMALL LETTER 
A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, 
COMBINING CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I, LATIN SMALL LETTER B
+0061 1E08F 0315 0300 05AE 0062;0061 05AE 1E08F 0300 0315 0062;0061 05AE 1E08F 
0300 0315 0062;0061 05AE 1E08F 0300 0315 0062;0061 05AE 1E08F 0300 0315 0062; # 
(a◌𞂏◌̕◌̀◌֮b; a◌֮◌𞂏◌̀◌̕b; a◌֮◌𞂏◌̀◌̕b; a◌֮◌𞂏◌̀◌̕b; a◌֮◌𞂏◌̀◌̕b; ) LATIN SMALL 
LETTER A, COMBINING CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I, COMBINING 
COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL 
LETTER B
 0061 0315 0300 05AE 1E130 0062;00E0 05AE 1E130 0315 0062;0061 05AE 0300 1E130 
0315 0062;00E0 05AE 1E130 0315 0062;0061 05AE 0300 1E130 0315 0062; # 
(a◌̕◌̀◌֮◌𞄰b; à◌֮◌𞄰◌̕b; a◌֮◌̀◌𞄰◌̕b; à◌֮◌𞄰◌̕b; a◌֮◌̀◌𞄰◌̕b; ) LATIN SMALL LETTER 
A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, 
NYIAKENG PUACHUE HMONG TONE-B, LATIN SMALL LETTER B
 0061 1E130 0315 0300 05AE 0062;0061 05AE 1E130 0300 0315 0062;0061 05AE 1E130 
0300 0315 0062;0061 05AE 1E130 0300 0315 0062;0061 05AE 1E130 0300 0315 0062; # 
(a◌𞄰◌̕◌̀◌֮b; a◌֮◌𞄰◌̀◌̕b; a◌֮◌𞄰◌̀◌̕b; a◌֮◌𞄰◌̀◌̕b; a◌֮◌𞄰◌̀◌̕b; ) LATIN SMALL 
LETTER A, NYIAKENG PUACHUE HMONG TONE-B, COMBINING COMMA ABOVE RIGHT, COMBINING 
GRAVE ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
 0061 0315 0300 05AE 1E131 0062;00E0 05AE 1E131 0315 0062;0061 05AE 0300 1E131 
0315 0062;00E0 05AE 1E131 0315 0062;0061 05AE 0300 1E131 0315 0062; # 
(a◌̕◌̀◌֮◌𞄱b; à◌֮◌𞄱◌̕b; a◌֮◌̀◌𞄱◌̕b; à◌֮◌𞄱◌̕b; a◌֮◌̀◌𞄱◌̕b; ) LATIN SMALL LETTER 
A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, 
NYIAKENG PUACHUE HMONG TONE-M, LATIN SMALL LETTER B
@@ -18836,6 +18910,14 @@ FFEE;FFEE;FFEE;25CB;25CB; # (○; ○; ○; ○; ○; ) 
HALFWIDTH WHITE CIRCLE
 0061 1E2EE 0315 0300 05AE 0062;0061 05AE 1E2EE 0300 0315 0062;0061 05AE 1E2EE 
0300 0315 0062;0061 05AE 1E2EE 0300 0315 0062;0061 05AE 1E2EE 0300 0315 0062; # 
(a◌𞋮◌̕◌̀◌֮b; a◌֮◌𞋮◌̀◌̕b; a◌֮◌𞋮◌̀◌̕b; a◌֮◌𞋮◌̀◌̕b; a◌֮◌𞋮◌̀◌̕b; ) LATIN SMALL 
LETTER A, WANCHO TONE KOI, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, 
HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
 0061 0315 0300 05AE 1E2EF 0062;00E0 05AE 1E2EF 0315 0062;0061 05AE 0300 1E2EF 
0315 0062;00E0 05AE 1E2EF 0315 0062;0061 05AE 0300 1E2EF 0315 0062; # 
(a◌̕◌̀◌֮◌𞋯b; à◌֮◌𞋯◌̕b; a◌֮◌̀◌𞋯◌̕b; à◌֮◌𞋯◌̕b; a◌֮◌̀◌𞋯◌̕b; ) LATIN SMALL LETTER 
A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, 
WANCHO TONE KOINI, LATIN SMALL LETTER B
 0061 1E2EF 0315 0300 05AE 0062;0061 05AE 1E2EF 0300 0315 0062;0061 05AE 1E2EF 
0300 0315 0062;0061 05AE 1E2EF 0300 0315 0062;0061 05AE 1E2EF 0300 0315 0062; # 
(a◌𞋯◌̕◌̀◌֮b; a◌֮◌𞋯◌̀◌̕b; a◌֮◌𞋯◌̀◌̕b; a◌֮◌𞋯◌̀◌̕b; a◌֮◌𞋯◌̀◌̕b; ) LATIN SMALL 
LETTER A, WANCHO TONE KOINI, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE 
ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
+0061 035C 0315 0300 1E4EC 0062;00E0 0315 1E4EC 035C 0062;0061 0300 0315 1E4EC 
035C 0062;00E0 0315 1E4EC 035C 0062;0061 0300 0315 1E4EC 035C 0062; # 
(a◌͜◌̕◌̀◌𞓬b; à◌̕◌𞓬◌͜b; a◌̀◌̕◌𞓬◌͜b; à◌̕◌𞓬◌͜b; a◌̀◌̕◌𞓬◌͜b; ) LATIN SMALL LETTER 
A, COMBINING DOUBLE BREVE BELOW, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE 
ACCENT, NAG MUNDARI SIGN MUHOR, LATIN SMALL LETTER B
+0061 1E4EC 035C 0315 0300 0062;00E0 1E4EC 0315 035C 0062;0061 0300 1E4EC 0315 
035C 0062;00E0 1E4EC 0315 035C 0062;0061 0300 1E4EC 0315 035C 0062; # 
(a◌𞓬◌͜◌̕◌̀b; à◌𞓬◌̕◌͜b; a◌̀◌𞓬◌̕◌͜b; à◌𞓬◌̕◌͜b; a◌̀◌𞓬◌̕◌͜b; ) LATIN SMALL LETTER 
A, NAG MUNDARI SIGN MUHOR, COMBINING DOUBLE BREVE BELOW, COMBINING COMMA ABOVE 
RIGHT, COMBINING GRAVE ACCENT, LATIN SMALL LETTER B
+0061 035C 0315 0300 1E4ED 0062;00E0 0315 1E4ED 035C 0062;0061 0300 0315 1E4ED 
035C 0062;00E0 0315 1E4ED 035C 0062;0061 0300 0315 1E4ED 035C 0062; # 
(a◌͜◌̕◌̀◌𞓭b; à◌̕◌𞓭◌͜b; a◌̀◌̕◌𞓭◌͜b; à◌̕◌𞓭◌͜b; a◌̀◌̕◌𞓭◌͜b; ) LATIN SMALL LETTER 
A, COMBINING DOUBLE BREVE BELOW, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE 
ACCENT, NAG MUNDARI SIGN TOYOR, LATIN SMALL LETTER B
+0061 1E4ED 035C 0315 0300 0062;00E0 1E4ED 0315 035C 0062;0061 0300 1E4ED 0315 
035C 0062;00E0 1E4ED 0315 035C 0062;0061 0300 1E4ED 0315 035C 0062; # 
(a◌𞓭◌͜◌̕◌̀b; à◌𞓭◌̕◌͜b; a◌̀◌𞓭◌̕◌͜b; à◌𞓭◌̕◌͜b; a◌̀◌𞓭◌̕◌͜b; ) LATIN SMALL LETTER 
A, NAG MUNDARI SIGN TOYOR, COMBINING DOUBLE BREVE BELOW, COMBINING COMMA ABOVE 
RIGHT, COMBINING GRAVE ACCENT, LATIN SMALL LETTER B
+0061 059A 0316 1DFA 1E4EE 0062;0061 1DFA 0316 1E4EE 059A 0062;0061 1DFA 0316 
1E4EE 059A 0062;0061 1DFA 0316 1E4EE 059A 0062;0061 1DFA 0316 1E4EE 059A 0062; 
# (a◌֚◌̖◌᷺◌𞓮b; a◌᷺◌̖◌𞓮◌֚b; a◌᷺◌̖◌𞓮◌֚b; a◌᷺◌̖◌𞓮◌֚b; a◌᷺◌̖◌𞓮◌֚b; ) LATIN SMALL 
LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, COMBINING DOT 
BELOW LEFT, NAG MUNDARI SIGN IKIR, LATIN SMALL LETTER B
+0061 1E4EE 059A 0316 1DFA 0062;0061 1DFA 1E4EE 0316 059A 0062;0061 1DFA 1E4EE 
0316 059A 0062;0061 1DFA 1E4EE 0316 059A 0062;0061 1DFA 1E4EE 0316 059A 0062; # 
(a◌𞓮◌֚◌̖◌᷺b; a◌᷺◌𞓮◌̖◌֚b; a◌᷺◌𞓮◌̖◌֚b; a◌᷺◌𞓮◌̖◌֚b; a◌᷺◌𞓮◌̖◌֚b; ) LATIN SMALL 
LETTER A, NAG MUNDARI SIGN IKIR, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT 
BELOW, COMBINING DOT BELOW LEFT, LATIN SMALL LETTER B
+0061 0315 0300 05AE 1E4EF 0062;00E0 05AE 1E4EF 0315 0062;0061 05AE 0300 1E4EF 
0315 0062;00E0 05AE 1E4EF 0315 0062;0061 05AE 0300 1E4EF 0315 0062; # 
(a◌̕◌̀◌֮◌𞓯b; à◌֮◌𞓯◌̕b; a◌֮◌̀◌𞓯◌̕b; à◌֮◌𞓯◌̕b; a◌֮◌̀◌𞓯◌̕b; ) LATIN SMALL LETTER 
A, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE ACCENT, HEBREW ACCENT ZINOR, 
NAG MUNDARI SIGN SUTUH, LATIN SMALL LETTER B
+0061 1E4EF 0315 0300 05AE 0062;0061 05AE 1E4EF 0300 0315 0062;0061 05AE 1E4EF 
0300 0315 0062;0061 05AE 1E4EF 0300 0315 0062;0061 05AE 1E4EF 0300 0315 0062; # 
(a◌𞓯◌̕◌̀◌֮b; a◌֮◌𞓯◌̀◌̕b; a◌֮◌𞓯◌̀◌̕b; a◌֮◌𞓯◌̀◌̕b; a◌֮◌𞓯◌̀◌̕b; ) LATIN SMALL 
LETTER A, NAG MUNDARI SIGN SUTUH, COMBINING COMMA ABOVE RIGHT, COMBINING GRAVE 
ACCENT, HEBREW ACCENT ZINOR, LATIN SMALL LETTER B
 0061 059A 0316 1DFA 1E8D0 0062;0061 1DFA 0316 1E8D0 059A 0062;0061 1DFA 0316 
1E8D0 059A 0062;0061 1DFA 0316 1E8D0 059A 0062;0061 1DFA 0316 1E8D0 059A 0062; 
# (a◌֚◌̖◌᷺◌𞣐b; a◌᷺◌̖◌𞣐◌֚b; a◌᷺◌̖◌𞣐◌֚b; a◌᷺◌̖◌𞣐◌֚b; a◌᷺◌̖◌𞣐◌֚b; ) LATIN SMALL 
LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, COMBINING DOT 
BELOW LEFT, MENDE KIKAKUI COMBINING NUMBER TEENS, LATIN SMALL LETTER B
 0061 1E8D0 059A 0316 1DFA 0062;0061 1DFA 1E8D0 0316 059A 0062;0061 1DFA 1E8D0 
0316 059A 0062;0061 1DFA 1E8D0 0316 059A 0062;0061 1DFA 1E8D0 0316 059A 0062; # 
(a◌𞣐◌֚◌̖◌᷺b; a◌᷺◌𞣐◌̖◌֚b; a◌᷺◌𞣐◌̖◌֚b; a◌᷺◌𞣐◌̖◌֚b; a◌᷺◌𞣐◌̖◌֚b; ) LATIN SMALL 
LETTER A, MENDE KIKAKUI COMBINING NUMBER TEENS, HEBREW ACCENT YETIV, COMBINING 
GRAVE ACCENT BELOW, COMBINING DOT BELOW LEFT, LATIN SMALL LETTER B
 0061 059A 0316 1DFA 1E8D1 0062;0061 1DFA 0316 1E8D1 059A 0062;0061 1DFA 0316 
1E8D1 059A 0062;0061 1DFA 0316 1E8D1 059A 0062;0061 1DFA 0316 1E8D1 059A 0062; 
# (a◌֚◌̖◌᷺◌𞣑b; a◌᷺◌̖◌𞣑◌֚b; a◌᷺◌̖◌𞣑◌֚b; a◌᷺◌̖◌𞣑◌֚b; a◌᷺◌̖◌𞣑◌֚b; ) LATIN SMALL 
LETTER A, HEBREW ACCENT YETIV, COMBINING GRAVE ACCENT BELOW, COMBINING DOT 
BELOW LEFT, MENDE KIKAKUI COMBINING NUMBER TENS, LATIN SMALL LETTER B
diff --git a/admin/unidata/PropertyValueAliases.txt 
b/admin/unidata/PropertyValueAliases.txt
index bdc13857dc..9346fcf03e 100644
--- a/admin/unidata/PropertyValueAliases.txt
+++ b/admin/unidata/PropertyValueAliases.txt
@@ -1,11 +1,11 @@
-# PropertyValueAliases-14.0.0.txt
-# Date: 2021-05-10, 21:08:53 GMT
-# © 2021 Unicode®, Inc.
+# PropertyValueAliases-15.0.0.txt
+# Date: 2022-08-05, 23:42:17 GMT
+# © 2022 Unicode®, Inc.
 # Unicode and the Unicode Logo are registered trademarks of Unicode, Inc. in 
the U.S. and other countries.
-# For terms of use, see http://www.unicode.org/terms_of_use.html
+# For terms of use, see https://www.unicode.org/terms_of_use.html
 #
 # Unicode Character Database
-#   For documentation, see http://www.unicode.org/reports/tr44/
+#   For documentation, see https://www.unicode.org/reports/tr44/
 #
 # This file contains aliases for property values used in the UCD.
 # These names can be used for XML formats of UCD data, for regular-expression
@@ -90,6 +90,7 @@ age; 12.0                             ; V12_0
 age; 12.1                             ; V12_1
 age; 13.0                             ; V13_0
 age; 14.0                             ; V14_0
+age; 15.0                             ; V15_0
 age; NA                               ; Unassigned
 
 # Alphabetic (Alpha)
@@ -135,7 +136,6 @@ Bidi_M; Y                             ; Yes                 
             ; T
 
 # Bidi_Mirroring_Glyph (bmg)
 
-# @missing: 0000..10FFFF; Bidi_Mirroring_Glyph; <none>
 
 # Bidi_Paired_Bracket (bpb)
 
@@ -162,6 +162,7 @@ blk; Ancient_Symbols                  ; Ancient_Symbols
 blk; Arabic                           ; Arabic
 blk; Arabic_Ext_A                     ; Arabic_Extended_A
 blk; Arabic_Ext_B                     ; Arabic_Extended_B
+blk; Arabic_Ext_C                     ; Arabic_Extended_C
 blk; Arabic_Math                      ; Arabic_Mathematical_Alphabetic_Symbols
 blk; Arabic_PF_A                      ; Arabic_Presentation_Forms_A      ; 
Arabic_Presentation_Forms-A
 blk; Arabic_PF_B                      ; Arabic_Presentation_Forms_B
@@ -206,6 +207,7 @@ blk; CJK_Ext_D                        ; 
CJK_Unified_Ideographs_Extension_D
 blk; CJK_Ext_E                        ; CJK_Unified_Ideographs_Extension_E
 blk; CJK_Ext_F                        ; CJK_Unified_Ideographs_Extension_F
 blk; CJK_Ext_G                        ; CJK_Unified_Ideographs_Extension_G
+blk; CJK_Ext_H                        ; CJK_Unified_Ideographs_Extension_H
 blk; CJK_Radicals_Sup                 ; CJK_Radicals_Supplement
 blk; CJK_Strokes                      ; CJK_Strokes
 blk; CJK_Symbols                      ; CJK_Symbols_And_Punctuation
@@ -223,10 +225,12 @@ blk; Cyrillic                         ; Cyrillic
 blk; Cyrillic_Ext_A                   ; Cyrillic_Extended_A
 blk; Cyrillic_Ext_B                   ; Cyrillic_Extended_B
 blk; Cyrillic_Ext_C                   ; Cyrillic_Extended_C
+blk; Cyrillic_Ext_D                   ; Cyrillic_Extended_D
 blk; Cyrillic_Sup                     ; Cyrillic_Supplement              ; 
Cyrillic_Supplementary
 blk; Deseret                          ; Deseret
 blk; Devanagari                       ; Devanagari
 blk; Devanagari_Ext                   ; Devanagari_Extended
+blk; Devanagari_Ext_A                 ; Devanagari_Extended_A
 blk; Diacriticals                     ; Combining_Diacritical_Marks
 blk; Diacriticals_Ext                 ; Combining_Diacritical_Marks_Extended
 blk; Diacriticals_For_Symbols         ; 
Combining_Diacritical_Marks_For_Symbols; Combining_Marks_For_Symbols
@@ -288,6 +292,7 @@ blk; Jamo_Ext_A                       ; 
Hangul_Jamo_Extended_A
 blk; Jamo_Ext_B                       ; Hangul_Jamo_Extended_B
 blk; Javanese                         ; Javanese
 blk; Kaithi                           ; Kaithi
+blk; Kaktovik_Numerals                ; Kaktovik_Numerals
 blk; Kana_Ext_A                       ; Kana_Extended_A
 blk; Kana_Ext_B                       ; Kana_Extended_B
 blk; Kana_Sup                         ; Kana_Supplement
@@ -296,6 +301,7 @@ blk; Kangxi                           ; Kangxi_Radicals
 blk; Kannada                          ; Kannada
 blk; Katakana                         ; Katakana
 blk; Katakana_Ext                     ; Katakana_Phonetic_Extensions
+blk; Kawi                             ; Kawi
 blk; Kayah_Li                         ; Kayah_Li
 blk; Kharoshthi                       ; Kharoshthi
 blk; Khitan_Small_Script              ; Khitan_Small_Script
@@ -360,6 +366,7 @@ blk; Myanmar                          ; Myanmar
 blk; Myanmar_Ext_A                    ; Myanmar_Extended_A
 blk; Myanmar_Ext_B                    ; Myanmar_Extended_B
 blk; Nabataean                        ; Nabataean
+blk; Nag_Mundari                      ; Nag_Mundari
 blk; Nandinagari                      ; Nandinagari
 blk; NB                               ; No_Block
 blk; New_Tai_Lue                      ; New_Tai_Lue
@@ -663,7 +670,6 @@ EPres; Y                              ; Yes                 
             ; T
 
 # Equivalent_Unified_Ideograph (EqUIdeo)
 
-# @missing: 0000..10FFFF; Equivalent_Unified_Ideograph; <none>
 
 # Expands_On_NFC (XO_NFC)
 
@@ -1143,7 +1149,6 @@ NFD_QC; Y                             ; Yes
 
 # NFKC_Casefold (NFKC_CF)
 
-# @missing: 0000..10FFFF; NFKC_Casefold; <code point>
 
 # NFKC_Quick_Check (NFKC_QC)
 
@@ -1313,6 +1318,7 @@ sc ; Ital                             ; Old_Italic
 sc ; Java                             ; Javanese
 sc ; Kali                             ; Kayah_Li
 sc ; Kana                             ; Katakana
+sc ; Kawi                             ; Kawi
 sc ; Khar                             ; Kharoshthi
 sc ; Khmr                             ; Khmer
 sc ; Khoj                             ; Khojki
@@ -1345,6 +1351,7 @@ sc ; Mroo                             ; Mro
 sc ; Mtei                             ; Meetei_Mayek
 sc ; Mult                             ; Multani
 sc ; Mymr                             ; Myanmar
+sc ; Nagm                             ; Nag_Mundari
 sc ; Nand                             ; Nandinagari
 sc ; Narb                             ; Old_North_Arabian
 sc ; Nbat                             ; Nabataean
@@ -1418,7 +1425,6 @@ sc ; Zzzz                             ; Unknown
 
 # Script_Extensions (scx)
 
-# @missing: 0000..10FFFF; Script_Extensions; <script>
 
 # Sentence_Break (SB)
 
diff --git a/admin/unidata/ScriptExtensions.txt 
b/admin/unidata/ScriptExtensions.txt
index 3f5cd1c0db..2f5a1727e3 100644
--- a/admin/unidata/ScriptExtensions.txt
+++ b/admin/unidata/ScriptExtensions.txt
@@ -1,11 +1,11 @@
-# ScriptExtensions-14.0.0.txt
-# Date: 2021-06-04, 02:19:38 GMT
-# © 2021 Unicode®, Inc.
+# ScriptExtensions-15.0.0.txt
+# Date: 2022-02-02, 00:57:11 GMT
+# © 2022 Unicode®, Inc.
 # Unicode and the Unicode Logo are registered trademarks of Unicode, Inc. in 
the U.S. and other countries.
-# For terms of use, see http://www.unicode.org/terms_of_use.html
+# For terms of use, see https://www.unicode.org/terms_of_use.html
 #
 # Unicode Character Database
-#   For documentation, see http://www.unicode.org/reports/tr44/
+#   For documentation, see https://www.unicode.org/reports/tr44/
 #
 # The Script_Extensions property indicates which characters are commonly used
 # with more than one script, but with a limited number of scripts.
diff --git a/admin/unidata/Scripts.txt b/admin/unidata/Scripts.txt
index a138373011..2b138bffb8 100644
--- a/admin/unidata/Scripts.txt
+++ b/admin/unidata/Scripts.txt
@@ -1,11 +1,11 @@
-# Scripts-14.0.0.txt
-# Date: 2021-07-10, 00:35:31 GMT
-# © 2021 Unicode®, Inc.
+# Scripts-15.0.0.txt
+# Date: 2022-04-26, 23:15:02 GMT
+# © 2022 Unicode®, Inc.
 # Unicode and the Unicode Logo are registered trademarks of Unicode, Inc. in 
the U.S. and other countries.
-# For terms of use, see http://www.unicode.org/terms_of_use.html
+# For terms of use, see https://www.unicode.org/terms_of_use.html
 #
 # Unicode Character Database
-#   For documentation, see http://www.unicode.org/reports/tr44/
+#   For documentation, see https://www.unicode.org/reports/tr44/
 # For more information, see:
 #   UAX #24, Unicode Script Property: https://www.unicode.org/reports/tr24/
 #     Especially the sections:
@@ -532,6 +532,7 @@ FFFC..FFFD    ; Common # So   [2] OBJECT REPLACEMENT 
CHARACTER..REPLACEMENT CHAR
 1D183..1D184  ; Common # So   [2] MUSICAL SYMBOL ARPEGGIATO UP..MUSICAL SYMBOL 
ARPEGGIATO DOWN
 1D18C..1D1A9  ; Common # So  [30] MUSICAL SYMBOL RINFORZANDO..MUSICAL SYMBOL 
DEGREE SLASH
 1D1AE..1D1EA  ; Common # So  [61] MUSICAL SYMBOL PEDAL MARK..MUSICAL SYMBOL 
KORON
+1D2C0..1D2D3  ; Common # No  [20] KAKTOVIK NUMERAL ZERO..KAKTOVIK NUMERAL 
NINETEEN
 1D2E0..1D2F3  ; Common # No  [20] MAYAN NUMERAL ZERO..MAYAN NUMERAL NINETEEN
 1D300..1D356  ; Common # So  [87] MONOGRAM FOR EARTH..TETRAGRAM FOR FOSTERING
 1D360..1D378  ; Common # No  [25] COUNTING ROD UNIT DIGIT ONE..TALLY MARK FIVE
@@ -601,10 +602,10 @@ FFFC..FFFD    ; Common # So   [2] OBJECT REPLACEMENT 
CHARACTER..REPLACEMENT CHAR
 1F300..1F3FA  ; Common # So [251] CYCLONE..AMPHORA
 1F3FB..1F3FF  ; Common # Sk   [5] EMOJI MODIFIER FITZPATRICK TYPE-1-2..EMOJI 
MODIFIER FITZPATRICK TYPE-6
 1F400..1F6D7  ; Common # So [728] RAT..ELEVATOR
-1F6DD..1F6EC  ; Common # So  [16] PLAYGROUND SLIDE..AIRPLANE ARRIVING
+1F6DC..1F6EC  ; Common # So  [17] WIRELESS..AIRPLANE ARRIVING
 1F6F0..1F6FC  ; Common # So  [13] SATELLITE..ROLLER SKATE
-1F700..1F773  ; Common # So [116] ALCHEMICAL SYMBOL FOR 
QUINTESSENCE..ALCHEMICAL SYMBOL FOR HALF OUNCE
-1F780..1F7D8  ; Common # So  [89] BLACK LEFT-POINTING ISOSCELES RIGHT 
TRIANGLE..NEGATIVE CIRCLED SQUARE
+1F700..1F776  ; Common # So [119] ALCHEMICAL SYMBOL FOR QUINTESSENCE..LUNAR 
ECLIPSE
+1F77B..1F7D9  ; Common # So  [95] HAUMEA..NINE POINTED WHITE STAR
 1F7E0..1F7EB  ; Common # So  [12] LARGE ORANGE CIRCLE..LARGE BROWN SQUARE
 1F7F0         ; Common # So       HEAVY EQUALS SIGN
 1F800..1F80B  ; Common # So  [12] LEFTWARDS ARROW WITH SMALL TRIANGLE 
ARROWHEAD..DOWNWARDS ARROW WITH LARGE TRIANGLE ARROWHEAD
@@ -615,22 +616,20 @@ FFFC..FFFD    ; Common # So   [2] OBJECT REPLACEMENT 
CHARACTER..REPLACEMENT CHAR
 1F8B0..1F8B1  ; Common # So   [2] ARROW POINTING UPWARDS THEN NORTH 
WEST..ARROW POINTING RIGHTWARDS THEN CURVING SOUTH WEST
 1F900..1FA53  ; Common # So [340] CIRCLED CROSS FORMEE WITH FOUR DOTS..BLACK 
CHESS KNIGHT-BISHOP
 1FA60..1FA6D  ; Common # So  [14] XIANGQI RED GENERAL..XIANGQI BLACK SOLDIER
-1FA70..1FA74  ; Common # So   [5] BALLET SHOES..THONG SANDAL
-1FA78..1FA7C  ; Common # So   [5] DROP OF BLOOD..CRUTCH
-1FA80..1FA86  ; Common # So   [7] YO-YO..NESTING DOLLS
-1FA90..1FAAC  ; Common # So  [29] RINGED PLANET..HAMSA
-1FAB0..1FABA  ; Common # So  [11] FLY..NEST WITH EGGS
-1FAC0..1FAC5  ; Common # So   [6] ANATOMICAL HEART..PERSON WITH CROWN
-1FAD0..1FAD9  ; Common # So  [10] BLUEBERRIES..JAR
-1FAE0..1FAE7  ; Common # So   [8] MELTING FACE..BUBBLES
-1FAF0..1FAF6  ; Common # So   [7] HAND WITH INDEX FINGER AND THUMB 
CROSSED..HEART HANDS
+1FA70..1FA7C  ; Common # So  [13] BALLET SHOES..CRUTCH
+1FA80..1FA88  ; Common # So   [9] YO-YO..FLUTE
+1FA90..1FABD  ; Common # So  [46] RINGED PLANET..WING
+1FABF..1FAC5  ; Common # So   [7] GOOSE..PERSON WITH CROWN
+1FACE..1FADB  ; Common # So  [14] MOOSE..PEA POD
+1FAE0..1FAE8  ; Common # So   [9] MELTING FACE..SHAKING FACE
+1FAF0..1FAF8  ; Common # So   [9] HAND WITH INDEX FINGER AND THUMB 
CROSSED..RIGHTWARDS PUSHING HAND
 1FB00..1FB92  ; Common # So [147] BLOCK SEXTANT-1..UPPER HALF INVERSE MEDIUM 
SHADE AND LOWER HALF BLOCK
 1FB94..1FBCA  ; Common # So  [55] LEFT HALF INVERSE MEDIUM SHADE AND RIGHT 
HALF BLOCK..WHITE UP-POINTING CHEVRON
 1FBF0..1FBF9  ; Common # Nd  [10] SEGMENTED DIGIT ZERO..SEGMENTED DIGIT NINE
 E0001         ; Common # Cf       LANGUAGE TAG
 E0020..E007F  ; Common # Cf  [96] TAG SPACE..CANCEL TAG
 
-# Total code points: 8252
+# Total code points: 8301
 
 # ================================================
 
@@ -697,8 +696,9 @@ FF41..FF5A    ; Latin # L&  [26] FULLWIDTH LATIN SMALL 
LETTER A..FULLWIDTH LATIN
 1DF00..1DF09  ; Latin # L&  [10] LATIN SMALL LETTER FENG DIGRAPH WITH 
TRILL..LATIN SMALL LETTER T WITH HOOK AND RETROFLEX HOOK
 1DF0A         ; Latin # Lo       LATIN LETTER RETROFLEX CLICK WITH RETROFLEX 
HOOK
 1DF0B..1DF1E  ; Latin # L&  [20] LATIN SMALL LETTER ESH WITH DOUBLE BAR..LATIN 
SMALL LETTER S WITH CURL
+1DF25..1DF2A  ; Latin # L&   [6] LATIN SMALL LETTER D WITH MID-HEIGHT LEFT 
HOOK..LATIN SMALL LETTER T WITH MID-HEIGHT LEFT HOOK
 
-# Total code points: 1475
+# Total code points: 1481
 
 # ================================================
 
@@ -784,8 +784,10 @@ A680..A69B    ; Cyrillic # L&  [28] CYRILLIC CAPITAL 
LETTER DWE..CYRILLIC SMALL
 A69C..A69D    ; Cyrillic # Lm   [2] MODIFIER LETTER CYRILLIC HARD 
SIGN..MODIFIER LETTER CYRILLIC SOFT SIGN
 A69E..A69F    ; Cyrillic # Mn   [2] COMBINING CYRILLIC LETTER EF..COMBINING 
CYRILLIC LETTER IOTIFIED E
 FE2E..FE2F    ; Cyrillic # Mn   [2] COMBINING CYRILLIC TITLO LEFT 
HALF..COMBINING CYRILLIC TITLO RIGHT HALF
+1E030..1E06D  ; Cyrillic # Lm  [62] MODIFIER LETTER CYRILLIC SMALL A..MODIFIER 
LETTER CYRILLIC SMALL STRAIGHT U WITH STROKE
+1E08F         ; Cyrillic # Mn       COMBINING CYRILLIC SMALL LETTER 
BYELORUSSIAN-UKRAINIAN I
 
-# Total code points: 443
+# Total code points: 506
 
 # ================================================
 
@@ -883,6 +885,7 @@ FDFD..FDFF    ; Arabic # So   [3] ARABIC LIGATURE BISMILLAH 
AR-RAHMAN AR-RAHEEM.
 FE70..FE74    ; Arabic # Lo   [5] ARABIC FATHATAN ISOLATED FORM..ARABIC 
KASRATAN ISOLATED FORM
 FE76..FEFC    ; Arabic # Lo [135] ARABIC FATHA ISOLATED FORM..ARABIC LIGATURE 
LAM WITH ALEF FINAL FORM
 10E60..10E7E  ; Arabic # No  [31] RUMI DIGIT ONE..RUMI FRACTION TWO THIRDS
+10EFD..10EFF  ; Arabic # Mn   [3] ARABIC SMALL LOW WORD SAKTA..ARABIC SMALL 
LOW WORD MADDA
 1EE00..1EE03  ; Arabic # Lo   [4] ARABIC MATHEMATICAL ALEF..ARABIC 
MATHEMATICAL DAL
 1EE05..1EE1F  ; Arabic # Lo  [27] ARABIC MATHEMATICAL WAW..ARABIC MATHEMATICAL 
DOTLESS QAF
 1EE21..1EE22  ; Arabic # Lo   [2] ARABIC MATHEMATICAL INITIAL BEH..ARABIC 
MATHEMATICAL INITIAL JEEM
@@ -918,7 +921,7 @@ FE76..FEFC    ; Arabic # Lo [135] ARABIC FATHA ISOLATED 
FORM..ARABIC LIGATURE LA
 1EEAB..1EEBB  ; Arabic # Lo  [17] ARABIC MATHEMATICAL DOUBLE-STRUCK 
LAM..ARABIC MATHEMATICAL DOUBLE-STRUCK GHAIN
 1EEF0..1EEF1  ; Arabic # Sm   [2] ARABIC MATHEMATICAL OPERATOR MEEM WITH HAH 
WITH TATWEEL..ARABIC MATHEMATICAL OPERATOR HAH WITH DAL
 
-# Total code points: 1365
+# Total code points: 1368
 
 # ================================================
 
@@ -970,8 +973,9 @@ A8FB          ; Devanagari # Lo       DEVANAGARI HEADSTROKE
 A8FC          ; Devanagari # Po       DEVANAGARI SIGN SIDDHAM
 A8FD..A8FE    ; Devanagari # Lo   [2] DEVANAGARI JAIN OM..DEVANAGARI LETTER AY
 A8FF          ; Devanagari # Mn       DEVANAGARI VOWEL SIGN AY
+11B00..11B09  ; Devanagari # Po  [10] DEVANAGARI HEAD MARK..DEVANAGARI SIGN 
MINDU
 
-# Total code points: 154
+# Total code points: 164
 
 # ================================================
 
@@ -1182,8 +1186,9 @@ A8FF          ; Devanagari # Mn       DEVANAGARI VOWEL 
SIGN AY
 0CE2..0CE3    ; Kannada # Mn   [2] KANNADA VOWEL SIGN VOCALIC L..KANNADA VOWEL 
SIGN VOCALIC LL
 0CE6..0CEF    ; Kannada # Nd  [10] KANNADA DIGIT ZERO..KANNADA DIGIT NINE
 0CF1..0CF2    ; Kannada # Lo   [2] KANNADA SIGN JIHVAMULIYA..KANNADA SIGN 
UPADHMANIYA
+0CF3          ; Kannada # Mc       KANNADA SIGN COMBINING ANUSVARA ABOVE RIGHT
 
-# Total code points: 90
+# Total code points: 91
 
 # ================================================
 
@@ -1263,11 +1268,11 @@ A8FF          ; Devanagari # Mn       DEVANAGARI VOWEL 
SIGN AY
 0EBD          ; Lao # Lo       LAO SEMIVOWEL SIGN NYO
 0EC0..0EC4    ; Lao # Lo   [5] LAO VOWEL SIGN E..LAO VOWEL SIGN AI
 0EC6          ; Lao # Lm       LAO KO LA
-0EC8..0ECD    ; Lao # Mn   [6] LAO TONE MAI EK..LAO NIGGAHITA
+0EC8..0ECE    ; Lao # Mn   [7] LAO TONE MAI EK..LAO YAMAKKAN
 0ED0..0ED9    ; Lao # Nd  [10] LAO DIGIT ZERO..LAO DIGIT NINE
 0EDC..0EDF    ; Lao # Lo   [4] LAO HO NO..LAO LETTER KHMU NYO
 
-# Total code points: 82
+# Total code points: 83
 
 # ================================================
 
@@ -1532,10 +1537,11 @@ AB70..ABBF    ; Cherokee # L&  [80] CHEROKEE SMALL 
LETTER A..CHEROKEE SMALL LETT
 309D..309E    ; Hiragana # Lm   [2] HIRAGANA ITERATION MARK..HIRAGANA VOICED 
ITERATION MARK
 309F          ; Hiragana # Lo       HIRAGANA DIGRAPH YORI
 1B001..1B11F  ; Hiragana # Lo [287] HIRAGANA LETTER ARCHAIC YE..HIRAGANA 
LETTER ARCHAIC WU
+1B132         ; Hiragana # Lo       HIRAGANA LETTER SMALL KO
 1B150..1B152  ; Hiragana # Lo   [3] HIRAGANA LETTER SMALL WI..HIRAGANA LETTER 
SMALL WO
 1F200         ; Hiragana # So       SQUARE HIRAGANA HOKA
 
-# Total code points: 380
+# Total code points: 381
 
 # ================================================
 
@@ -1552,9 +1558,10 @@ FF71..FF9D    ; Katakana # Lo  [45] HALFWIDTH KATAKANA 
LETTER A..HALFWIDTH KATAK
 1AFFD..1AFFE  ; Katakana # Lm   [2] KATAKANA LETTER MINNAN NASALIZED 
TONE-7..KATAKANA LETTER MINNAN NASALIZED TONE-8
 1B000         ; Katakana # Lo       KATAKANA LETTER ARCHAIC E
 1B120..1B122  ; Katakana # Lo   [3] KATAKANA LETTER ARCHAIC YI..KATAKANA 
LETTER ARCHAIC WU
+1B155         ; Katakana # Lo       KATAKANA LETTER SMALL KO
 1B164..1B167  ; Katakana # Lo   [4] KATAKANA LETTER SMALL WI..KATAKANA LETTER 
SMALL N
 
-# Total code points: 320
+# Total code points: 321
 
 # ================================================
 
@@ -1582,14 +1589,15 @@ FA70..FAD9    ; Han # Lo [106] CJK COMPATIBILITY 
IDEOGRAPH-FA70..CJK COMPATIBILI
 16FE3         ; Han # Lm       OLD CHINESE ITERATION MARK
 16FF0..16FF1  ; Han # Mc   [2] VIETNAMESE ALTERNATE READING MARK 
CA..VIETNAMESE ALTERNATE READING MARK NHAY
 20000..2A6DF  ; Han # Lo [42720] CJK UNIFIED IDEOGRAPH-20000..CJK UNIFIED 
IDEOGRAPH-2A6DF
-2A700..2B738  ; Han # Lo [4153] CJK UNIFIED IDEOGRAPH-2A700..CJK UNIFIED 
IDEOGRAPH-2B738
+2A700..2B739  ; Han # Lo [4154] CJK UNIFIED IDEOGRAPH-2A700..CJK UNIFIED 
IDEOGRAPH-2B739
 2B740..2B81D  ; Han # Lo [222] CJK UNIFIED IDEOGRAPH-2B740..CJK UNIFIED 
IDEOGRAPH-2B81D
 2B820..2CEA1  ; Han # Lo [5762] CJK UNIFIED IDEOGRAPH-2B820..CJK UNIFIED 
IDEOGRAPH-2CEA1
 2CEB0..2EBE0  ; Han # Lo [7473] CJK UNIFIED IDEOGRAPH-2CEB0..CJK UNIFIED 
IDEOGRAPH-2EBE0
 2F800..2FA1D  ; Han # Lo [542] CJK COMPATIBILITY IDEOGRAPH-2F800..CJK 
COMPATIBILITY IDEOGRAPH-2FA1D
 30000..3134A  ; Han # Lo [4939] CJK UNIFIED IDEOGRAPH-30000..CJK UNIFIED 
IDEOGRAPH-3134A
+31350..323AF  ; Han # Lo [4192] CJK UNIFIED IDEOGRAPH-31350..CJK UNIFIED 
IDEOGRAPH-323AF
 
-# Total code points: 94215
+# Total code points: 98408
 
 # ================================================
 
@@ -2093,10 +2101,13 @@ AADE..AADF    ; Tai_Viet # Po   [2] TAI VIET SYMBOL HO 
HOI..TAI VIET SYMBOL KOI
 
 # ================================================
 
-13000..1342E  ; Egyptian_Hieroglyphs # Lo [1071] EGYPTIAN HIEROGLYPH 
A001..EGYPTIAN HIEROGLYPH AA032
-13430..13438  ; Egyptian_Hieroglyphs # Cf   [9] EGYPTIAN HIEROGLYPH VERTICAL 
JOINER..EGYPTIAN HIEROGLYPH END SEGMENT
+13000..1342F  ; Egyptian_Hieroglyphs # Lo [1072] EGYPTIAN HIEROGLYPH 
A001..EGYPTIAN HIEROGLYPH V011D
+13430..1343F  ; Egyptian_Hieroglyphs # Cf  [16] EGYPTIAN HIEROGLYPH VERTICAL 
JOINER..EGYPTIAN HIEROGLYPH END WALLED ENCLOSURE
+13440         ; Egyptian_Hieroglyphs # Mn       EGYPTIAN HIEROGLYPH MIRROR 
HORIZONTALLY
+13441..13446  ; Egyptian_Hieroglyphs # Lo   [6] EGYPTIAN HIEROGLYPH FULL 
BLANK..EGYPTIAN HIEROGLYPH WIDE LOST SIGN
+13447..13455  ; Egyptian_Hieroglyphs # Mn  [15] EGYPTIAN HIEROGLYPH MODIFIER 
DAMAGED AT TOP START..EGYPTIAN HIEROGLYPH MODIFIER DAMAGED
 
-# Total code points: 1080
+# Total code points: 1110
 
 # ================================================
 
@@ -2440,8 +2451,10 @@ ABF0..ABF9    ; Meetei_Mayek # Nd  [10] MEETEI MAYEK 
DIGIT ZERO..MEETEI MAYEK DI
 11236..11237  ; Khojki # Mn   [2] KHOJKI SIGN NUKTA..KHOJKI SIGN SHADDA
 11238..1123D  ; Khojki # Po   [6] KHOJKI DANDA..KHOJKI ABBREVIATION SIGN
 1123E         ; Khojki # Mn       KHOJKI SIGN SUKUN
+1123F..11240  ; Khojki # Lo   [2] KHOJKI LETTER QA..KHOJKI LETTER SHORT I
+11241         ; Khojki # Mn       KHOJKI VOWEL SIGN VOCALIC R
 
-# Total code points: 62
+# Total code points: 65
 
 # ================================================
 
@@ -2988,4 +3001,31 @@ ABF0..ABF9    ; Meetei_Mayek # Nd  [10] MEETEI MAYEK 
DIGIT ZERO..MEETEI MAYEK DI
 
 # Total code points: 70
 
+# ================================================
+
+11F00..11F01  ; Kawi # Mn   [2] KAWI SIGN CANDRABINDU..KAWI SIGN ANUSVARA
+11F02         ; Kawi # Lo       KAWI SIGN REPHA
+11F03         ; Kawi # Mc       KAWI SIGN VISARGA
+11F04..11F10  ; Kawi # Lo  [13] KAWI LETTER A..KAWI LETTER O
+11F12..11F33  ; Kawi # Lo  [34] KAWI LETTER KA..KAWI LETTER JNYA
+11F34..11F35  ; Kawi # Mc   [2] KAWI VOWEL SIGN AA..KAWI VOWEL SIGN ALTERNATE 
AA
+11F36..11F3A  ; Kawi # Mn   [5] KAWI VOWEL SIGN I..KAWI VOWEL SIGN VOCALIC R
+11F3E..11F3F  ; Kawi # Mc   [2] KAWI VOWEL SIGN E..KAWI VOWEL SIGN AI
+11F40         ; Kawi # Mn       KAWI VOWEL SIGN EU
+11F41         ; Kawi # Mc       KAWI SIGN KILLER
+11F42         ; Kawi # Mn       KAWI CONJOINER
+11F43..11F4F  ; Kawi # Po  [13] KAWI DANDA..KAWI PUNCTUATION CLOSING SPIRAL
+11F50..11F59  ; Kawi # Nd  [10] KAWI DIGIT ZERO..KAWI DIGIT NINE
+
+# Total code points: 86
+
+# ================================================
+
+1E4D0..1E4EA  ; Nag_Mundari # Lo  [27] NAG MUNDARI LETTER O..NAG MUNDARI 
LETTER ELL
+1E4EB         ; Nag_Mundari # Lm       NAG MUNDARI SIGN OJOD
+1E4EC..1E4EF  ; Nag_Mundari # Mn   [4] NAG MUNDARI SIGN MUHOR..NAG MUNDARI 
SIGN SUTUH
+1E4F0..1E4F9  ; Nag_Mundari # Nd  [10] NAG MUNDARI DIGIT ZERO..NAG MUNDARI 
DIGIT NINE
+
+# Total code points: 42
+
 # EOF
diff --git a/admin/unidata/SpecialCasing.txt b/admin/unidata/SpecialCasing.txt
index 1c2e968a8c..08d04fa942 100644
--- a/admin/unidata/SpecialCasing.txt
+++ b/admin/unidata/SpecialCasing.txt
@@ -1,11 +1,11 @@
-# SpecialCasing-14.0.0.txt
-# Date: 2021-03-08, 19:35:55 GMT
-# © 2021 Unicode®, Inc.
+# SpecialCasing-15.0.0.txt
+# Date: 2022-02-02, 23:35:52 GMT
+# © 2022 Unicode®, Inc.
 # Unicode and the Unicode Logo are registered trademarks of Unicode, Inc. in 
the U.S. and other countries.
-# For terms of use, see http://www.unicode.org/terms_of_use.html
+# For terms of use, see https://www.unicode.org/terms_of_use.html
 #
 # Unicode Character Database
-#   For documentation, see http://www.unicode.org/reports/tr44/
+#   For documentation, see https://www.unicode.org/reports/tr44/
 #
 # Special Casing
 #
diff --git a/admin/unidata/UnicodeData.txt b/admin/unidata/UnicodeData.txt
index b5abef7ed4..ea963a7162 100644
--- a/admin/unidata/UnicodeData.txt
+++ b/admin/unidata/UnicodeData.txt
@@ -2975,6 +2975,7 @@
 0CEF;KANNADA DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
 0CF1;KANNADA SIGN JIHVAMULIYA;Lo;0;L;;;;;N;;;;;
 0CF2;KANNADA SIGN UPADHMANIYA;Lo;0;L;;;;;N;;;;;
+0CF3;KANNADA SIGN COMBINING ANUSVARA ABOVE RIGHT;Mc;0;L;;;;;N;;;;;
 0D00;MALAYALAM SIGN COMBINING ANUSVARA ABOVE;Mn;0;NSM;;;;;N;;;;;
 0D01;MALAYALAM SIGN CANDRABINDU;Mn;0;NSM;;;;;N;;;;;
 0D02;MALAYALAM SIGN ANUSVARA;Mc;0;L;;;;;N;;;;;
@@ -3339,6 +3340,7 @@
 0ECB;LAO TONE MAI CATAWA;Mn;122;NSM;;;;;N;;;;;
 0ECC;LAO CANCELLATION MARK;Mn;0;NSM;;;;;N;;;;;
 0ECD;LAO NIGGAHITA;Mn;0;NSM;;;;;N;;;;;
+0ECE;LAO YAMAKKAN;Mn;0;NSM;;;;;N;;;;;
 0ED0;LAO DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
 0ED1;LAO DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
 0ED2;LAO DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
@@ -19393,6 +19395,9 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
 10EAD;YEZIDI HYPHENATION MARK;Pd;0;R;;;;;N;;;;;
 10EB0;YEZIDI LETTER LAM WITH DOT ABOVE;Lo;0;R;;;;;N;;;;;
 10EB1;YEZIDI LETTER YOT WITH CIRCUMFLEX ABOVE;Lo;0;R;;;;;N;;;;;
+10EFD;ARABIC SMALL LOW WORD SAKTA;Mn;220;NSM;;;;;N;;;;;
+10EFE;ARABIC SMALL LOW WORD QASR;Mn;220;NSM;;;;;N;;;;;
+10EFF;ARABIC SMALL LOW WORD MADDA;Mn;220;NSM;;;;;N;;;;;
 10F00;OLD SOGDIAN LETTER ALEPH;Lo;0;R;;;;;N;;;;;
 10F01;OLD SOGDIAN LETTER FINAL ALEPH;Lo;0;R;;;;;N;;;;;
 10F02;OLD SOGDIAN LETTER BETH;Lo;0;R;;;;;N;;;;;
@@ -20058,6 +20063,9 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
 1123C;KHOJKI DOUBLE SECTION MARK;Po;0;L;;;;;N;;;;;
 1123D;KHOJKI ABBREVIATION SIGN;Po;0;L;;;;;N;;;;;
 1123E;KHOJKI SIGN SUKUN;Mn;0;NSM;;;;;N;;;;;
+1123F;KHOJKI LETTER QA;Lo;0;L;;;;;N;;;;;
+11240;KHOJKI LETTER SHORT I;Lo;0;L;;;;;N;;;;;
+11241;KHOJKI VOWEL SIGN VOCALIC R;Mn;0;NSM;;;;;N;;;;;
 11280;MULTANI LETTER A;Lo;0;L;;;;;N;;;;;
 11281;MULTANI LETTER I;Lo;0;L;;;;;N;;;;;
 11282;MULTANI LETTER U;Lo;0;L;;;;;N;;;;;
@@ -21256,6 +21264,16 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
 11AF6;PAU CIN HAU LOW-FALLING TONE LONG FINAL;Lo;0;L;;;;;N;;;;;
 11AF7;PAU CIN HAU LOW-FALLING TONE FINAL;Lo;0;L;;;;;N;;;;;
 11AF8;PAU CIN HAU GLOTTAL STOP FINAL;Lo;0;L;;;;;N;;;;;
+11B00;DEVANAGARI HEAD MARK;Po;0;L;;;;;N;;;;;
+11B01;DEVANAGARI HEAD MARK WITH HEADSTROKE;Po;0;L;;;;;N;;;;;
+11B02;DEVANAGARI SIGN BHALE;Po;0;L;;;;;N;;;;;
+11B03;DEVANAGARI SIGN BHALE WITH HOOK;Po;0;L;;;;;N;;;;;
+11B04;DEVANAGARI SIGN EXTENDED BHALE;Po;0;L;;;;;N;;;;;
+11B05;DEVANAGARI SIGN EXTENDED BHALE WITH HOOK;Po;0;L;;;;;N;;;;;
+11B06;DEVANAGARI SIGN WESTERN FIVE-LIKE BHALE;Po;0;L;;;;;N;;;;;
+11B07;DEVANAGARI SIGN WESTERN NINE-LIKE BHALE;Po;0;L;;;;;N;;;;;
+11B08;DEVANAGARI SIGN REVERSED NINE-LIKE BHALE;Po;0;L;;;;;N;;;;;
+11B09;DEVANAGARI SIGN MINDU;Po;0;L;;;;;N;;;;;
 11C00;BHAIKSUKI LETTER A;Lo;0;L;;;;;N;;;;;
 11C01;BHAIKSUKI LETTER AA;Lo;0;L;;;;;N;;;;;
 11C02;BHAIKSUKI LETTER I;Lo;0;L;;;;;N;;;;;
@@ -21584,6 +21602,92 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
 11EF6;MAKASAR VOWEL SIGN O;Mc;0;L;;;;;N;;;;;
 11EF7;MAKASAR PASSIMBANG;Po;0;L;;;;;N;;;;;
 11EF8;MAKASAR END OF SECTION;Po;0;L;;;;;N;;;;;
+11F00;KAWI SIGN CANDRABINDU;Mn;0;NSM;;;;;N;;;;;
+11F01;KAWI SIGN ANUSVARA;Mn;0;NSM;;;;;N;;;;;
+11F02;KAWI SIGN REPHA;Lo;0;L;;;;;N;;;;;
+11F03;KAWI SIGN VISARGA;Mc;0;L;;;;;N;;;;;
+11F04;KAWI LETTER A;Lo;0;L;;;;;N;;;;;
+11F05;KAWI LETTER AA;Lo;0;L;;;;;N;;;;;
+11F06;KAWI LETTER I;Lo;0;L;;;;;N;;;;;
+11F07;KAWI LETTER II;Lo;0;L;;;;;N;;;;;
+11F08;KAWI LETTER U;Lo;0;L;;;;;N;;;;;
+11F09;KAWI LETTER UU;Lo;0;L;;;;;N;;;;;
+11F0A;KAWI LETTER VOCALIC R;Lo;0;L;;;;;N;;;;;
+11F0B;KAWI LETTER VOCALIC RR;Lo;0;L;;;;;N;;;;;
+11F0C;KAWI LETTER VOCALIC L;Lo;0;L;;;;;N;;;;;
+11F0D;KAWI LETTER VOCALIC LL;Lo;0;L;;;;;N;;;;;
+11F0E;KAWI LETTER E;Lo;0;L;;;;;N;;;;;
+11F0F;KAWI LETTER AI;Lo;0;L;;;;;N;;;;;
+11F10;KAWI LETTER O;Lo;0;L;;;;;N;;;;;
+11F12;KAWI LETTER KA;Lo;0;L;;;;;N;;;;;
+11F13;KAWI LETTER KHA;Lo;0;L;;;;;N;;;;;
+11F14;KAWI LETTER GA;Lo;0;L;;;;;N;;;;;
+11F15;KAWI LETTER GHA;Lo;0;L;;;;;N;;;;;
+11F16;KAWI LETTER NGA;Lo;0;L;;;;;N;;;;;
+11F17;KAWI LETTER CA;Lo;0;L;;;;;N;;;;;
+11F18;KAWI LETTER CHA;Lo;0;L;;;;;N;;;;;
+11F19;KAWI LETTER JA;Lo;0;L;;;;;N;;;;;
+11F1A;KAWI LETTER JHA;Lo;0;L;;;;;N;;;;;
+11F1B;KAWI LETTER NYA;Lo;0;L;;;;;N;;;;;
+11F1C;KAWI LETTER TTA;Lo;0;L;;;;;N;;;;;
+11F1D;KAWI LETTER TTHA;Lo;0;L;;;;;N;;;;;
+11F1E;KAWI LETTER DDA;Lo;0;L;;;;;N;;;;;
+11F1F;KAWI LETTER DDHA;Lo;0;L;;;;;N;;;;;
+11F20;KAWI LETTER NNA;Lo;0;L;;;;;N;;;;;
+11F21;KAWI LETTER TA;Lo;0;L;;;;;N;;;;;
+11F22;KAWI LETTER THA;Lo;0;L;;;;;N;;;;;
+11F23;KAWI LETTER DA;Lo;0;L;;;;;N;;;;;
+11F24;KAWI LETTER DHA;Lo;0;L;;;;;N;;;;;
+11F25;KAWI LETTER NA;Lo;0;L;;;;;N;;;;;
+11F26;KAWI LETTER PA;Lo;0;L;;;;;N;;;;;
+11F27;KAWI LETTER PHA;Lo;0;L;;;;;N;;;;;
+11F28;KAWI LETTER BA;Lo;0;L;;;;;N;;;;;
+11F29;KAWI LETTER BHA;Lo;0;L;;;;;N;;;;;
+11F2A;KAWI LETTER MA;Lo;0;L;;;;;N;;;;;
+11F2B;KAWI LETTER YA;Lo;0;L;;;;;N;;;;;
+11F2C;KAWI LETTER RA;Lo;0;L;;;;;N;;;;;
+11F2D;KAWI LETTER LA;Lo;0;L;;;;;N;;;;;
+11F2E;KAWI LETTER WA;Lo;0;L;;;;;N;;;;;
+11F2F;KAWI LETTER SHA;Lo;0;L;;;;;N;;;;;
+11F30;KAWI LETTER SSA;Lo;0;L;;;;;N;;;;;
+11F31;KAWI LETTER SA;Lo;0;L;;;;;N;;;;;
+11F32;KAWI LETTER HA;Lo;0;L;;;;;N;;;;;
+11F33;KAWI LETTER JNYA;Lo;0;L;;;;;N;;;;;
+11F34;KAWI VOWEL SIGN AA;Mc;0;L;;;;;N;;;;;
+11F35;KAWI VOWEL SIGN ALTERNATE AA;Mc;0;L;;;;;N;;;;;
+11F36;KAWI VOWEL SIGN I;Mn;0;NSM;;;;;N;;;;;
+11F37;KAWI VOWEL SIGN II;Mn;0;NSM;;;;;N;;;;;
+11F38;KAWI VOWEL SIGN U;Mn;0;NSM;;;;;N;;;;;
+11F39;KAWI VOWEL SIGN UU;Mn;0;NSM;;;;;N;;;;;
+11F3A;KAWI VOWEL SIGN VOCALIC R;Mn;0;NSM;;;;;N;;;;;
+11F3E;KAWI VOWEL SIGN E;Mc;0;L;;;;;N;;;;;
+11F3F;KAWI VOWEL SIGN AI;Mc;0;L;;;;;N;;;;;
+11F40;KAWI VOWEL SIGN EU;Mn;0;NSM;;;;;N;;;;;
+11F41;KAWI SIGN KILLER;Mc;9;L;;;;;N;;;;;
+11F42;KAWI CONJOINER;Mn;9;NSM;;;;;N;;;;;
+11F43;KAWI DANDA;Po;0;L;;;;;N;;;;;
+11F44;KAWI DOUBLE DANDA;Po;0;L;;;;;N;;;;;
+11F45;KAWI PUNCTUATION SECTION MARKER;Po;0;L;;;;;N;;;;;
+11F46;KAWI PUNCTUATION ALTERNATE SECTION MARKER;Po;0;L;;;;;N;;;;;
+11F47;KAWI PUNCTUATION FLOWER;Po;0;L;;;;;N;;;;;
+11F48;KAWI PUNCTUATION SPACE FILLER;Po;0;L;;;;;N;;;;;
+11F49;KAWI PUNCTUATION DOT;Po;0;L;;;;;N;;;;;
+11F4A;KAWI PUNCTUATION DOUBLE DOT;Po;0;L;;;;;N;;;;;
+11F4B;KAWI PUNCTUATION TRIPLE DOT;Po;0;L;;;;;N;;;;;
+11F4C;KAWI PUNCTUATION CIRCLE;Po;0;L;;;;;N;;;;;
+11F4D;KAWI PUNCTUATION FILLED CIRCLE;Po;0;L;;;;;N;;;;;
+11F4E;KAWI PUNCTUATION SPIRAL;Po;0;L;;;;;N;;;;;
+11F4F;KAWI PUNCTUATION CLOSING SPIRAL;Po;0;L;;;;;N;;;;;
+11F50;KAWI DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
+11F51;KAWI DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
+11F52;KAWI DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
+11F53;KAWI DIGIT THREE;Nd;0;L;;3;3;3;N;;;;;
+11F54;KAWI DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;;
+11F55;KAWI DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;;
+11F56;KAWI DIGIT SIX;Nd;0;L;;6;6;6;N;;;;;
+11F57;KAWI DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
+11F58;KAWI DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
+11F59;KAWI DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
 11FB0;LISU LETTER YHA;Lo;0;L;;;;;N;;;;;
 11FC0;TAMIL FRACTION ONE THREE-HUNDRED-AND-TWENTIETH;No;0;L;;;;1/320;N;;;;;
 11FC1;TAMIL FRACTION ONE ONE-HUNDRED-AND-SIXTIETH;No;0;L;;;;1/160;N;;;;;
@@ -24040,6 +24144,7 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
 1342C;EGYPTIAN HIEROGLYPH AA030;Lo;0;L;;;;;N;;;;;
 1342D;EGYPTIAN HIEROGLYPH AA031;Lo;0;L;;;;;N;;;;;
 1342E;EGYPTIAN HIEROGLYPH AA032;Lo;0;L;;;;;N;;;;;
+1342F;EGYPTIAN HIEROGLYPH V011D;Lo;0;L;;;;;N;;;;;
 13430;EGYPTIAN HIEROGLYPH VERTICAL JOINER;Cf;0;L;;;;;N;;;;;
 13431;EGYPTIAN HIEROGLYPH HORIZONTAL JOINER;Cf;0;L;;;;;N;;;;;
 13432;EGYPTIAN HIEROGLYPH INSERT AT TOP START;Cf;0;L;;;;;N;;;;;
@@ -24049,6 +24154,35 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
 13436;EGYPTIAN HIEROGLYPH OVERLAY MIDDLE;Cf;0;L;;;;;N;;;;;
 13437;EGYPTIAN HIEROGLYPH BEGIN SEGMENT;Cf;0;L;;;;;N;;;;;
 13438;EGYPTIAN HIEROGLYPH END SEGMENT;Cf;0;L;;;;;N;;;;;
+13439;EGYPTIAN HIEROGLYPH INSERT AT MIDDLE;Cf;0;L;;;;;N;;;;;
+1343A;EGYPTIAN HIEROGLYPH INSERT AT TOP;Cf;0;L;;;;;N;;;;;
+1343B;EGYPTIAN HIEROGLYPH INSERT AT BOTTOM;Cf;0;L;;;;;N;;;;;
+1343C;EGYPTIAN HIEROGLYPH BEGIN ENCLOSURE;Cf;0;L;;;;;N;;;;;
+1343D;EGYPTIAN HIEROGLYPH END ENCLOSURE;Cf;0;L;;;;;N;;;;;
+1343E;EGYPTIAN HIEROGLYPH BEGIN WALLED ENCLOSURE;Cf;0;L;;;;;N;;;;;
+1343F;EGYPTIAN HIEROGLYPH END WALLED ENCLOSURE;Cf;0;L;;;;;N;;;;;
+13440;EGYPTIAN HIEROGLYPH MIRROR HORIZONTALLY;Mn;0;NSM;;;;;N;;;;;
+13441;EGYPTIAN HIEROGLYPH FULL BLANK;Lo;0;L;;;;;N;;;;;
+13442;EGYPTIAN HIEROGLYPH HALF BLANK;Lo;0;L;;;;;N;;;;;
+13443;EGYPTIAN HIEROGLYPH LOST SIGN;Lo;0;L;;;;;N;;;;;
+13444;EGYPTIAN HIEROGLYPH HALF LOST SIGN;Lo;0;L;;;;;N;;;;;
+13445;EGYPTIAN HIEROGLYPH TALL LOST SIGN;Lo;0;L;;;;;N;;;;;
+13446;EGYPTIAN HIEROGLYPH WIDE LOST SIGN;Lo;0;L;;;;;N;;;;;
+13447;EGYPTIAN HIEROGLYPH MODIFIER DAMAGED AT TOP START;Mn;0;NSM;;;;;N;;;;;
+13448;EGYPTIAN HIEROGLYPH MODIFIER DAMAGED AT BOTTOM START;Mn;0;NSM;;;;;N;;;;;
+13449;EGYPTIAN HIEROGLYPH MODIFIER DAMAGED AT START;Mn;0;NSM;;;;;N;;;;;
+1344A;EGYPTIAN HIEROGLYPH MODIFIER DAMAGED AT TOP END;Mn;0;NSM;;;;;N;;;;;
+1344B;EGYPTIAN HIEROGLYPH MODIFIER DAMAGED AT TOP;Mn;0;NSM;;;;;N;;;;;
+1344C;EGYPTIAN HIEROGLYPH MODIFIER DAMAGED AT BOTTOM START AND TOP 
END;Mn;0;NSM;;;;;N;;;;;
+1344D;EGYPTIAN HIEROGLYPH MODIFIER DAMAGED AT START AND TOP;Mn;0;NSM;;;;;N;;;;;
+1344E;EGYPTIAN HIEROGLYPH MODIFIER DAMAGED AT BOTTOM END;Mn;0;NSM;;;;;N;;;;;
+1344F;EGYPTIAN HIEROGLYPH MODIFIER DAMAGED AT TOP START AND BOTTOM 
END;Mn;0;NSM;;;;;N;;;;;
+13450;EGYPTIAN HIEROGLYPH MODIFIER DAMAGED AT BOTTOM;Mn;0;NSM;;;;;N;;;;;
+13451;EGYPTIAN HIEROGLYPH MODIFIER DAMAGED AT START AND 
BOTTOM;Mn;0;NSM;;;;;N;;;;;
+13452;EGYPTIAN HIEROGLYPH MODIFIER DAMAGED AT END;Mn;0;NSM;;;;;N;;;;;
+13453;EGYPTIAN HIEROGLYPH MODIFIER DAMAGED AT TOP AND END;Mn;0;NSM;;;;;N;;;;;
+13454;EGYPTIAN HIEROGLYPH MODIFIER DAMAGED AT BOTTOM AND 
END;Mn;0;NSM;;;;;N;;;;;
+13455;EGYPTIAN HIEROGLYPH MODIFIER DAMAGED;Mn;0;NSM;;;;;N;;;;;
 14400;ANATOLIAN HIEROGLYPH A001;Lo;0;L;;;;;N;;;;;
 14401;ANATOLIAN HIEROGLYPH A002;Lo;0;L;;;;;N;;;;;
 14402;ANATOLIAN HIEROGLYPH A003;Lo;0;L;;;;;N;;;;;
@@ -27289,9 +27423,11 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
 1B120;KATAKANA LETTER ARCHAIC YI;Lo;0;L;;;;;N;;;;;
 1B121;KATAKANA LETTER ARCHAIC YE;Lo;0;L;;;;;N;;;;;
 1B122;KATAKANA LETTER ARCHAIC WU;Lo;0;L;;;;;N;;;;;
+1B132;HIRAGANA LETTER SMALL KO;Lo;0;L;;;;;N;;;;;
 1B150;HIRAGANA LETTER SMALL WI;Lo;0;L;;;;;N;;;;;
 1B151;HIRAGANA LETTER SMALL WE;Lo;0;L;;;;;N;;;;;
 1B152;HIRAGANA LETTER SMALL WO;Lo;0;L;;;;;N;;;;;
+1B155;KATAKANA LETTER SMALL KO;Lo;0;L;;;;;N;;;;;
 1B164;KATAKANA LETTER SMALL WI;Lo;0;L;;;;;N;;;;;
 1B165;KATAKANA LETTER SMALL WE;Lo;0;L;;;;;N;;;;;
 1B166;KATAKANA LETTER SMALL WO;Lo;0;L;;;;;N;;;;;
@@ -28573,6 +28709,26 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
 1D243;COMBINING GREEK MUSICAL TETRASEME;Mn;230;NSM;;;;;N;;;;;
 1D244;COMBINING GREEK MUSICAL PENTASEME;Mn;230;NSM;;;;;N;;;;;
 1D245;GREEK MUSICAL LEIMMA;So;0;ON;;;;;N;;;;;
+1D2C0;KAKTOVIK NUMERAL ZERO;No;0;L;;;;0;N;;;;;
+1D2C1;KAKTOVIK NUMERAL ONE;No;0;L;;;;1;N;;;;;
+1D2C2;KAKTOVIK NUMERAL TWO;No;0;L;;;;2;N;;;;;
+1D2C3;KAKTOVIK NUMERAL THREE;No;0;L;;;;3;N;;;;;
+1D2C4;KAKTOVIK NUMERAL FOUR;No;0;L;;;;4;N;;;;;
+1D2C5;KAKTOVIK NUMERAL FIVE;No;0;L;;;;5;N;;;;;
+1D2C6;KAKTOVIK NUMERAL SIX;No;0;L;;;;6;N;;;;;
+1D2C7;KAKTOVIK NUMERAL SEVEN;No;0;L;;;;7;N;;;;;
+1D2C8;KAKTOVIK NUMERAL EIGHT;No;0;L;;;;8;N;;;;;
+1D2C9;KAKTOVIK NUMERAL NINE;No;0;L;;;;9;N;;;;;
+1D2CA;KAKTOVIK NUMERAL TEN;No;0;L;;;;10;N;;;;;
+1D2CB;KAKTOVIK NUMERAL ELEVEN;No;0;L;;;;11;N;;;;;
+1D2CC;KAKTOVIK NUMERAL TWELVE;No;0;L;;;;12;N;;;;;
+1D2CD;KAKTOVIK NUMERAL THIRTEEN;No;0;L;;;;13;N;;;;;
+1D2CE;KAKTOVIK NUMERAL FOURTEEN;No;0;L;;;;14;N;;;;;
+1D2CF;KAKTOVIK NUMERAL FIFTEEN;No;0;L;;;;15;N;;;;;
+1D2D0;KAKTOVIK NUMERAL SIXTEEN;No;0;L;;;;16;N;;;;;
+1D2D1;KAKTOVIK NUMERAL SEVENTEEN;No;0;L;;;;17;N;;;;;
+1D2D2;KAKTOVIK NUMERAL EIGHTEEN;No;0;L;;;;18;N;;;;;
+1D2D3;KAKTOVIK NUMERAL NINETEEN;No;0;L;;;;19;N;;;;;
 1D2E0;MAYAN NUMERAL ZERO;No;0;L;;;;0;N;;;;;
 1D2E1;MAYAN NUMERAL ONE;No;0;L;;;;1;N;;;;;
 1D2E2;MAYAN NUMERAL TWO;No;0;L;;;;2;N;;;;;
@@ -30404,6 +30560,12 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
 1DF1C;LATIN SMALL LETTER TESH DIGRAPH WITH RETROFLEX HOOK;Ll;0;L;;;;;N;;;;;
 1DF1D;LATIN SMALL LETTER C WITH RETROFLEX HOOK;Ll;0;L;;;;;N;;;;;
 1DF1E;LATIN SMALL LETTER S WITH CURL;Ll;0;L;;;;;N;;;;;
+1DF25;LATIN SMALL LETTER D WITH MID-HEIGHT LEFT HOOK;Ll;0;L;;;;;N;;;;;
+1DF26;LATIN SMALL LETTER L WITH MID-HEIGHT LEFT HOOK;Ll;0;L;;;;;N;;;;;
+1DF27;LATIN SMALL LETTER N WITH MID-HEIGHT LEFT HOOK;Ll;0;L;;;;;N;;;;;
+1DF28;LATIN SMALL LETTER R WITH MID-HEIGHT LEFT HOOK;Ll;0;L;;;;;N;;;;;
+1DF29;LATIN SMALL LETTER S WITH MID-HEIGHT LEFT HOOK;Ll;0;L;;;;;N;;;;;
+1DF2A;LATIN SMALL LETTER T WITH MID-HEIGHT LEFT HOOK;Ll;0;L;;;;;N;;;;;
 1E000;COMBINING GLAGOLITIC LETTER AZU;Mn;230;NSM;;;;;N;;;;;
 1E001;COMBINING GLAGOLITIC LETTER BUKY;Mn;230;NSM;;;;;N;;;;;
 1E002;COMBINING GLAGOLITIC LETTER VEDE;Mn;230;NSM;;;;;N;;;;;
@@ -30442,6 +30604,69 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
 1E028;COMBINING GLAGOLITIC LETTER BIG YUS;Mn;230;NSM;;;;;N;;;;;
 1E029;COMBINING GLAGOLITIC LETTER IOTATED BIG YUS;Mn;230;NSM;;;;;N;;;;;
 1E02A;COMBINING GLAGOLITIC LETTER FITA;Mn;230;NSM;;;;;N;;;;;
+1E030;MODIFIER LETTER CYRILLIC SMALL A;Lm;0;L;<super> 0430;;;;N;;;;;
+1E031;MODIFIER LETTER CYRILLIC SMALL BE;Lm;0;L;<super> 0431;;;;N;;;;;
+1E032;MODIFIER LETTER CYRILLIC SMALL VE;Lm;0;L;<super> 0432;;;;N;;;;;
+1E033;MODIFIER LETTER CYRILLIC SMALL GHE;Lm;0;L;<super> 0433;;;;N;;;;;
+1E034;MODIFIER LETTER CYRILLIC SMALL DE;Lm;0;L;<super> 0434;;;;N;;;;;
+1E035;MODIFIER LETTER CYRILLIC SMALL IE;Lm;0;L;<super> 0435;;;;N;;;;;
+1E036;MODIFIER LETTER CYRILLIC SMALL ZHE;Lm;0;L;<super> 0436;;;;N;;;;;
+1E037;MODIFIER LETTER CYRILLIC SMALL ZE;Lm;0;L;<super> 0437;;;;N;;;;;
+1E038;MODIFIER LETTER CYRILLIC SMALL I;Lm;0;L;<super> 0438;;;;N;;;;;
+1E039;MODIFIER LETTER CYRILLIC SMALL KA;Lm;0;L;<super> 043A;;;;N;;;;;
+1E03A;MODIFIER LETTER CYRILLIC SMALL EL;Lm;0;L;<super> 043B;;;;N;;;;;
+1E03B;MODIFIER LETTER CYRILLIC SMALL EM;Lm;0;L;<super> 043C;;;;N;;;;;
+1E03C;MODIFIER LETTER CYRILLIC SMALL O;Lm;0;L;<super> 043E;;;;N;;;;;
+1E03D;MODIFIER LETTER CYRILLIC SMALL PE;Lm;0;L;<super> 043F;;;;N;;;;;
+1E03E;MODIFIER LETTER CYRILLIC SMALL ER;Lm;0;L;<super> 0440;;;;N;;;;;
+1E03F;MODIFIER LETTER CYRILLIC SMALL ES;Lm;0;L;<super> 0441;;;;N;;;;;
+1E040;MODIFIER LETTER CYRILLIC SMALL TE;Lm;0;L;<super> 0442;;;;N;;;;;
+1E041;MODIFIER LETTER CYRILLIC SMALL U;Lm;0;L;<super> 0443;;;;N;;;;;
+1E042;MODIFIER LETTER CYRILLIC SMALL EF;Lm;0;L;<super> 0444;;;;N;;;;;
+1E043;MODIFIER LETTER CYRILLIC SMALL HA;Lm;0;L;<super> 0445;;;;N;;;;;
+1E044;MODIFIER LETTER CYRILLIC SMALL TSE;Lm;0;L;<super> 0446;;;;N;;;;;
+1E045;MODIFIER LETTER CYRILLIC SMALL CHE;Lm;0;L;<super> 0447;;;;N;;;;;
+1E046;MODIFIER LETTER CYRILLIC SMALL SHA;Lm;0;L;<super> 0448;;;;N;;;;;
+1E047;MODIFIER LETTER CYRILLIC SMALL YERU;Lm;0;L;<super> 044B;;;;N;;;;;
+1E048;MODIFIER LETTER CYRILLIC SMALL E;Lm;0;L;<super> 044D;;;;N;;;;;
+1E049;MODIFIER LETTER CYRILLIC SMALL YU;Lm;0;L;<super> 044E;;;;N;;;;;
+1E04A;MODIFIER LETTER CYRILLIC SMALL DZZE;Lm;0;L;<super> A689;;;;N;;;;;
+1E04B;MODIFIER LETTER CYRILLIC SMALL SCHWA;Lm;0;L;<super> 04D9;;;;N;;;;;
+1E04C;MODIFIER LETTER CYRILLIC SMALL BYELORUSSIAN-UKRAINIAN I;Lm;0;L;<super> 
0456;;;;N;;;;;
+1E04D;MODIFIER LETTER CYRILLIC SMALL JE;Lm;0;L;<super> 0458;;;;N;;;;;
+1E04E;MODIFIER LETTER CYRILLIC SMALL BARRED O;Lm;0;L;<super> 04E9;;;;N;;;;;
+1E04F;MODIFIER LETTER CYRILLIC SMALL STRAIGHT U;Lm;0;L;<super> 04AF;;;;N;;;;;
+1E050;MODIFIER LETTER CYRILLIC SMALL PALOCHKA;Lm;0;L;<super> 04CF;;;;N;;;;;
+1E051;CYRILLIC SUBSCRIPT SMALL LETTER A;Lm;0;L;<sub> 0430;;;;N;;;;;
+1E052;CYRILLIC SUBSCRIPT SMALL LETTER BE;Lm;0;L;<sub> 0431;;;;N;;;;;
+1E053;CYRILLIC SUBSCRIPT SMALL LETTER VE;Lm;0;L;<sub> 0432;;;;N;;;;;
+1E054;CYRILLIC SUBSCRIPT SMALL LETTER GHE;Lm;0;L;<sub> 0433;;;;N;;;;;
+1E055;CYRILLIC SUBSCRIPT SMALL LETTER DE;Lm;0;L;<sub> 0434;;;;N;;;;;
+1E056;CYRILLIC SUBSCRIPT SMALL LETTER IE;Lm;0;L;<sub> 0435;;;;N;;;;;
+1E057;CYRILLIC SUBSCRIPT SMALL LETTER ZHE;Lm;0;L;<sub> 0436;;;;N;;;;;
+1E058;CYRILLIC SUBSCRIPT SMALL LETTER ZE;Lm;0;L;<sub> 0437;;;;N;;;;;
+1E059;CYRILLIC SUBSCRIPT SMALL LETTER I;Lm;0;L;<sub> 0438;;;;N;;;;;
+1E05A;CYRILLIC SUBSCRIPT SMALL LETTER KA;Lm;0;L;<sub> 043A;;;;N;;;;;
+1E05B;CYRILLIC SUBSCRIPT SMALL LETTER EL;Lm;0;L;<sub> 043B;;;;N;;;;;
+1E05C;CYRILLIC SUBSCRIPT SMALL LETTER O;Lm;0;L;<sub> 043E;;;;N;;;;;
+1E05D;CYRILLIC SUBSCRIPT SMALL LETTER PE;Lm;0;L;<sub> 043F;;;;N;;;;;
+1E05E;CYRILLIC SUBSCRIPT SMALL LETTER ES;Lm;0;L;<sub> 0441;;;;N;;;;;
+1E05F;CYRILLIC SUBSCRIPT SMALL LETTER U;Lm;0;L;<sub> 0443;;;;N;;;;;
+1E060;CYRILLIC SUBSCRIPT SMALL LETTER EF;Lm;0;L;<sub> 0444;;;;N;;;;;
+1E061;CYRILLIC SUBSCRIPT SMALL LETTER HA;Lm;0;L;<sub> 0445;;;;N;;;;;
+1E062;CYRILLIC SUBSCRIPT SMALL LETTER TSE;Lm;0;L;<sub> 0446;;;;N;;;;;
+1E063;CYRILLIC SUBSCRIPT SMALL LETTER CHE;Lm;0;L;<sub> 0447;;;;N;;;;;
+1E064;CYRILLIC SUBSCRIPT SMALL LETTER SHA;Lm;0;L;<sub> 0448;;;;N;;;;;
+1E065;CYRILLIC SUBSCRIPT SMALL LETTER HARD SIGN;Lm;0;L;<sub> 044A;;;;N;;;;;
+1E066;CYRILLIC SUBSCRIPT SMALL LETTER YERU;Lm;0;L;<sub> 044B;;;;N;;;;;
+1E067;CYRILLIC SUBSCRIPT SMALL LETTER GHE WITH UPTURN;Lm;0;L;<sub> 
0491;;;;N;;;;;
+1E068;CYRILLIC SUBSCRIPT SMALL LETTER BYELORUSSIAN-UKRAINIAN I;Lm;0;L;<sub> 
0456;;;;N;;;;;
+1E069;CYRILLIC SUBSCRIPT SMALL LETTER DZE;Lm;0;L;<sub> 0455;;;;N;;;;;
+1E06A;CYRILLIC SUBSCRIPT SMALL LETTER DZHE;Lm;0;L;<sub> 045F;;;;N;;;;;
+1E06B;MODIFIER LETTER CYRILLIC SMALL ES WITH DESCENDER;Lm;0;L;<super> 
04AB;;;;N;;;;;
+1E06C;MODIFIER LETTER CYRILLIC SMALL YERU WITH BACK YER;Lm;0;L;<super> 
A651;;;;N;;;;;
+1E06D;MODIFIER LETTER CYRILLIC SMALL STRAIGHT U WITH STROKE;Lm;0;L;<super> 
04B1;;;;N;;;;;
+1E08F;COMBINING CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN 
I;Mn;230;NSM;;;;;N;;;;;
 1E100;NYIAKENG PUACHUE HMONG LETTER MA;Lo;0;L;;;;;N;;;;;
 1E101;NYIAKENG PUACHUE HMONG LETTER TSA;Lo;0;L;;;;;N;;;;;
 1E102;NYIAKENG PUACHUE HMONG LETTER NTA;Lo;0;L;;;;;N;;;;;
@@ -30603,6 +30828,48 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
 1E2F8;WANCHO DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
 1E2F9;WANCHO DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
 1E2FF;WANCHO NGUN SIGN;Sc;0;ET;;;;;N;;;;;
+1E4D0;NAG MUNDARI LETTER O;Lo;0;L;;;;;N;;;;;
+1E4D1;NAG MUNDARI LETTER OP;Lo;0;L;;;;;N;;;;;
+1E4D2;NAG MUNDARI LETTER OL;Lo;0;L;;;;;N;;;;;
+1E4D3;NAG MUNDARI LETTER OY;Lo;0;L;;;;;N;;;;;
+1E4D4;NAG MUNDARI LETTER ONG;Lo;0;L;;;;;N;;;;;
+1E4D5;NAG MUNDARI LETTER A;Lo;0;L;;;;;N;;;;;
+1E4D6;NAG MUNDARI LETTER AJ;Lo;0;L;;;;;N;;;;;
+1E4D7;NAG MUNDARI LETTER AB;Lo;0;L;;;;;N;;;;;
+1E4D8;NAG MUNDARI LETTER ANY;Lo;0;L;;;;;N;;;;;
+1E4D9;NAG MUNDARI LETTER AH;Lo;0;L;;;;;N;;;;;
+1E4DA;NAG MUNDARI LETTER I;Lo;0;L;;;;;N;;;;;
+1E4DB;NAG MUNDARI LETTER IS;Lo;0;L;;;;;N;;;;;
+1E4DC;NAG MUNDARI LETTER IDD;Lo;0;L;;;;;N;;;;;
+1E4DD;NAG MUNDARI LETTER IT;Lo;0;L;;;;;N;;;;;
+1E4DE;NAG MUNDARI LETTER IH;Lo;0;L;;;;;N;;;;;
+1E4DF;NAG MUNDARI LETTER U;Lo;0;L;;;;;N;;;;;
+1E4E0;NAG MUNDARI LETTER UC;Lo;0;L;;;;;N;;;;;
+1E4E1;NAG MUNDARI LETTER UD;Lo;0;L;;;;;N;;;;;
+1E4E2;NAG MUNDARI LETTER UK;Lo;0;L;;;;;N;;;;;
+1E4E3;NAG MUNDARI LETTER UR;Lo;0;L;;;;;N;;;;;
+1E4E4;NAG MUNDARI LETTER E;Lo;0;L;;;;;N;;;;;
+1E4E5;NAG MUNDARI LETTER ENN;Lo;0;L;;;;;N;;;;;
+1E4E6;NAG MUNDARI LETTER EG;Lo;0;L;;;;;N;;;;;
+1E4E7;NAG MUNDARI LETTER EM;Lo;0;L;;;;;N;;;;;
+1E4E8;NAG MUNDARI LETTER EN;Lo;0;L;;;;;N;;;;;
+1E4E9;NAG MUNDARI LETTER ETT;Lo;0;L;;;;;N;;;;;
+1E4EA;NAG MUNDARI LETTER ELL;Lo;0;L;;;;;N;;;;;
+1E4EB;NAG MUNDARI SIGN OJOD;Lm;0;L;;;;;N;;;;;
+1E4EC;NAG MUNDARI SIGN MUHOR;Mn;232;NSM;;;;;N;;;;;
+1E4ED;NAG MUNDARI SIGN TOYOR;Mn;232;NSM;;;;;N;;;;;
+1E4EE;NAG MUNDARI SIGN IKIR;Mn;220;NSM;;;;;N;;;;;
+1E4EF;NAG MUNDARI SIGN SUTUH;Mn;230;NSM;;;;;N;;;;;
+1E4F0;NAG MUNDARI DIGIT ZERO;Nd;0;L;;0;0;0;N;;;;;
+1E4F1;NAG MUNDARI DIGIT ONE;Nd;0;L;;1;1;1;N;;;;;
+1E4F2;NAG MUNDARI DIGIT TWO;Nd;0;L;;2;2;2;N;;;;;
+1E4F3;NAG MUNDARI DIGIT THREE;Nd;0;L;;3;3;3;N;;;;;
+1E4F4;NAG MUNDARI DIGIT FOUR;Nd;0;L;;4;4;4;N;;;;;
+1E4F5;NAG MUNDARI DIGIT FIVE;Nd;0;L;;5;5;5;N;;;;;
+1E4F6;NAG MUNDARI DIGIT SIX;Nd;0;L;;6;6;6;N;;;;;
+1E4F7;NAG MUNDARI DIGIT SEVEN;Nd;0;L;;7;7;7;N;;;;;
+1E4F8;NAG MUNDARI DIGIT EIGHT;Nd;0;L;;8;8;8;N;;;;;
+1E4F9;NAG MUNDARI DIGIT NINE;Nd;0;L;;9;9;9;N;;;;;
 1E7E0;ETHIOPIC SYLLABLE HHYA;Lo;0;L;;;;;N;;;;;
 1E7E1;ETHIOPIC SYLLABLE HHYU;Lo;0;L;;;;;N;;;;;
 1E7E2;ETHIOPIC SYLLABLE HHYI;Lo;0;L;;;;;N;;;;;
@@ -32678,6 +32945,7 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
 1F6D5;HINDU TEMPLE;So;0;ON;;;;;N;;;;;
 1F6D6;HUT;So;0;ON;;;;;N;;;;;
 1F6D7;ELEVATOR;So;0;ON;;;;;N;;;;;
+1F6DC;WIRELESS;So;0;ON;;;;;N;;;;;
 1F6DD;PLAYGROUND SLIDE;So;0;ON;;;;;N;;;;;
 1F6DE;WHEEL;So;0;ON;;;;;N;;;;;
 1F6DF;RING BUOY;So;0;ON;;;;;N;;;;;
@@ -32823,6 +33091,14 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
 1F771;ALCHEMICAL SYMBOL FOR MONTH;So;0;ON;;;;;N;;;;;
 1F772;ALCHEMICAL SYMBOL FOR HALF DRAM;So;0;ON;;;;;N;;;;;
 1F773;ALCHEMICAL SYMBOL FOR HALF OUNCE;So;0;ON;;;;;N;;;;;
+1F774;LOT OF FORTUNE;So;0;ON;;;;;N;;;;;
+1F775;OCCULTATION;So;0;ON;;;;;N;;;;;
+1F776;LUNAR ECLIPSE;So;0;ON;;;;;N;;;;;
+1F77B;HAUMEA;So;0;ON;;;;;N;;;;;
+1F77C;MAKEMAKE;So;0;ON;;;;;N;;;;;
+1F77D;GONGGONG;So;0;ON;;;;;N;;;;;
+1F77E;QUAOAR;So;0;ON;;;;;N;;;;;
+1F77F;ORCUS;So;0;ON;;;;;N;;;;;
 1F780;BLACK LEFT-POINTING ISOSCELES RIGHT TRIANGLE;So;0;ON;;;;;N;;;;;
 1F781;BLACK UP-POINTING ISOSCELES RIGHT TRIANGLE;So;0;ON;;;;;N;;;;;
 1F782;BLACK RIGHT-POINTING ISOSCELES RIGHT TRIANGLE;So;0;ON;;;;;N;;;;;
@@ -32912,6 +33188,7 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
 1F7D6;NEGATIVE CIRCLED TRIANGLE;So;0;ON;;;;;N;;;;;
 1F7D7;CIRCLED SQUARE;So;0;ON;;;;;N;;;;;
 1F7D8;NEGATIVE CIRCLED SQUARE;So;0;ON;;;;;N;;;;;
+1F7D9;NINE POINTED WHITE STAR;So;0;ON;;;;;N;;;;;
 1F7E0;LARGE ORANGE CIRCLE;So;0;ON;;;;;N;;;;;
 1F7E1;LARGE YELLOW CIRCLE;So;0;ON;;;;;N;;;;;
 1F7E2;LARGE GREEN CIRCLE;So;0;ON;;;;;N;;;;;
@@ -33434,6 +33711,9 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
 1FA72;BRIEFS;So;0;ON;;;;;N;;;;;
 1FA73;SHORTS;So;0;ON;;;;;N;;;;;
 1FA74;THONG SANDAL;So;0;ON;;;;;N;;;;;
+1FA75;LIGHT BLUE HEART;So;0;ON;;;;;N;;;;;
+1FA76;GREY HEART;So;0;ON;;;;;N;;;;;
+1FA77;PINK HEART;So;0;ON;;;;;N;;;;;
 1FA78;DROP OF BLOOD;So;0;ON;;;;;N;;;;;
 1FA79;ADHESIVE BANDAGE;So;0;ON;;;;;N;;;;;
 1FA7A;STETHOSCOPE;So;0;ON;;;;;N;;;;;
@@ -33446,6 +33726,8 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
 1FA84;MAGIC WAND;So;0;ON;;;;;N;;;;;
 1FA85;PINATA;So;0;ON;;;;;N;;;;;
 1FA86;NESTING DOLLS;So;0;ON;;;;;N;;;;;
+1FA87;MARACAS;So;0;ON;;;;;N;;;;;
+1FA88;FLUTE;So;0;ON;;;;;N;;;;;
 1FA90;RINGED PLANET;So;0;ON;;;;;N;;;;;
 1FA91;CHAIR;So;0;ON;;;;;N;;;;;
 1FA92;RAZOR;So;0;ON;;;;;N;;;;;
@@ -33475,6 +33757,9 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
 1FAAA;IDENTIFICATION CARD;So;0;ON;;;;;N;;;;;
 1FAAB;LOW BATTERY;So;0;ON;;;;;N;;;;;
 1FAAC;HAMSA;So;0;ON;;;;;N;;;;;
+1FAAD;FOLDING HAND FAN;So;0;ON;;;;;N;;;;;
+1FAAE;HAIR PICK;So;0;ON;;;;;N;;;;;
+1FAAF;KHANDA;So;0;ON;;;;;N;;;;;
 1FAB0;FLY;So;0;ON;;;;;N;;;;;
 1FAB1;WORM;So;0;ON;;;;;N;;;;;
 1FAB2;BEETLE;So;0;ON;;;;;N;;;;;
@@ -33486,12 +33771,18 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
 1FAB8;CORAL;So;0;ON;;;;;N;;;;;
 1FAB9;EMPTY NEST;So;0;ON;;;;;N;;;;;
 1FABA;NEST WITH EGGS;So;0;ON;;;;;N;;;;;
+1FABB;HYACINTH;So;0;ON;;;;;N;;;;;
+1FABC;JELLYFISH;So;0;ON;;;;;N;;;;;
+1FABD;WING;So;0;ON;;;;;N;;;;;
+1FABF;GOOSE;So;0;ON;;;;;N;;;;;
 1FAC0;ANATOMICAL HEART;So;0;ON;;;;;N;;;;;
 1FAC1;LUNGS;So;0;ON;;;;;N;;;;;
 1FAC2;PEOPLE HUGGING;So;0;ON;;;;;N;;;;;
 1FAC3;PREGNANT MAN;So;0;ON;;;;;N;;;;;
 1FAC4;PREGNANT PERSON;So;0;ON;;;;;N;;;;;
 1FAC5;PERSON WITH CROWN;So;0;ON;;;;;N;;;;;
+1FACE;MOOSE;So;0;ON;;;;;N;;;;;
+1FACF;DONKEY;So;0;ON;;;;;N;;;;;
 1FAD0;BLUEBERRIES;So;0;ON;;;;;N;;;;;
 1FAD1;BELL PEPPER;So;0;ON;;;;;N;;;;;
 1FAD2;OLIVE;So;0;ON;;;;;N;;;;;
@@ -33502,6 +33793,8 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
 1FAD7;POURING LIQUID;So;0;ON;;;;;N;;;;;
 1FAD8;BEANS;So;0;ON;;;;;N;;;;;
 1FAD9;JAR;So;0;ON;;;;;N;;;;;
+1FADA;GINGER ROOT;So;0;ON;;;;;N;;;;;
+1FADB;PEA POD;So;0;ON;;;;;N;;;;;
 1FAE0;MELTING FACE;So;0;ON;;;;;N;;;;;
 1FAE1;SALUTING FACE;So;0;ON;;;;;N;;;;;
 1FAE2;FACE WITH OPEN EYES AND HAND OVER MOUTH;So;0;ON;;;;;N;;;;;
@@ -33510,6 +33803,7 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
 1FAE5;DOTTED LINE FACE;So;0;ON;;;;;N;;;;;
 1FAE6;BITING LIP;So;0;ON;;;;;N;;;;;
 1FAE7;BUBBLES;So;0;ON;;;;;N;;;;;
+1FAE8;SHAKING FACE;So;0;ON;;;;;N;;;;;
 1FAF0;HAND WITH INDEX FINGER AND THUMB CROSSED;So;0;ON;;;;;N;;;;;
 1FAF1;RIGHTWARDS HAND;So;0;ON;;;;;N;;;;;
 1FAF2;LEFTWARDS HAND;So;0;ON;;;;;N;;;;;
@@ -33517,6 +33811,8 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
 1FAF4;PALM UP HAND;So;0;ON;;;;;N;;;;;
 1FAF5;INDEX POINTING AT THE VIEWER;So;0;ON;;;;;N;;;;;
 1FAF6;HEART HANDS;So;0;ON;;;;;N;;;;;
+1FAF7;LEFTWARDS PUSHING HAND;So;0;ON;;;;;N;;;;;
+1FAF8;RIGHTWARDS PUSHING HAND;So;0;ON;;;;;N;;;;;
 1FB00;BLOCK SEXTANT-1;So;0;ON;;;;;N;;;;;
 1FB01;BLOCK SEXTANT-2;So;0;ON;;;;;N;;;;;
 1FB02;BLOCK SEXTANT-12;So;0;ON;;;;;N;;;;;
@@ -33732,7 +34028,7 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
 20000;<CJK Ideograph Extension B, First>;Lo;0;L;;;;;N;;;;;
 2A6DF;<CJK Ideograph Extension B, Last>;Lo;0;L;;;;;N;;;;;
 2A700;<CJK Ideograph Extension C, First>;Lo;0;L;;;;;N;;;;;
-2B738;<CJK Ideograph Extension C, Last>;Lo;0;L;;;;;N;;;;;
+2B739;<CJK Ideograph Extension C, Last>;Lo;0;L;;;;;N;;;;;
 2B740;<CJK Ideograph Extension D, First>;Lo;0;L;;;;;N;;;;;
 2B81D;<CJK Ideograph Extension D, Last>;Lo;0;L;;;;;N;;;;;
 2B820;<CJK Ideograph Extension E, First>;Lo;0;L;;;;;N;;;;;
@@ -34283,6 +34579,8 @@ FFFD;REPLACEMENT CHARACTER;So;0;ON;;;;;N;;;;;
 2FA1D;CJK COMPATIBILITY IDEOGRAPH-2FA1D;Lo;0;L;2A600;;;;N;;;;;
 30000;<CJK Ideograph Extension G, First>;Lo;0;L;;;;;N;;;;;
 3134A;<CJK Ideograph Extension G, Last>;Lo;0;L;;;;;N;;;;;
+31350;<CJK Ideograph Extension H, First>;Lo;0;L;;;;;N;;;;;
+323AF;<CJK Ideograph Extension H, Last>;Lo;0;L;;;;;N;;;;;
 E0001;LANGUAGE TAG;Cf;0;BN;;;;;N;;;;;
 E0020;TAG SPACE;Cf;0;BN;;;;;N;;;;;
 E0021;TAG EXCLAMATION MARK;Cf;0;BN;;;;;N;;;;;
diff --git a/admin/unidata/confusables.txt b/admin/unidata/confusables.txt
index c75f78ec3e..24b61d519a 100644
--- a/admin/unidata/confusables.txt
+++ b/admin/unidata/confusables.txt
@@ -1,13 +1,13 @@
-# confusables.txt
-# Date: 2021-05-29, 22:09:29 GMT
-# © 2021 Unicode®, Inc.
+# confusables.txt
+# Date: 2022-08-26, 16:49:08 GMT
+# © 2022 Unicode®, Inc.
 # Unicode and the Unicode Logo are registered trademarks of Unicode, Inc. in 
the U.S. and other countries.
-# For terms of use, see http://www.unicode.org/terms_of_use.html
+# For terms of use, see https://www.unicode.org/terms_of_use.html
 #
 # Unicode Security Mechanisms for UTS #39
-# Version: 14.0.0
+# Version: 15.0.0
 #
-# For documentation and usage, see http://www.unicode.org/reports/tr39
+# For documentation and usage, see https://www.unicode.org/reports/tr39
 #
 05AD ; 0596 ;  MA      # ( ֭ → ֖ ) HEBREW ACCENT DEHI → HEBREW ACCENT TIPEHA   
#
 
@@ -2761,11 +2761,11 @@ FE87 ;  006C 0655 ;     MA      # ( ‎ﺇ‎ → lٕ ) ARABIC 
LETTER ALEF WITH HAMZA BELO
 
 02AB ; 006C 007A ;     MA      # ( ʫ → lz ) LATIN SMALL LETTER LZ DIGRAPH → 
LATIN SMALL LETTER L, LATIN SMALL LETTER Z #
 
+0675 ; 006C 0674 ;     MA      # ( ‎ٵ‎ → ‎lٴ‎ ) ARABIC LETTER HIGH HAMZA ALEF 
→ LATIN SMALL LETTER L, ARABIC LETTER HIGH HAMZA # →‎اٴ‎→
 0623 ; 006C 0674 ;     MA      # ( ‎أ‎ → ‎lٴ‎ ) ARABIC LETTER ALEF WITH HAMZA 
ABOVE → LATIN SMALL LETTER L, ARABIC LETTER HIGH HAMZA   # →‎ٵ‎→→‎اٴ‎→
 FE84 ; 006C 0674 ;     MA      # ( ‎ﺄ‎ → ‎lٴ‎ ) ARABIC LETTER ALEF WITH HAMZA 
ABOVE FINAL FORM → LATIN SMALL LETTER L, ARABIC LETTER HIGH HAMZA        # 
→‎أ‎→→‎ٵ‎→→‎اٴ‎→
 FE83 ; 006C 0674 ;     MA      # ( ‎ﺃ‎ → ‎lٴ‎ ) ARABIC LETTER ALEF WITH HAMZA 
ABOVE ISOLATED FORM → LATIN SMALL LETTER L, ARABIC LETTER HIGH HAMZA     # 
→‎ٵ‎→→‎اٴ‎→
 0672 ; 006C 0674 ;     MA      # ( ‎ٲ‎ → ‎lٴ‎ ) ARABIC LETTER ALEF WITH WAVY 
HAMZA ABOVE → LATIN SMALL LETTER L, ARABIC LETTER HIGH HAMZA      # 
→‎أ‎→→‎ٵ‎→→‎اٴ‎→
-0675 ; 006C 0674 ;     MA      # ( ‎ٵ‎ → ‎lٴ‎ ) ARABIC LETTER HIGH HAMZA ALEF 
→ LATIN SMALL LETTER L, ARABIC LETTER HIGH HAMZA # →‎اٴ‎→
 
 FDF3 ; 006C 0643 0628 0631 ;   MA      # ( ‎ﷳ‎ → ‎lكبر‎ ) ARABIC LIGATURE 
AKBAR ISOLATED FORM → LATIN SMALL LETTER L, ARABIC LETTER KAF, ARABIC LETTER 
BEH, ARABIC LETTER REH  # →‎اكبر‎→
 
@@ -5351,10 +5351,10 @@ FBE2 ;  0648 0302 ;     MA      # ( ‎ﯢ‎ → ‎و̂‎ ) ARABIC 
LETTER KIRGHIZ YU IS
 FBDC ; 0648 0670 ;     MA      # ( ‎ﯜ‎ → ‎وٰ‎ ) ARABIC LETTER YU FINAL FORM → 
ARABIC LETTER WAW, ARABIC LETTER SUPERSCRIPT ALEF        # →‎ۈ‎→
 FBDB ; 0648 0670 ;     MA      # ( ‎ﯛ‎ → ‎وٰ‎ ) ARABIC LETTER YU ISOLATED FORM 
→ ARABIC LETTER WAW, ARABIC LETTER SUPERSCRIPT ALEF     # →‎ۈ‎→
 
+0676 ; 0648 0674 ;     MA      # ( ‎ٶ‎ → ‎وٴ‎ ) ARABIC LETTER HIGH HAMZA WAW → 
ARABIC LETTER WAW, ARABIC LETTER HIGH HAMZA     #
 0624 ; 0648 0674 ;     MA      # ( ‎ؤ‎ → ‎وٴ‎ ) ARABIC LETTER WAW WITH HAMZA 
ABOVE → ARABIC LETTER WAW, ARABIC LETTER HIGH HAMZA       # →‎ٶ‎→
 FE86 ; 0648 0674 ;     MA      # ( ‎ﺆ‎ → ‎وٴ‎ ) ARABIC LETTER WAW WITH HAMZA 
ABOVE FINAL FORM → ARABIC LETTER WAW, ARABIC LETTER HIGH HAMZA    # →‎ٶ‎→
 FE85 ; 0648 0674 ;     MA      # ( ‎ﺅ‎ → ‎وٴ‎ ) ARABIC LETTER WAW WITH HAMZA 
ABOVE ISOLATED FORM → ARABIC LETTER WAW, ARABIC LETTER HIGH HAMZA # →‎ٶ‎→
-0676 ; 0648 0674 ;     MA      # ( ‎ٶ‎ → ‎وٴ‎ ) ARABIC LETTER HIGH HAMZA WAW → 
ARABIC LETTER WAW, ARABIC LETTER HIGH HAMZA     #
 
 0677 ; 0648 0313 0674 ;        MA      # ( ‎ٷ‎ → ‎و̓ٴ‎ ) ARABIC LETTER U WITH 
HAMZA ABOVE → ARABIC LETTER WAW, COMBINING COMMA ABOVE, ARABIC LETTER HIGH 
HAMZA # →‎ۇٴ‎→
 FBDD ; 0648 0313 0674 ;        MA      # ( ‎ﯝ‎ → ‎و̓ٴ‎ ) ARABIC LETTER U WITH 
HAMZA ABOVE ISOLATED FORM → ARABIC LETTER WAW, COMBINING COMMA ABOVE, ARABIC 
LETTER HIGH HAMZA   # →‎ۇٴ‎→
@@ -5446,12 +5446,12 @@ FCF1 ;  0649 006F ;     MA      # ( ‎ﳱ‎ → ‎ىo‎ ) ARABIC 
LIGATURE YEH WITH HEH
 
 FCE6 ; 0649 06DB 006F ;        MA      # ( ‎ﳦ‎ → ‎ىۛo‎ ) ARABIC LIGATURE THEH 
WITH HEH MEDIAL FORM → ARABIC LETTER ALEF MAKSURA, ARABIC SMALL HIGH THREE 
DOTS, LATIN SMALL LETTER O    # →‎ثه‎→
 
+0678 ; 0649 0674 ;     MA      # ( ‎ٸ‎ → ‎ىٴ‎ ) ARABIC LETTER HIGH HAMZA YEH → 
ARABIC LETTER ALEF MAKSURA, ARABIC LETTER HIGH HAMZA    # →‎يٴ‎→
 0626 ; 0649 0674 ;     MA      # ( ‎ئ‎ → ‎ىٴ‎ ) ARABIC LETTER YEH WITH HAMZA 
ABOVE → ARABIC LETTER ALEF MAKSURA, ARABIC LETTER HIGH HAMZA      # →‎ٸ‎→→‎يٴ‎→
 FE8B ; 0649 0674 ;     MA      # ( ‎ﺋ‎ → ‎ىٴ‎ ) ARABIC LETTER YEH WITH HAMZA 
ABOVE INITIAL FORM → ARABIC LETTER ALEF MAKSURA, ARABIC LETTER HIGH HAMZA # 
→‎ئ‎→→‎ٸ‎→→‎يٴ‎→
 FE8C ; 0649 0674 ;     MA      # ( ‎ﺌ‎ → ‎ىٴ‎ ) ARABIC LETTER YEH WITH HAMZA 
ABOVE MEDIAL FORM → ARABIC LETTER ALEF MAKSURA, ARABIC LETTER HIGH HAMZA  # 
→‎ئ‎→→‎ٸ‎→→‎يٴ‎→
 FE8A ; 0649 0674 ;     MA      # ( ‎ﺊ‎ → ‎ىٴ‎ ) ARABIC LETTER YEH WITH HAMZA 
ABOVE FINAL FORM → ARABIC LETTER ALEF MAKSURA, ARABIC LETTER HIGH HAMZA   # 
→‎ئ‎→→‎ٸ‎→→‎يٴ‎→
 FE89 ; 0649 0674 ;     MA      # ( ‎ﺉ‎ → ‎ىٴ‎ ) ARABIC LETTER YEH WITH HAMZA 
ABOVE ISOLATED FORM → ARABIC LETTER ALEF MAKSURA, ARABIC LETTER HIGH HAMZA      
  # →‎ٸ‎→→‎يٴ‎→
-0678 ; 0649 0674 ;     MA      # ( ‎ٸ‎ → ‎ىٴ‎ ) ARABIC LETTER HIGH HAMZA YEH → 
ARABIC LETTER ALEF MAKSURA, ARABIC LETTER HIGH HAMZA    # →‎يٴ‎→
 
 FBEB ; 0649 0674 006C ;        MA      # ( ‎ﯫ‎ → ‎ىٴl‎ ) ARABIC LIGATURE YEH 
WITH HAMZA ABOVE WITH ALEF FINAL FORM → ARABIC LETTER ALEF MAKSURA, ARABIC 
LETTER HIGH HAMZA, LATIN SMALL LETTER L        # →‎ئا‎→
 FBEA ; 0649 0674 006C ;        MA      # ( ‎ﯪ‎ → ‎ىٴl‎ ) ARABIC LIGATURE YEH 
WITH HAMZA ABOVE WITH ALEF ISOLATED FORM → ARABIC LETTER ALEF MAKSURA, ARABIC 
LETTER HIGH HAMZA, LATIN SMALL LETTER L     # →‎ئا‎→
@@ -7535,10 +7535,10 @@ FA7E ;  5944 ;  MA      # ( 奄 → 奄 ) CJK COMPATIBILITY 
IDEOGRAPH-FA7E → CJK UNIF
 
 F90C ; 5948 ;  MA      # ( 奈 → 奈 ) CJK COMPATIBILITY IDEOGRAPH-F90C → CJK 
UNIFIED IDEOGRAPH-5948       #
 
-F909 ; 5951 ;  MA      # ( 契 → 契 ) CJK COMPATIBILITY IDEOGRAPH-F909 → CJK 
UNIFIED IDEOGRAPH-5951       #
-
 FA7F ; 5954 ;  MA      # ( 奔 → 奔 ) CJK COMPATIBILITY IDEOGRAPH-FA7F → CJK 
UNIFIED IDEOGRAPH-5954       #
 
+F909 ; 5951 ;  MA      # ( 契 → 契 ) CJK COMPATIBILITY IDEOGRAPH-F909 → CJK 
UNIFIED IDEOGRAPH-5951       #
+
 2F85F ;        5962 ;  MA      # ( 奢 → 奢 ) CJK COMPATIBILITY IDEOGRAPH-2F85F → 
CJK UNIFIED IDEOGRAPH-5962      #
 
 F981 ; 5973 ;  MA      # ( 女 → 女 ) CJK COMPATIBILITY IDEOGRAPH-F981 → CJK 
UNIFIED IDEOGRAPH-5973       #
diff --git a/admin/unidata/copyright.html b/admin/unidata/copyright.html
index 66e54b06fc..0ae01c11ad 100644
--- a/admin/unidata/copyright.html
+++ b/admin/unidata/copyright.html
@@ -82,7 +82,7 @@ pre {
         </tr>
         <tr>
           <td valign="top" class="navColCell">
-        <a href="https://www.unicode.org/license.html";>Unicode Data Files and 
Software License</a></td>
+        <a href="https://www.unicode.org/license.txt";>Unicode Data Files and 
Software License</a></td>
         </tr>
         <tr>
           <td class="navColTitle">Related Links</td>
@@ -118,7 +118,7 @@ pre {
             <ol type="A">
               <li><u><a name="1"></a>Unicode Copyright</u>
               <ol>
-                <li>Copyright © 1991-2021 Unicode, Inc. All rights 
reserved.</li>
+                <li>Copyright © 1991-2022 Unicode, Inc. All rights 
reserved.</li>
               </ol>
               </li>
 
@@ -153,7 +153,7 @@ http://site.icu-project.org/download/
                 herein.</li>
                 <li>Further specifications of rights and restrictions 
pertaining
                 to the use of the Unicode DATA FILES and SOFTWARE can be found 
in the
-                <a href="https://www.unicode.org/license.html";>Unicode Data 
Files and Software License</a>.</li>
+                <a href="https://www.unicode.org/license.txt";>Unicode Data 
Files and Software License</a>.</li>
                 <li>Each version of the Unicode Standard has further
                 specifications of rights and restrictions of use. For the book
                 editions (Unicode 5.0 and earlier), these are found on the back
diff --git a/admin/unidata/emoji-data.txt b/admin/unidata/emoji-data.txt
index 2e9cf75ad4..7942fc89a3 100644
--- a/admin/unidata/emoji-data.txt
+++ b/admin/unidata/emoji-data.txt
@@ -1,13 +1,13 @@
-# emoji-data-14.0.0.txt
-# Date: 2021-08-26, 17:22:22 GMT
-# © 2021 Unicode®, Inc.
+# emoji-data.txt
+# Date: 2022-08-02, 00:26:10 GMT
+# © 2022 Unicode®, Inc.
 # Unicode and the Unicode Logo are registered trademarks of Unicode, Inc. in 
the U.S. and other countries.
-# For terms of use, see http://www.unicode.org/terms_of_use.html
+# For terms of use, see https://www.unicode.org/terms_of_use.html
 #
 # Emoji Data for UTS #51
-# Used with Emoji Version 14.0 and subsequent minor revisions (if any)
+# Used with Emoji Version 15.0 and subsequent minor revisions (if any)
 #
-# For documentation and usage, see http://www.unicode.org/reports/tr51
+# For documentation and usage, see https://www.unicode.org/reports/tr51
 #
 # Format:
 # <codepoint(s)> ; <property> # <comments>
@@ -20,7 +20,6 @@
 # ================================================
 
 # All omitted code points have Emoji=No
-# @missing: 0000..10FFFF  ; Emoji ; No
 
 0023          ; Emoji                # E0.0   [1] (#️)       hash sign
 002A          ; Emoji                # E0.0   [1] (*️)       asterisk
@@ -341,6 +340,7 @@
 1F6D1..1F6D2  ; Emoji                # E3.0   [2] (🛑..🛒)    stop 
sign..shopping cart
 1F6D5         ; Emoji                # E12.0  [1] (🛕)       hindu temple
 1F6D6..1F6D7  ; Emoji                # E13.0  [2] (🛖..🛗)    hut..elevator
+1F6DC         ; Emoji                # E15.0  [1] (🛜)       wireless
 1F6DD..1F6DF  ; Emoji                # E14.0  [3] (🛝..🛟)    playground 
slide..ring buoy
 1F6E0..1F6E5  ; Emoji                # E0.7   [6] (🛠️..🛥️)    hammer and 
wrench..motor boat
 1F6E9         ; Emoji                # E0.7   [1] (🛩️)       small airplane
@@ -401,28 +401,36 @@
 1F9E7..1F9FF  ; Emoji                # E11.0 [25] (🧧..🧿)    red 
envelope..nazar amulet
 1FA70..1FA73  ; Emoji                # E12.0  [4] (🩰..🩳)    ballet 
shoes..shorts
 1FA74         ; Emoji                # E13.0  [1] (🩴)       thong sandal
+1FA75..1FA77  ; Emoji                # E15.0  [3] (🩵..🩷)    light blue 
heart..pink heart
 1FA78..1FA7A  ; Emoji                # E12.0  [3] (🩸..🩺)    drop of 
blood..stethoscope
 1FA7B..1FA7C  ; Emoji                # E14.0  [2] (🩻..🩼)    x-ray..crutch
 1FA80..1FA82  ; Emoji                # E12.0  [3] (🪀..🪂)    yo-yo..parachute
 1FA83..1FA86  ; Emoji                # E13.0  [4] (🪃..🪆)    boomerang..nesting 
dolls
+1FA87..1FA88  ; Emoji                # E15.0  [2] (🪇..🪈)    maracas..flute
 1FA90..1FA95  ; Emoji                # E12.0  [6] (🪐..🪕)    ringed 
planet..banjo
 1FA96..1FAA8  ; Emoji                # E13.0 [19] (🪖..🪨)    military 
helmet..rock
 1FAA9..1FAAC  ; Emoji                # E14.0  [4] (🪩..🪬)    mirror ball..hamsa
+1FAAD..1FAAF  ; Emoji                # E15.0  [3] (🪭..🪯)    folding hand 
fan..khanda
 1FAB0..1FAB6  ; Emoji                # E13.0  [7] (🪰..🪶)    fly..feather
 1FAB7..1FABA  ; Emoji                # E14.0  [4] (🪷..🪺)    lotus..nest with 
eggs
+1FABB..1FABD  ; Emoji                # E15.0  [3] (🪻..🪽)    hyacinth..wing
+1FABF         ; Emoji                # E15.0  [1] (🪿)       goose
 1FAC0..1FAC2  ; Emoji                # E13.0  [3] (🫀..🫂)    anatomical 
heart..people hugging
 1FAC3..1FAC5  ; Emoji                # E14.0  [3] (🫃..🫅)    pregnant 
man..person with crown
+1FACE..1FACF  ; Emoji                # E15.0  [2] (🫎..🫏)    moose..donkey
 1FAD0..1FAD6  ; Emoji                # E13.0  [7] (🫐..🫖)    blueberries..teapot
 1FAD7..1FAD9  ; Emoji                # E14.0  [3] (🫗..🫙)    pouring liquid..jar
+1FADA..1FADB  ; Emoji                # E15.0  [2] (🫚..🫛)    ginger root..pea 
pod
 1FAE0..1FAE7  ; Emoji                # E14.0  [8] (🫠..🫧)    melting 
face..bubbles
+1FAE8         ; Emoji                # E15.0  [1] (🫨)       shaking face
 1FAF0..1FAF6  ; Emoji                # E14.0  [7] (🫰..🫶)    hand with index 
finger and thumb crossed..heart hands
+1FAF7..1FAF8  ; Emoji                # E15.0  [2] (🫷..🫸)    leftwards pushing 
hand..rightwards pushing hand
 
-# Total elements: 1404
+# Total elements: 1424
 
 # ================================================
 
 # All omitted code points have Emoji_Presentation=No
-# @missing: 0000..10FFFF  ; Emoji_Presentation ; No
 
 231A..231B    ; Emoji_Presentation   # E0.6   [2] (⌚..⌛)    watch..hourglass 
done
 23E9..23EC    ; Emoji_Presentation   # E0.6   [4] (⏩..⏬)    fast-forward 
button..fast down button
@@ -625,6 +633,7 @@
 1F6D1..1F6D2  ; Emoji_Presentation   # E3.0   [2] (🛑..🛒)    stop 
sign..shopping cart
 1F6D5         ; Emoji_Presentation   # E12.0  [1] (🛕)       hindu temple
 1F6D6..1F6D7  ; Emoji_Presentation   # E13.0  [2] (🛖..🛗)    hut..elevator
+1F6DC         ; Emoji_Presentation   # E15.0  [1] (🛜)       wireless
 1F6DD..1F6DF  ; Emoji_Presentation   # E14.0  [3] (🛝..🛟)    playground 
slide..ring buoy
 1F6EB..1F6EC  ; Emoji_Presentation   # E1.0   [2] (🛫..🛬)    airplane 
departure..airplane arrival
 1F6F4..1F6F6  ; Emoji_Presentation   # E3.0   [3] (🛴..🛶)    kick scooter..canoe
@@ -681,28 +690,36 @@
 1F9E7..1F9FF  ; Emoji_Presentation   # E11.0 [25] (🧧..🧿)    red 
envelope..nazar amulet
 1FA70..1FA73  ; Emoji_Presentation   # E12.0  [4] (🩰..🩳)    ballet 
shoes..shorts
 1FA74         ; Emoji_Presentation   # E13.0  [1] (🩴)       thong sandal
+1FA75..1FA77  ; Emoji_Presentation   # E15.0  [3] (🩵..🩷)    light blue 
heart..pink heart
 1FA78..1FA7A  ; Emoji_Presentation   # E12.0  [3] (🩸..🩺)    drop of 
blood..stethoscope
 1FA7B..1FA7C  ; Emoji_Presentation   # E14.0  [2] (🩻..🩼)    x-ray..crutch
 1FA80..1FA82  ; Emoji_Presentation   # E12.0  [3] (🪀..🪂)    yo-yo..parachute
 1FA83..1FA86  ; Emoji_Presentation   # E13.0  [4] (🪃..🪆)    boomerang..nesting 
dolls
+1FA87..1FA88  ; Emoji_Presentation   # E15.0  [2] (🪇..🪈)    maracas..flute
 1FA90..1FA95  ; Emoji_Presentation   # E12.0  [6] (🪐..🪕)    ringed 
planet..banjo
 1FA96..1FAA8  ; Emoji_Presentation   # E13.0 [19] (🪖..🪨)    military 
helmet..rock
 1FAA9..1FAAC  ; Emoji_Presentation   # E14.0  [4] (🪩..🪬)    mirror ball..hamsa
+1FAAD..1FAAF  ; Emoji_Presentation   # E15.0  [3] (🪭..🪯)    folding hand 
fan..khanda
 1FAB0..1FAB6  ; Emoji_Presentation   # E13.0  [7] (🪰..🪶)    fly..feather
 1FAB7..1FABA  ; Emoji_Presentation   # E14.0  [4] (🪷..🪺)    lotus..nest with 
eggs
+1FABB..1FABD  ; Emoji_Presentation   # E15.0  [3] (🪻..🪽)    hyacinth..wing
+1FABF         ; Emoji_Presentation   # E15.0  [1] (🪿)       goose
 1FAC0..1FAC2  ; Emoji_Presentation   # E13.0  [3] (🫀..🫂)    anatomical 
heart..people hugging
 1FAC3..1FAC5  ; Emoji_Presentation   # E14.0  [3] (🫃..🫅)    pregnant 
man..person with crown
+1FACE..1FACF  ; Emoji_Presentation   # E15.0  [2] (🫎..🫏)    moose..donkey
 1FAD0..1FAD6  ; Emoji_Presentation   # E13.0  [7] (🫐..🫖)    blueberries..teapot
 1FAD7..1FAD9  ; Emoji_Presentation   # E14.0  [3] (🫗..🫙)    pouring liquid..jar
+1FADA..1FADB  ; Emoji_Presentation   # E15.0  [2] (🫚..🫛)    ginger root..pea 
pod
 1FAE0..1FAE7  ; Emoji_Presentation   # E14.0  [8] (🫠..🫧)    melting 
face..bubbles
+1FAE8         ; Emoji_Presentation   # E15.0  [1] (🫨)       shaking face
 1FAF0..1FAF6  ; Emoji_Presentation   # E14.0  [7] (🫰..🫶)    hand with index 
finger and thumb crossed..heart hands
+1FAF7..1FAF8  ; Emoji_Presentation   # E15.0  [2] (🫷..🫸)    leftwards pushing 
hand..rightwards pushing hand
 
-# Total elements: 1185
+# Total elements: 1205
 
 # ================================================
 
 # All omitted code points have Emoji_Modifier=No
-# @missing: 0000..10FFFF  ; Emoji_Modifier ; No
 
 1F3FB..1F3FF  ; Emoji_Modifier       # E1.0   [5] (🏻..🏿)    light skin 
tone..dark skin tone
 
@@ -711,7 +728,6 @@
 # ================================================
 
 # All omitted code points have Emoji_Modifier_Base=No
-# @missing: 0000..10FFFF  ; Emoji_Modifier_Base ; No
 
 261D          ; Emoji_Modifier_Base  # E0.6   [1] (☝️)       index pointing up
 26F9          ; Emoji_Modifier_Base  # E0.7   [1] (⛹️)       person bouncing 
ball
@@ -762,13 +778,13 @@
 1F9D1..1F9DD  ; Emoji_Modifier_Base  # E5.0  [13] (🧑..🧝)    person..elf
 1FAC3..1FAC5  ; Emoji_Modifier_Base  # E14.0  [3] (🫃..🫅)    pregnant 
man..person with crown
 1FAF0..1FAF6  ; Emoji_Modifier_Base  # E14.0  [7] (🫰..🫶)    hand with index 
finger and thumb crossed..heart hands
+1FAF7..1FAF8  ; Emoji_Modifier_Base  # E15.0  [2] (🫷..🫸)    leftwards pushing 
hand..rightwards pushing hand
 
-# Total elements: 132
+# Total elements: 134
 
 # ================================================
 
 # All omitted code points have Emoji_Component=No
-# @missing: 0000..10FFFF  ; Emoji_Component ; No
 
 0023          ; Emoji_Component      # E0.0   [1] (#️)       hash sign
 002A          ; Emoji_Component      # E0.0   [1] (*️)       asterisk
@@ -786,7 +802,6 @@ E0020..E007F  ; Emoji_Component      # E0.0  [96] (󠀠..󠁿)    
  tag space..c
 # ================================================
 
 # All omitted code points have Extended_Pictographic=No
-# @missing: 0000..10FFFF  ; Extended_Pictographic ; No
 
 00A9          ; Extended_Pictographic# E0.6   [1] (©️)       copyright
 00AE          ; Extended_Pictographic# E0.6   [1] (®️)       registered
@@ -1190,7 +1205,8 @@ E0020..E007F  ; Emoji_Component      # E0.0  [96] (󠀠..󠁿)  
    tag space..c
 1F6D3..1F6D4  ; Extended_Pictographic# E0.0   [2] (🛓..🛔)    STUPA..PAGODA
 1F6D5         ; Extended_Pictographic# E12.0  [1] (🛕)       hindu temple
 1F6D6..1F6D7  ; Extended_Pictographic# E13.0  [2] (🛖..🛗)    hut..elevator
-1F6D8..1F6DC  ; Extended_Pictographic# E0.0   [5] (🛘..🛜)    
<reserved-1F6D8>..<reserved-1F6DC>
+1F6D8..1F6DB  ; Extended_Pictographic# E0.0   [4] (🛘..🛛)    
<reserved-1F6D8>..<reserved-1F6DB>
+1F6DC         ; Extended_Pictographic# E15.0  [1] (🛜)       wireless
 1F6DD..1F6DF  ; Extended_Pictographic# E14.0  [3] (🛝..🛟)    playground 
slide..ring buoy
 1F6E0..1F6E5  ; Extended_Pictographic# E0.7   [6] (🛠️..🛥️)    hammer and 
wrench..motor boat
 1F6E6..1F6E8  ; Extended_Pictographic# E0.0   [3] (🛦..🛨)    UP-POINTING 
MILITARY AIRPLANE..UP-POINTING SMALL AIRPLANE
@@ -1207,7 +1223,7 @@ E0020..E007F  ; Emoji_Component      # E0.0  [96] (󠀠..󠁿)  
    tag space..c
 1F6FA         ; Extended_Pictographic# E12.0  [1] (🛺)       auto rickshaw
 1F6FB..1F6FC  ; Extended_Pictographic# E13.0  [2] (🛻..🛼)    pickup 
truck..roller skate
 1F6FD..1F6FF  ; Extended_Pictographic# E0.0   [3] (🛽..🛿)    
<reserved-1F6FD>..<reserved-1F6FF>
-1F774..1F77F  ; Extended_Pictographic# E0.0  [12] (🝴..🝿)    
<reserved-1F774>..<reserved-1F77F>
+1F774..1F77F  ; Extended_Pictographic# E0.0  [12] (🝴..🝿)    LOT OF 
FORTUNE..ORCUS
 1F7D5..1F7DF  ; Extended_Pictographic# E0.0  [11] (🟕..🟟)    CIRCLED 
TRIANGLE..<reserved-1F7DF>
 1F7E0..1F7EB  ; Extended_Pictographic# E12.0 [12] (🟠..🟫)    orange 
circle..brown square
 1F7EC..1F7EF  ; Extended_Pictographic# E0.0   [4] (🟬..🟯)    
<reserved-1F7EC>..<reserved-1F7EF>
@@ -1266,30 +1282,37 @@ E0020..E007F  ; Emoji_Component      # E0.0  [96] 
(󠀠..󠁿)      tag space..c
 1FA00..1FA6F  ; Extended_Pictographic# E0.0 [112] (🨀..🩯)    NEUTRAL CHESS 
KING..<reserved-1FA6F>
 1FA70..1FA73  ; Extended_Pictographic# E12.0  [4] (🩰..🩳)    ballet 
shoes..shorts
 1FA74         ; Extended_Pictographic# E13.0  [1] (🩴)       thong sandal
-1FA75..1FA77  ; Extended_Pictographic# E0.0   [3] (🩵..🩷)    
<reserved-1FA75>..<reserved-1FA77>
+1FA75..1FA77  ; Extended_Pictographic# E15.0  [3] (🩵..🩷)    light blue 
heart..pink heart
 1FA78..1FA7A  ; Extended_Pictographic# E12.0  [3] (🩸..🩺)    drop of 
blood..stethoscope
 1FA7B..1FA7C  ; Extended_Pictographic# E14.0  [2] (🩻..🩼)    x-ray..crutch
 1FA7D..1FA7F  ; Extended_Pictographic# E0.0   [3] (🩽..🩿)    
<reserved-1FA7D>..<reserved-1FA7F>
 1FA80..1FA82  ; Extended_Pictographic# E12.0  [3] (🪀..🪂)    yo-yo..parachute
 1FA83..1FA86  ; Extended_Pictographic# E13.0  [4] (🪃..🪆)    boomerang..nesting 
dolls
-1FA87..1FA8F  ; Extended_Pictographic# E0.0   [9] (🪇..🪏)    
<reserved-1FA87>..<reserved-1FA8F>
+1FA87..1FA88  ; Extended_Pictographic# E15.0  [2] (🪇..🪈)    maracas..flute
+1FA89..1FA8F  ; Extended_Pictographic# E0.0   [7] (🪉..🪏)    
<reserved-1FA89>..<reserved-1FA8F>
 1FA90..1FA95  ; Extended_Pictographic# E12.0  [6] (🪐..🪕)    ringed 
planet..banjo
 1FA96..1FAA8  ; Extended_Pictographic# E13.0 [19] (🪖..🪨)    military 
helmet..rock
 1FAA9..1FAAC  ; Extended_Pictographic# E14.0  [4] (🪩..🪬)    mirror ball..hamsa
-1FAAD..1FAAF  ; Extended_Pictographic# E0.0   [3] (🪭..🪯)    
<reserved-1FAAD>..<reserved-1FAAF>
+1FAAD..1FAAF  ; Extended_Pictographic# E15.0  [3] (🪭..🪯)    folding hand 
fan..khanda
 1FAB0..1FAB6  ; Extended_Pictographic# E13.0  [7] (🪰..🪶)    fly..feather
 1FAB7..1FABA  ; Extended_Pictographic# E14.0  [4] (🪷..🪺)    lotus..nest with 
eggs
-1FABB..1FABF  ; Extended_Pictographic# E0.0   [5] (🪻..🪿)    
<reserved-1FABB>..<reserved-1FABF>
+1FABB..1FABD  ; Extended_Pictographic# E15.0  [3] (🪻..🪽)    hyacinth..wing
+1FABE         ; Extended_Pictographic# E0.0   [1] (🪾)       <reserved-1FABE>
+1FABF         ; Extended_Pictographic# E15.0  [1] (🪿)       goose
 1FAC0..1FAC2  ; Extended_Pictographic# E13.0  [3] (🫀..🫂)    anatomical 
heart..people hugging
 1FAC3..1FAC5  ; Extended_Pictographic# E14.0  [3] (🫃..🫅)    pregnant 
man..person with crown
-1FAC6..1FACF  ; Extended_Pictographic# E0.0  [10] (🫆..🫏)    
<reserved-1FAC6>..<reserved-1FACF>
+1FAC6..1FACD  ; Extended_Pictographic# E0.0   [8] (🫆..🫍)    
<reserved-1FAC6>..<reserved-1FACD>
+1FACE..1FACF  ; Extended_Pictographic# E15.0  [2] (🫎..🫏)    moose..donkey
 1FAD0..1FAD6  ; Extended_Pictographic# E13.0  [7] (🫐..🫖)    blueberries..teapot
 1FAD7..1FAD9  ; Extended_Pictographic# E14.0  [3] (🫗..🫙)    pouring liquid..jar
-1FADA..1FADF  ; Extended_Pictographic# E0.0   [6] (🫚..🫟)    
<reserved-1FADA>..<reserved-1FADF>
+1FADA..1FADB  ; Extended_Pictographic# E15.0  [2] (🫚..🫛)    ginger root..pea 
pod
+1FADC..1FADF  ; Extended_Pictographic# E0.0   [4] (🫜..🫟)    
<reserved-1FADC>..<reserved-1FADF>
 1FAE0..1FAE7  ; Extended_Pictographic# E14.0  [8] (🫠..🫧)    melting 
face..bubbles
-1FAE8..1FAEF  ; Extended_Pictographic# E0.0   [8] (🫨..🫯)    
<reserved-1FAE8>..<reserved-1FAEF>
+1FAE8         ; Extended_Pictographic# E15.0  [1] (🫨)       shaking face
+1FAE9..1FAEF  ; Extended_Pictographic# E0.0   [7] (🫩..🫯)    
<reserved-1FAE9>..<reserved-1FAEF>
 1FAF0..1FAF6  ; Extended_Pictographic# E14.0  [7] (🫰..🫶)    hand with index 
finger and thumb crossed..heart hands
-1FAF7..1FAFF  ; Extended_Pictographic# E0.0   [9] (🫷..🫿)    
<reserved-1FAF7>..<reserved-1FAFF>
+1FAF7..1FAF8  ; Extended_Pictographic# E15.0  [2] (🫷..🫸)    leftwards pushing 
hand..rightwards pushing hand
+1FAF9..1FAFF  ; Extended_Pictographic# E0.0   [7] (🫹..🫿)    
<reserved-1FAF9>..<reserved-1FAFF>
 1FC00..1FFFD  ; Extended_Pictographic# E0.0[1022] (🰀..🿽)    
<reserved-1FC00>..<reserved-1FFFD>
 
 # Total elements: 3537
diff --git a/admin/unidata/emoji-sequences.txt 
b/admin/unidata/emoji-sequences.txt
index dedf7ff543..ffd4066811 100644
--- a/admin/unidata/emoji-sequences.txt
+++ b/admin/unidata/emoji-sequences.txt
@@ -1,13 +1,13 @@
 # emoji-sequences.txt
-# Date: 2021-08-26, 17:22:22 GMT
-# © 2021 Unicode®, Inc.
+# Date: 2022-08-15, 23:13:41 GMT
+# © 2022 Unicode®, Inc.
 # Unicode and the Unicode Logo are registered trademarks of Unicode, Inc. in 
the U.S. and other countries.
-# For terms of use, see http://www.unicode.org/terms_of_use.html
+# For terms of use, see https://www.unicode.org/terms_of_use.html
 #
 # Emoji Sequence Data for UTS #51
-# Version: 14.0
+# Version: 15.0
 #
-# For documentation and usage, see http://www.unicode.org/reports/tr51
+# For documentation and usage, see https://www.unicode.org/reports/tr51
 #
 # Format:
 #   code_point(s) ; type_field ; description # comments
@@ -38,144 +38,145 @@
 
 # Basic_Emoji
 
-231A..231B    ; Basic_Emoji                  ; watch                           
                               # E0.6   [2] (⌚..⌛)
-23E9..23EC    ; Basic_Emoji                  ; fast-forward button             
                               # E0.6   [4] (⏩..⏬)
+
+231A..231B    ; Basic_Emoji                  ; watch..hourglass done           
                               # E0.6   [2] (⌚..⌛)
+23E9..23EC    ; Basic_Emoji                  ; fast-forward button..fast down 
button                          # E0.6   [4] (⏩..⏬)
 23F0          ; Basic_Emoji                  ; alarm clock                     
                               # E0.6   [1] (⏰)
 23F3          ; Basic_Emoji                  ; hourglass not done              
                               # E0.6   [1] (⏳)
-25FD..25FE    ; Basic_Emoji                  ; white medium-small square       
                               # E0.6   [2] (◽..◾)
-2614..2615    ; Basic_Emoji                  ; umbrella with rain drops        
                               # E0.6   [2] (☔..☕)
-2648..2653    ; Basic_Emoji                  ; Aries                           
                               # E0.6  [12] (♈..♓)
+25FD..25FE    ; Basic_Emoji                  ; white medium-small 
square..black medium-small square           # E0.6   [2] (◽..◾)
+2614..2615    ; Basic_Emoji                  ; umbrella with rain drops..hot 
beverage                         # E0.6   [2] (☔..☕)
+2648..2653    ; Basic_Emoji                  ; Aries..Pisces                   
                               # E0.6  [12] (♈..♓)
 267F          ; Basic_Emoji                  ; wheelchair symbol               
                               # E0.6   [1] (♿)
 2693          ; Basic_Emoji                  ; anchor                          
                               # E0.6   [1] (⚓)
 26A1          ; Basic_Emoji                  ; high voltage                    
                               # E0.6   [1] (⚡)
-26AA..26AB    ; Basic_Emoji                  ; white circle                    
                               # E0.6   [2] (⚪..⚫)
-26BD..26BE    ; Basic_Emoji                  ; soccer ball                     
                               # E0.6   [2] (⚽..⚾)
-26C4..26C5    ; Basic_Emoji                  ; snowman without snow            
                               # E0.6   [2] (⛄..⛅)
+26AA..26AB    ; Basic_Emoji                  ; white circle..black circle      
                               # E0.6   [2] (⚪..⚫)
+26BD..26BE    ; Basic_Emoji                  ; soccer ball..baseball           
                               # E0.6   [2] (⚽..⚾)
+26C4..26C5    ; Basic_Emoji                  ; snowman without snow..sun 
behind cloud                         # E0.6   [2] (⛄..⛅)
 26CE          ; Basic_Emoji                  ; Ophiuchus                       
                               # E0.6   [1] (⛎)
 26D4          ; Basic_Emoji                  ; no entry                        
                               # E0.6   [1] (⛔)
 26EA          ; Basic_Emoji                  ; church                          
                               # E0.6   [1] (⛪)
-26F2..26F3    ; Basic_Emoji                  ; fountain                        
                               # E0.6   [2] (⛲..⛳)
+26F2..26F3    ; Basic_Emoji                  ; fountain..flag in hole          
                               # E0.6   [2] (⛲..⛳)
 26F5          ; Basic_Emoji                  ; sailboat                        
                               # E0.6   [1] (⛵)
 26FA          ; Basic_Emoji                  ; tent                            
                               # E0.6   [1] (⛺)
 26FD          ; Basic_Emoji                  ; fuel pump                       
                               # E0.6   [1] (⛽)
 2705          ; Basic_Emoji                  ; check mark button               
                               # E0.6   [1] (✅)
-270A..270B    ; Basic_Emoji                  ; raised fist                     
                               # E0.6   [2] (✊..✋)
+270A..270B    ; Basic_Emoji                  ; raised fist..raised hand        
                               # E0.6   [2] (✊..✋)
 2728          ; Basic_Emoji                  ; sparkles                        
                               # E0.6   [1] (✨)
 274C          ; Basic_Emoji                  ; cross mark                      
                               # E0.6   [1] (❌)
 274E          ; Basic_Emoji                  ; cross mark button               
                               # E0.6   [1] (❎)
-2753..2755    ; Basic_Emoji                  ; red question mark               
                               # E0.6   [3] (❓..❕)
+2753..2755    ; Basic_Emoji                  ; red question mark..white 
exclamation mark                      # E0.6   [3] (❓..❕)
 2757          ; Basic_Emoji                  ; red exclamation mark            
                               # E0.6   [1] (❗)
-2795..2797    ; Basic_Emoji                  ; plus                            
                               # E0.6   [3] (➕..➗)
+2795..2797    ; Basic_Emoji                  ; plus..divide                    
                               # E0.6   [3] (➕..➗)
 27B0          ; Basic_Emoji                  ; curly loop                      
                               # E0.6   [1] (➰)
 27BF          ; Basic_Emoji                  ; double curly loop               
                               # E1.0   [1] (➿)
-2B1B..2B1C    ; Basic_Emoji                  ; black large square              
                               # E0.6   [2] (⬛..⬜)
+2B1B..2B1C    ; Basic_Emoji                  ; black large square..white large 
square                         # E0.6   [2] (⬛..⬜)
 2B50          ; Basic_Emoji                  ; star                            
                               # E0.6   [1] (⭐)
 2B55          ; Basic_Emoji                  ; hollow red circle               
                               # E0.6   [1] (⭕)
 1F004         ; Basic_Emoji                  ; mahjong red dragon              
                               # E0.6   [1] (🀄)
 1F0CF         ; Basic_Emoji                  ; joker                           
                               # E0.6   [1] (🃏)
 1F18E         ; Basic_Emoji                  ; AB button (blood type)          
                               # E0.6   [1] (🆎)
-1F191..1F19A  ; Basic_Emoji                  ; CL button                       
                               # E0.6  [10] (🆑..🆚)
+1F191..1F19A  ; Basic_Emoji                  ; CL button..VS button            
                               # E0.6  [10] (🆑..🆚)
 1F201         ; Basic_Emoji                  ; Japanese “here” button          
                               # E0.6   [1] (🈁)
 1F21A         ; Basic_Emoji                  ; Japanese “free of charge” 
button                               # E0.6   [1] (🈚)
 1F22F         ; Basic_Emoji                  ; Japanese “reserved” button      
                               # E0.6   [1] (🈯)
-1F232..1F236  ; Basic_Emoji                  ; Japanese “prohibited” button    
                               # E0.6   [5] (🈲..🈶)
-1F238..1F23A  ; Basic_Emoji                  ; Japanese “application” button   
                               # E0.6   [3] (🈸..🈺)
-1F250..1F251  ; Basic_Emoji                  ; Japanese “bargain” button       
                               # E0.6   [2] (🉐..🉑)
-1F300..1F30C  ; Basic_Emoji                  ; cyclone                         
                               # E0.6  [13] (🌀..🌌)
-1F30D..1F30E  ; Basic_Emoji                  ; globe showing Europe-Africa     
                               # E0.7   [2] (🌍..🌎)
+1F232..1F236  ; Basic_Emoji                  ; Japanese “prohibited” 
button..Japanese “not free of charge” button#E0.6 [5] (🈲..🈶)
+1F238..1F23A  ; Basic_Emoji                  ; Japanese “application” 
button..Japanese “open for business” button#E0.6 [3] (🈸..🈺)
+1F250..1F251  ; Basic_Emoji                  ; Japanese “bargain” 
button..Japanese “acceptable” button        # E0.6   [2] (🉐..🉑)
+1F300..1F30C  ; Basic_Emoji                  ; cyclone..milky way              
                               # E0.6  [13] (🌀..🌌)
+1F30D..1F30E  ; Basic_Emoji                  ; globe showing 
Europe-Africa..globe showing Americas            # E0.7   [2] (🌍..🌎)
 1F30F         ; Basic_Emoji                  ; globe showing Asia-Australia    
                               # E0.6   [1] (🌏)
 1F310         ; Basic_Emoji                  ; globe with meridians            
                               # E1.0   [1] (🌐)
 1F311         ; Basic_Emoji                  ; new moon                        
                               # E0.6   [1] (🌑)
 1F312         ; Basic_Emoji                  ; waxing crescent moon            
                               # E1.0   [1] (🌒)
-1F313..1F315  ; Basic_Emoji                  ; first quarter moon              
                               # E0.6   [3] (🌓..🌕)
-1F316..1F318  ; Basic_Emoji                  ; waning gibbous moon             
                               # E1.0   [3] (🌖..🌘)
+1F313..1F315  ; Basic_Emoji                  ; first quarter moon..full moon   
                               # E0.6   [3] (🌓..🌕)
+1F316..1F318  ; Basic_Emoji                  ; waning gibbous moon..waning 
crescent moon                      # E1.0   [3] (🌖..🌘)
 1F319         ; Basic_Emoji                  ; crescent moon                   
                               # E0.6   [1] (🌙)
 1F31A         ; Basic_Emoji                  ; new moon face                   
                               # E1.0   [1] (🌚)
 1F31B         ; Basic_Emoji                  ; first quarter moon face         
                               # E0.6   [1] (🌛)
 1F31C         ; Basic_Emoji                  ; last quarter moon face          
                               # E0.7   [1] (🌜)
-1F31D..1F31E  ; Basic_Emoji                  ; full moon face                  
                               # E1.0   [2] (🌝..🌞)
-1F31F..1F320  ; Basic_Emoji                  ; glowing star                    
                               # E0.6   [2] (🌟..🌠)
-1F32D..1F32F  ; Basic_Emoji                  ; hot dog                         
                               # E1.0   [3] (🌭..🌯)
-1F330..1F331  ; Basic_Emoji                  ; chestnut                        
                               # E0.6   [2] (🌰..🌱)
-1F332..1F333  ; Basic_Emoji                  ; evergreen tree                  
                               # E1.0   [2] (🌲..🌳)
-1F334..1F335  ; Basic_Emoji                  ; palm tree                       
                               # E0.6   [2] (🌴..🌵)
-1F337..1F34A  ; Basic_Emoji                  ; tulip                           
                               # E0.6  [20] (🌷..🍊)
+1F31D..1F31E  ; Basic_Emoji                  ; full moon face..sun with face   
                               # E1.0   [2] (🌝..🌞)
+1F31F..1F320  ; Basic_Emoji                  ; glowing star..shooting star     
                               # E0.6   [2] (🌟..🌠)
+1F32D..1F32F  ; Basic_Emoji                  ; hot dog..burrito                
                               # E1.0   [3] (🌭..🌯)
+1F330..1F331  ; Basic_Emoji                  ; chestnut..seedling              
                               # E0.6   [2] (🌰..🌱)
+1F332..1F333  ; Basic_Emoji                  ; evergreen tree..deciduous tree  
                               # E1.0   [2] (🌲..🌳)
+1F334..1F335  ; Basic_Emoji                  ; palm tree..cactus               
                               # E0.6   [2] (🌴..🌵)
+1F337..1F34A  ; Basic_Emoji                  ; tulip..tangerine                
                               # E0.6  [20] (🌷..🍊)
 1F34B         ; Basic_Emoji                  ; lemon                           
                               # E1.0   [1] (🍋)
-1F34C..1F34F  ; Basic_Emoji                  ; banana                          
                               # E0.6   [4] (🍌..🍏)
+1F34C..1F34F  ; Basic_Emoji                  ; banana..green apple             
                               # E0.6   [4] (🍌..🍏)
 1F350         ; Basic_Emoji                  ; pear                            
                               # E1.0   [1] (🍐)
-1F351..1F37B  ; Basic_Emoji                  ; peach                           
                               # E0.6  [43] (🍑..🍻)
+1F351..1F37B  ; Basic_Emoji                  ; peach..clinking beer mugs       
                               # E0.6  [43] (🍑..🍻)
 1F37C         ; Basic_Emoji                  ; baby bottle                     
                               # E1.0   [1] (🍼)
-1F37E..1F37F  ; Basic_Emoji                  ; bottle with popping cork        
                               # E1.0   [2] (🍾..🍿)
-1F380..1F393  ; Basic_Emoji                  ; ribbon                          
                               # E0.6  [20] (🎀..🎓)
-1F3A0..1F3C4  ; Basic_Emoji                  ; carousel horse                  
                               # E0.6  [37] (🎠..🏄)
+1F37E..1F37F  ; Basic_Emoji                  ; bottle with popping 
cork..popcorn                              # E1.0   [2] (🍾..🍿)
+1F380..1F393  ; Basic_Emoji                  ; ribbon..graduation cap          
                               # E0.6  [20] (🎀..🎓)
+1F3A0..1F3C4  ; Basic_Emoji                  ; carousel horse..person surfing  
                               # E0.6  [37] (🎠..🏄)
 1F3C5         ; Basic_Emoji                  ; sports medal                    
                               # E1.0   [1] (🏅)
 1F3C6         ; Basic_Emoji                  ; trophy                          
                               # E0.6   [1] (🏆)
 1F3C7         ; Basic_Emoji                  ; horse racing                    
                               # E1.0   [1] (🏇)
 1F3C8         ; Basic_Emoji                  ; american football               
                               # E0.6   [1] (🏈)
 1F3C9         ; Basic_Emoji                  ; rugby football                  
                               # E1.0   [1] (🏉)
 1F3CA         ; Basic_Emoji                  ; person swimming                 
                               # E0.6   [1] (🏊)
-1F3CF..1F3D3  ; Basic_Emoji                  ; cricket game                    
                               # E1.0   [5] (🏏..🏓)
-1F3E0..1F3E3  ; Basic_Emoji                  ; house                           
                               # E0.6   [4] (🏠..🏣)
+1F3CF..1F3D3  ; Basic_Emoji                  ; cricket game..ping pong         
                               # E1.0   [5] (🏏..🏓)
+1F3E0..1F3E3  ; Basic_Emoji                  ; house..Japanese post office     
                               # E0.6   [4] (🏠..🏣)
 1F3E4         ; Basic_Emoji                  ; post office                     
                               # E1.0   [1] (🏤)
-1F3E5..1F3F0  ; Basic_Emoji                  ; hospital                        
                               # E0.6  [12] (🏥..🏰)
+1F3E5..1F3F0  ; Basic_Emoji                  ; hospital..castle                
                               # E0.6  [12] (🏥..🏰)
 1F3F4         ; Basic_Emoji                  ; black flag                      
                               # E1.0   [1] (🏴)
-1F3F8..1F407  ; Basic_Emoji                  ; badminton                       
                               # E1.0  [16] (🏸..🐇)
+1F3F8..1F407  ; Basic_Emoji                  ; badminton..rabbit               
                               # E1.0  [16] (🏸..🐇)
 1F408         ; Basic_Emoji                  ; cat                             
                               # E0.7   [1] (🐈)
-1F409..1F40B  ; Basic_Emoji                  ; dragon                          
                               # E1.0   [3] (🐉..🐋)
-1F40C..1F40E  ; Basic_Emoji                  ; snail                           
                               # E0.6   [3] (🐌..🐎)
-1F40F..1F410  ; Basic_Emoji                  ; ram                             
                               # E1.0   [2] (🐏..🐐)
-1F411..1F412  ; Basic_Emoji                  ; ewe                             
                               # E0.6   [2] (🐑..🐒)
+1F409..1F40B  ; Basic_Emoji                  ; dragon..whale                   
                               # E1.0   [3] (🐉..🐋)
+1F40C..1F40E  ; Basic_Emoji                  ; snail..horse                    
                               # E0.6   [3] (🐌..🐎)
+1F40F..1F410  ; Basic_Emoji                  ; ram..goat                       
                               # E1.0   [2] (🐏..🐐)
+1F411..1F412  ; Basic_Emoji                  ; ewe..monkey                     
                               # E0.6   [2] (🐑..🐒)
 1F413         ; Basic_Emoji                  ; rooster                         
                               # E1.0   [1] (🐓)
 1F414         ; Basic_Emoji                  ; chicken                         
                               # E0.6   [1] (🐔)
 1F415         ; Basic_Emoji                  ; dog                             
                               # E0.7   [1] (🐕)
 1F416         ; Basic_Emoji                  ; pig                             
                               # E1.0   [1] (🐖)
-1F417..1F429  ; Basic_Emoji                  ; boar                            
                               # E0.6  [19] (🐗..🐩)
+1F417..1F429  ; Basic_Emoji                  ; boar..poodle                    
                               # E0.6  [19] (🐗..🐩)
 1F42A         ; Basic_Emoji                  ; camel                           
                               # E1.0   [1] (🐪)
-1F42B..1F43E  ; Basic_Emoji                  ; two-hump camel                  
                               # E0.6  [20] (🐫..🐾)
+1F42B..1F43E  ; Basic_Emoji                  ; two-hump camel..paw prints      
                               # E0.6  [20] (🐫..🐾)
 1F440         ; Basic_Emoji                  ; eyes                            
                               # E0.6   [1] (👀)
-1F442..1F464  ; Basic_Emoji                  ; ear                             
                               # E0.6  [35] (👂..👤)
+1F442..1F464  ; Basic_Emoji                  ; ear..bust in silhouette         
                               # E0.6  [35] (👂..👤)
 1F465         ; Basic_Emoji                  ; busts in silhouette             
                               # E1.0   [1] (👥)
-1F466..1F46B  ; Basic_Emoji                  ; boy                             
                               # E0.6   [6] (👦..👫)
-1F46C..1F46D  ; Basic_Emoji                  ; men holding hands               
                               # E1.0   [2] (👬..👭)
-1F46E..1F4AC  ; Basic_Emoji                  ; police officer                  
                               # E0.6  [63] (👮..💬)
+1F466..1F46B  ; Basic_Emoji                  ; boy..woman and man holding 
hands                               # E0.6   [6] (👦..👫)
+1F46C..1F46D  ; Basic_Emoji                  ; men holding hands..women 
holding hands                         # E1.0   [2] (👬..👭)
+1F46E..1F4AC  ; Basic_Emoji                  ; police officer..speech balloon  
                               # E0.6  [63] (👮..💬)
 1F4AD         ; Basic_Emoji                  ; thought balloon                 
                               # E1.0   [1] (💭)
-1F4AE..1F4B5  ; Basic_Emoji                  ; white flower                    
                               # E0.6   [8] (💮..💵)
-1F4B6..1F4B7  ; Basic_Emoji                  ; euro banknote                   
                               # E1.0   [2] (💶..💷)
-1F4B8..1F4EB  ; Basic_Emoji                  ; money with wings                
                               # E0.6  [52] (💸..📫)
-1F4EC..1F4ED  ; Basic_Emoji                  ; open mailbox with raised flag   
                               # E0.7   [2] (📬..📭)
+1F4AE..1F4B5  ; Basic_Emoji                  ; white flower..dollar banknote   
                               # E0.6   [8] (💮..💵)
+1F4B6..1F4B7  ; Basic_Emoji                  ; euro banknote..pound banknote   
                               # E1.0   [2] (💶..💷)
+1F4B8..1F4EB  ; Basic_Emoji                  ; money with wings..closed 
mailbox with raised flag              # E0.6  [52] (💸..📫)
+1F4EC..1F4ED  ; Basic_Emoji                  ; open mailbox with raised 
flag..open mailbox with lowered flag  # E0.7   [2] (📬..📭)
 1F4EE         ; Basic_Emoji                  ; postbox                         
                               # E0.6   [1] (📮)
 1F4EF         ; Basic_Emoji                  ; postal horn                     
                               # E1.0   [1] (📯)
-1F4F0..1F4F4  ; Basic_Emoji                  ; newspaper                       
                               # E0.6   [5] (📰..📴)
+1F4F0..1F4F4  ; Basic_Emoji                  ; newspaper..mobile phone off     
                               # E0.6   [5] (📰..📴)
 1F4F5         ; Basic_Emoji                  ; no mobile phones                
                               # E1.0   [1] (📵)
-1F4F6..1F4F7  ; Basic_Emoji                  ; antenna bars                    
                               # E0.6   [2] (📶..📷)
+1F4F6..1F4F7  ; Basic_Emoji                  ; antenna bars..camera            
                               # E0.6   [2] (📶..📷)
 1F4F8         ; Basic_Emoji                  ; camera with flash               
                               # E1.0   [1] (📸)
-1F4F9..1F4FC  ; Basic_Emoji                  ; video camera                    
                               # E0.6   [4] (📹..📼)
-1F4FF..1F502  ; Basic_Emoji                  ; prayer beads                    
                               # E1.0   [4] (📿..🔂)
+1F4F9..1F4FC  ; Basic_Emoji                  ; video camera..videocassette     
                               # E0.6   [4] (📹..📼)
+1F4FF..1F502  ; Basic_Emoji                  ; prayer beads..repeat single 
button                             # E1.0   [4] (📿..🔂)
 1F503         ; Basic_Emoji                  ; clockwise vertical arrows       
                               # E0.6   [1] (🔃)
-1F504..1F507  ; Basic_Emoji                  ; counterclockwise arrows button  
                               # E1.0   [4] (🔄..🔇)
+1F504..1F507  ; Basic_Emoji                  ; counterclockwise arrows 
button..muted speaker                  # E1.0   [4] (🔄..🔇)
 1F508         ; Basic_Emoji                  ; speaker low volume              
                               # E0.7   [1] (🔈)
 1F509         ; Basic_Emoji                  ; speaker medium volume           
                               # E1.0   [1] (🔉)
-1F50A..1F514  ; Basic_Emoji                  ; speaker high volume             
                               # E0.6  [11] (🔊..🔔)
+1F50A..1F514  ; Basic_Emoji                  ; speaker high volume..bell       
                               # E0.6  [11] (🔊..🔔)
 1F515         ; Basic_Emoji                  ; bell with slash                 
                               # E1.0   [1] (🔕)
-1F516..1F52B  ; Basic_Emoji                  ; bookmark                        
                               # E0.6  [22] (🔖..🔫)
-1F52C..1F52D  ; Basic_Emoji                  ; microscope                      
                               # E1.0   [2] (🔬..🔭)
-1F52E..1F53D  ; Basic_Emoji                  ; crystal ball                    
                               # E0.6  [16] (🔮..🔽)
-1F54B..1F54E  ; Basic_Emoji                  ; kaaba                           
                               # E1.0   [4] (🕋..🕎)
-1F550..1F55B  ; Basic_Emoji                  ; one o’clock                     
                               # E0.6  [12] (🕐..🕛)
-1F55C..1F567  ; Basic_Emoji                  ; one-thirty                      
                               # E0.7  [12] (🕜..🕧)
+1F516..1F52B  ; Basic_Emoji                  ; bookmark..water pistol          
                               # E0.6  [22] (🔖..🔫)
+1F52C..1F52D  ; Basic_Emoji                  ; microscope..telescope           
                               # E1.0   [2] (🔬..🔭)
+1F52E..1F53D  ; Basic_Emoji                  ; crystal ball..downwards button  
                               # E0.6  [16] (🔮..🔽)
+1F54B..1F54E  ; Basic_Emoji                  ; kaaba..menorah                  
                               # E1.0   [4] (🕋..🕎)
+1F550..1F55B  ; Basic_Emoji                  ; one o’clock..twelve o’clock     
                               # E0.6  [12] (🕐..🕛)
+1F55C..1F567  ; Basic_Emoji                  ; one-thirty..twelve-thirty       
                               # E0.7  [12] (🕜..🕧)
 1F57A         ; Basic_Emoji                  ; man dancing                     
                               # E3.0   [1] (🕺)
-1F595..1F596  ; Basic_Emoji                  ; middle finger                   
                               # E1.0   [2] (🖕..🖖)
+1F595..1F596  ; Basic_Emoji                  ; middle finger..vulcan salute    
                               # E1.0   [2] (🖕..🖖)
 1F5A4         ; Basic_Emoji                  ; black heart                     
                               # E3.0   [1] (🖤)
-1F5FB..1F5FF  ; Basic_Emoji                  ; mount fuji                      
                               # E0.6   [5] (🗻..🗿)
+1F5FB..1F5FF  ; Basic_Emoji                  ; mount fuji..moai                
                               # E0.6   [5] (🗻..🗿)
 1F600         ; Basic_Emoji                  ; grinning face                   
                               # E1.0   [1] (😀)
-1F601..1F606  ; Basic_Emoji                  ; beaming face with smiling eyes  
                               # E0.6   [6] (😁..😆)
-1F607..1F608  ; Basic_Emoji                  ; smiling face with halo          
                               # E1.0   [2] (😇..😈)
-1F609..1F60D  ; Basic_Emoji                  ; winking face                    
                               # E0.6   [5] (😉..😍)
+1F601..1F606  ; Basic_Emoji                  ; beaming face with smiling 
eyes..grinning squinting face        # E0.6   [6] (😁..😆)
+1F607..1F608  ; Basic_Emoji                  ; smiling face with halo..smiling 
face with horns                # E1.0   [2] (😇..😈)
+1F609..1F60D  ; Basic_Emoji                  ; winking face..smiling face with 
heart-eyes                     # E0.6   [5] (😉..😍)
 1F60E         ; Basic_Emoji                  ; smiling face with sunglasses    
                               # E1.0   [1] (😎)
 1F60F         ; Basic_Emoji                  ; smirking face                   
                               # E0.6   [1] (😏)
 1F610         ; Basic_Emoji                  ; neutral face                    
                               # E0.7   [1] (😐)
 1F611         ; Basic_Emoji                  ; expressionless face             
                               # E1.0   [1] (😑)
-1F612..1F614  ; Basic_Emoji                  ; unamused face                   
                               # E0.6   [3] (😒..😔)
+1F612..1F614  ; Basic_Emoji                  ; unamused face..pensive face     
                               # E0.6   [3] (😒..😔)
 1F615         ; Basic_Emoji                  ; confused face                   
                               # E1.0   [1] (😕)
 1F616         ; Basic_Emoji                  ; confounded face                 
                               # E0.6   [1] (😖)
 1F617         ; Basic_Emoji                  ; kissing face                    
                               # E1.0   [1] (😗)
@@ -183,132 +184,142 @@
 1F619         ; Basic_Emoji                  ; kissing face with smiling eyes  
                               # E1.0   [1] (😙)
 1F61A         ; Basic_Emoji                  ; kissing face with closed eyes   
                               # E0.6   [1] (😚)
 1F61B         ; Basic_Emoji                  ; face with tongue                
                               # E1.0   [1] (😛)
-1F61C..1F61E  ; Basic_Emoji                  ; winking face with tongue        
                               # E0.6   [3] (😜..😞)
+1F61C..1F61E  ; Basic_Emoji                  ; winking face with 
tongue..disappointed face                    # E0.6   [3] (😜..😞)
 1F61F         ; Basic_Emoji                  ; worried face                    
                               # E1.0   [1] (😟)
-1F620..1F625  ; Basic_Emoji                  ; angry face                      
                               # E0.6   [6] (😠..😥)
-1F626..1F627  ; Basic_Emoji                  ; frowning face with open mouth   
                               # E1.0   [2] (😦..😧)
-1F628..1F62B  ; Basic_Emoji                  ; fearful face                    
                               # E0.6   [4] (😨..😫)
+1F620..1F625  ; Basic_Emoji                  ; angry face..sad but relieved 
face                              # E0.6   [6] (😠..😥)
+1F626..1F627  ; Basic_Emoji                  ; frowning face with open 
mouth..anguished face                  # E1.0   [2] (😦..😧)
+1F628..1F62B  ; Basic_Emoji                  ; fearful face..tired face        
                               # E0.6   [4] (😨..😫)
 1F62C         ; Basic_Emoji                  ; grimacing face                  
                               # E1.0   [1] (😬)
 1F62D         ; Basic_Emoji                  ; loudly crying face              
                               # E0.6   [1] (😭)
-1F62E..1F62F  ; Basic_Emoji                  ; face with open mouth            
                               # E1.0   [2] (😮..😯)
-1F630..1F633  ; Basic_Emoji                  ; anxious face with sweat         
                               # E0.6   [4] (😰..😳)
+1F62E..1F62F  ; Basic_Emoji                  ; face with open mouth..hushed 
face                              # E1.0   [2] (😮..😯)
+1F630..1F633  ; Basic_Emoji                  ; anxious face with 
sweat..flushed face                          # E0.6   [4] (😰..😳)
 1F634         ; Basic_Emoji                  ; sleeping face                   
                               # E1.0   [1] (😴)
 1F635         ; Basic_Emoji                  ; face with crossed-out eyes      
                               # E0.6   [1] (😵)
 1F636         ; Basic_Emoji                  ; face without mouth              
                               # E1.0   [1] (😶)
-1F637..1F640  ; Basic_Emoji                  ; face with medical mask          
                               # E0.6  [10] (😷..🙀)
-1F641..1F644  ; Basic_Emoji                  ; slightly frowning face          
                               # E1.0   [4] (🙁..🙄)
-1F645..1F64F  ; Basic_Emoji                  ; person gesturing NO             
                               # E0.6  [11] (🙅..🙏)
+1F637..1F640  ; Basic_Emoji                  ; face with medical mask..weary 
cat                              # E0.6  [10] (😷..🙀)
+1F641..1F644  ; Basic_Emoji                  ; slightly frowning face..face 
with rolling eyes                 # E1.0   [4] (🙁..🙄)
+1F645..1F64F  ; Basic_Emoji                  ; person gesturing NO..folded 
hands                              # E0.6  [11] (🙅..🙏)
 1F680         ; Basic_Emoji                  ; rocket                          
                               # E0.6   [1] (🚀)
-1F681..1F682  ; Basic_Emoji                  ; helicopter                      
                               # E1.0   [2] (🚁..🚂)
-1F683..1F685  ; Basic_Emoji                  ; railway car                     
                               # E0.6   [3] (🚃..🚅)
+1F681..1F682  ; Basic_Emoji                  ; helicopter..locomotive          
                               # E1.0   [2] (🚁..🚂)
+1F683..1F685  ; Basic_Emoji                  ; railway car..bullet train       
                               # E0.6   [3] (🚃..🚅)
 1F686         ; Basic_Emoji                  ; train                           
                               # E1.0   [1] (🚆)
 1F687         ; Basic_Emoji                  ; metro                           
                               # E0.6   [1] (🚇)
 1F688         ; Basic_Emoji                  ; light rail                      
                               # E1.0   [1] (🚈)
 1F689         ; Basic_Emoji                  ; station                         
                               # E0.6   [1] (🚉)
-1F68A..1F68B  ; Basic_Emoji                  ; tram                            
                               # E1.0   [2] (🚊..🚋)
+1F68A..1F68B  ; Basic_Emoji                  ; tram..tram car                  
                               # E1.0   [2] (🚊..🚋)
 1F68C         ; Basic_Emoji                  ; bus                             
                               # E0.6   [1] (🚌)
 1F68D         ; Basic_Emoji                  ; oncoming bus                    
                               # E0.7   [1] (🚍)
 1F68E         ; Basic_Emoji                  ; trolleybus                      
                               # E1.0   [1] (🚎)
 1F68F         ; Basic_Emoji                  ; bus stop                        
                               # E0.6   [1] (🚏)
 1F690         ; Basic_Emoji                  ; minibus                         
                               # E1.0   [1] (🚐)
-1F691..1F693  ; Basic_Emoji                  ; ambulance                       
                               # E0.6   [3] (🚑..🚓)
+1F691..1F693  ; Basic_Emoji                  ; ambulance..police car           
                               # E0.6   [3] (🚑..🚓)
 1F694         ; Basic_Emoji                  ; oncoming police car             
                               # E0.7   [1] (🚔)
 1F695         ; Basic_Emoji                  ; taxi                            
                               # E0.6   [1] (🚕)
 1F696         ; Basic_Emoji                  ; oncoming taxi                   
                               # E1.0   [1] (🚖)
 1F697         ; Basic_Emoji                  ; automobile                      
                               # E0.6   [1] (🚗)
 1F698         ; Basic_Emoji                  ; oncoming automobile             
                               # E0.7   [1] (🚘)
-1F699..1F69A  ; Basic_Emoji                  ; sport utility vehicle           
                               # E0.6   [2] (🚙..🚚)
-1F69B..1F6A1  ; Basic_Emoji                  ; articulated lorry               
                               # E1.0   [7] (🚛..🚡)
+1F699..1F69A  ; Basic_Emoji                  ; sport utility vehicle..delivery 
truck                          # E0.6   [2] (🚙..🚚)
+1F69B..1F6A1  ; Basic_Emoji                  ; articulated lorry..aerial 
tramway                              # E1.0   [7] (🚛..🚡)
 1F6A2         ; Basic_Emoji                  ; ship                            
                               # E0.6   [1] (🚢)
 1F6A3         ; Basic_Emoji                  ; person rowing boat              
                               # E1.0   [1] (🚣)
-1F6A4..1F6A5  ; Basic_Emoji                  ; speedboat                       
                               # E0.6   [2] (🚤..🚥)
+1F6A4..1F6A5  ; Basic_Emoji                  ; speedboat..horizontal traffic 
light                            # E0.6   [2] (🚤..🚥)
 1F6A6         ; Basic_Emoji                  ; vertical traffic light          
                               # E1.0   [1] (🚦)
-1F6A7..1F6AD  ; Basic_Emoji                  ; construction                    
                               # E0.6   [7] (🚧..🚭)
-1F6AE..1F6B1  ; Basic_Emoji                  ; litter in bin sign              
                               # E1.0   [4] (🚮..🚱)
+1F6A7..1F6AD  ; Basic_Emoji                  ; construction..no smoking        
                               # E0.6   [7] (🚧..🚭)
+1F6AE..1F6B1  ; Basic_Emoji                  ; litter in bin sign..non-potable 
water                          # E1.0   [4] (🚮..🚱)
 1F6B2         ; Basic_Emoji                  ; bicycle                         
                               # E0.6   [1] (🚲)
-1F6B3..1F6B5  ; Basic_Emoji                  ; no bicycles                     
                               # E1.0   [3] (🚳..🚵)
+1F6B3..1F6B5  ; Basic_Emoji                  ; no bicycles..person mountain 
biking                            # E1.0   [3] (🚳..🚵)
 1F6B6         ; Basic_Emoji                  ; person walking                  
                               # E0.6   [1] (🚶)
-1F6B7..1F6B8  ; Basic_Emoji                  ; no pedestrians                  
                               # E1.0   [2] (🚷..🚸)
-1F6B9..1F6BE  ; Basic_Emoji                  ; men’s room                      
                               # E0.6   [6] (🚹..🚾)
+1F6B7..1F6B8  ; Basic_Emoji                  ; no pedestrians..children 
crossing                              # E1.0   [2] (🚷..🚸)
+1F6B9..1F6BE  ; Basic_Emoji                  ; men’s room..water closet        
                               # E0.6   [6] (🚹..🚾)
 1F6BF         ; Basic_Emoji                  ; shower                          
                               # E1.0   [1] (🚿)
 1F6C0         ; Basic_Emoji                  ; person taking bath              
                               # E0.6   [1] (🛀)
-1F6C1..1F6C5  ; Basic_Emoji                  ; bathtub                         
                               # E1.0   [5] (🛁..🛅)
+1F6C1..1F6C5  ; Basic_Emoji                  ; bathtub..left luggage           
                               # E1.0   [5] (🛁..🛅)
 1F6CC         ; Basic_Emoji                  ; person in bed                   
                               # E1.0   [1] (🛌)
 1F6D0         ; Basic_Emoji                  ; place of worship                
                               # E1.0   [1] (🛐)
-1F6D1..1F6D2  ; Basic_Emoji                  ; stop sign                       
                               # E3.0   [2] (🛑..🛒)
+1F6D1..1F6D2  ; Basic_Emoji                  ; stop sign..shopping cart        
                               # E3.0   [2] (🛑..🛒)
 1F6D5         ; Basic_Emoji                  ; hindu temple                    
                               # E12.0  [1] (🛕)
-1F6D6..1F6D7  ; Basic_Emoji                  ; hut                             
                               # E13.0  [2] (🛖..🛗)
-1F6DD..1F6DF  ; Basic_Emoji                  ; playground slide                
                               # E14.0  [3] (🛝..🛟)
-1F6EB..1F6EC  ; Basic_Emoji                  ; airplane departure              
                               # E1.0   [2] (🛫..🛬)
-1F6F4..1F6F6  ; Basic_Emoji                  ; kick scooter                    
                               # E3.0   [3] (🛴..🛶)
-1F6F7..1F6F8  ; Basic_Emoji                  ; sled                            
                               # E5.0   [2] (🛷..🛸)
+1F6D6..1F6D7  ; Basic_Emoji                  ; hut..elevator                   
                               # E13.0  [2] (🛖..🛗)
+1F6DC         ; Basic_Emoji                  ; wireless                        
                               # E15.0  [1] (🛜)
+1F6DD..1F6DF  ; Basic_Emoji                  ; playground slide..ring buoy     
                               # E14.0  [3] (🛝..🛟)
+1F6EB..1F6EC  ; Basic_Emoji                  ; airplane departure..airplane 
arrival                           # E1.0   [2] (🛫..🛬)
+1F6F4..1F6F6  ; Basic_Emoji                  ; kick scooter..canoe             
                               # E3.0   [3] (🛴..🛶)
+1F6F7..1F6F8  ; Basic_Emoji                  ; sled..flying saucer             
                               # E5.0   [2] (🛷..🛸)
 1F6F9         ; Basic_Emoji                  ; skateboard                      
                               # E11.0  [1] (🛹)
 1F6FA         ; Basic_Emoji                  ; auto rickshaw                   
                               # E12.0  [1] (🛺)
-1F6FB..1F6FC  ; Basic_Emoji                  ; pickup truck                    
                               # E13.0  [2] (🛻..🛼)
-1F7E0..1F7EB  ; Basic_Emoji                  ; orange circle                   
                               # E12.0 [12] (🟠..🟫)
+1F6FB..1F6FC  ; Basic_Emoji                  ; pickup truck..roller skate      
                               # E13.0  [2] (🛻..🛼)
+1F7E0..1F7EB  ; Basic_Emoji                  ; orange circle..brown square     
                               # E12.0 [12] (🟠..🟫)
 1F7F0         ; Basic_Emoji                  ; heavy equals sign               
                               # E14.0  [1] (🟰)
 1F90C         ; Basic_Emoji                  ; pinched fingers                 
                               # E13.0  [1] (🤌)
-1F90D..1F90F  ; Basic_Emoji                  ; white heart                     
                               # E12.0  [3] (🤍..🤏)
-1F910..1F918  ; Basic_Emoji                  ; zipper-mouth face               
                               # E1.0   [9] (🤐..🤘)
-1F919..1F91E  ; Basic_Emoji                  ; call me hand                    
                               # E3.0   [6] (🤙..🤞)
+1F90D..1F90F  ; Basic_Emoji                  ; white heart..pinching hand      
                               # E12.0  [3] (🤍..🤏)
+1F910..1F918  ; Basic_Emoji                  ; zipper-mouth face..sign of the 
horns                           # E1.0   [9] (🤐..🤘)
+1F919..1F91E  ; Basic_Emoji                  ; call me hand..crossed fingers   
                               # E3.0   [6] (🤙..🤞)
 1F91F         ; Basic_Emoji                  ; love-you gesture                
                               # E5.0   [1] (🤟)
-1F920..1F927  ; Basic_Emoji                  ; cowboy hat face                 
                               # E3.0   [8] (🤠..🤧)
-1F928..1F92F  ; Basic_Emoji                  ; face with raised eyebrow        
                               # E5.0   [8] (🤨..🤯)
+1F920..1F927  ; Basic_Emoji                  ; cowboy hat face..sneezing face  
                               # E3.0   [8] (🤠..🤧)
+1F928..1F92F  ; Basic_Emoji                  ; face with raised 
eyebrow..exploding head                       # E5.0   [8] (🤨..🤯)
 1F930         ; Basic_Emoji                  ; pregnant woman                  
                               # E3.0   [1] (🤰)
-1F931..1F932  ; Basic_Emoji                  ; breast-feeding                  
                               # E5.0   [2] (🤱..🤲)
-1F933..1F93A  ; Basic_Emoji                  ; selfie                          
                               # E3.0   [8] (🤳..🤺)
-1F93C..1F93E  ; Basic_Emoji                  ; people wrestling                
                               # E3.0   [3] (🤼..🤾)
+1F931..1F932  ; Basic_Emoji                  ; breast-feeding..palms up 
together                              # E5.0   [2] (🤱..🤲)
+1F933..1F93A  ; Basic_Emoji                  ; selfie..person fencing          
                               # E3.0   [8] (🤳..🤺)
+1F93C..1F93E  ; Basic_Emoji                  ; people wrestling..person 
playing handball                      # E3.0   [3] (🤼..🤾)
 1F93F         ; Basic_Emoji                  ; diving mask                     
                               # E12.0  [1] (🤿)
-1F940..1F945  ; Basic_Emoji                  ; wilted flower                   
                               # E3.0   [6] (🥀..🥅)
-1F947..1F94B  ; Basic_Emoji                  ; 1st place medal                 
                               # E3.0   [5] (🥇..🥋)
+1F940..1F945  ; Basic_Emoji                  ; wilted flower..goal net         
                               # E3.0   [6] (🥀..🥅)
+1F947..1F94B  ; Basic_Emoji                  ; 1st place medal..martial arts 
uniform                          # E3.0   [5] (🥇..🥋)
 1F94C         ; Basic_Emoji                  ; curling stone                   
                               # E5.0   [1] (🥌)
-1F94D..1F94F  ; Basic_Emoji                  ; lacrosse                        
                               # E11.0  [3] (🥍..🥏)
-1F950..1F95E  ; Basic_Emoji                  ; croissant                       
                               # E3.0  [15] (🥐..🥞)
-1F95F..1F96B  ; Basic_Emoji                  ; dumpling                        
                               # E5.0  [13] (🥟..🥫)
-1F96C..1F970  ; Basic_Emoji                  ; leafy green                     
                               # E11.0  [5] (🥬..🥰)
+1F94D..1F94F  ; Basic_Emoji                  ; lacrosse..flying disc           
                               # E11.0  [3] (🥍..🥏)
+1F950..1F95E  ; Basic_Emoji                  ; croissant..pancakes             
                               # E3.0  [15] (🥐..🥞)
+1F95F..1F96B  ; Basic_Emoji                  ; dumpling..canned food           
                               # E5.0  [13] (🥟..🥫)
+1F96C..1F970  ; Basic_Emoji                  ; leafy green..smiling face with 
hearts                          # E11.0  [5] (🥬..🥰)
 1F971         ; Basic_Emoji                  ; yawning face                    
                               # E12.0  [1] (🥱)
 1F972         ; Basic_Emoji                  ; smiling face with tear          
                               # E13.0  [1] (🥲)
-1F973..1F976  ; Basic_Emoji                  ; partying face                   
                               # E11.0  [4] (🥳..🥶)
-1F977..1F978  ; Basic_Emoji                  ; ninja                           
                               # E13.0  [2] (🥷..🥸)
+1F973..1F976  ; Basic_Emoji                  ; partying face..cold face        
                               # E11.0  [4] (🥳..🥶)
+1F977..1F978  ; Basic_Emoji                  ; ninja..disguised face           
                               # E13.0  [2] (🥷..🥸)
 1F979         ; Basic_Emoji                  ; face holding back tears         
                               # E14.0  [1] (🥹)
 1F97A         ; Basic_Emoji                  ; pleading face                   
                               # E11.0  [1] (🥺)
 1F97B         ; Basic_Emoji                  ; sari                            
                               # E12.0  [1] (🥻)
-1F97C..1F97F  ; Basic_Emoji                  ; lab coat                        
                               # E11.0  [4] (🥼..🥿)
-1F980..1F984  ; Basic_Emoji                  ; crab                            
                               # E1.0   [5] (🦀..🦄)
-1F985..1F991  ; Basic_Emoji                  ; eagle                           
                               # E3.0  [13] (🦅..🦑)
-1F992..1F997  ; Basic_Emoji                  ; giraffe                         
                               # E5.0   [6] (🦒..🦗)
-1F998..1F9A2  ; Basic_Emoji                  ; kangaroo                        
                               # E11.0 [11] (🦘..🦢)
-1F9A3..1F9A4  ; Basic_Emoji                  ; mammoth                         
                               # E13.0  [2] (🦣..🦤)
-1F9A5..1F9AA  ; Basic_Emoji                  ; sloth                           
                               # E12.0  [6] (🦥..🦪)
-1F9AB..1F9AD  ; Basic_Emoji                  ; beaver                          
                               # E13.0  [3] (🦫..🦭)
-1F9AE..1F9AF  ; Basic_Emoji                  ; guide dog                       
                               # E12.0  [2] (🦮..🦯)
-1F9B0..1F9B9  ; Basic_Emoji                  ; red hair                        
                               # E11.0 [10] (🦰..🦹)
-1F9BA..1F9BF  ; Basic_Emoji                  ; safety vest                     
                               # E12.0  [6] (🦺..🦿)
+1F97C..1F97F  ; Basic_Emoji                  ; lab coat..flat shoe             
                               # E11.0  [4] (🥼..🥿)
+1F980..1F984  ; Basic_Emoji                  ; crab..unicorn                   
                               # E1.0   [5] (🦀..🦄)
+1F985..1F991  ; Basic_Emoji                  ; eagle..squid                    
                               # E3.0  [13] (🦅..🦑)
+1F992..1F997  ; Basic_Emoji                  ; giraffe..cricket                
                               # E5.0   [6] (🦒..🦗)
+1F998..1F9A2  ; Basic_Emoji                  ; kangaroo..swan                  
                               # E11.0 [11] (🦘..🦢)
+1F9A3..1F9A4  ; Basic_Emoji                  ; mammoth..dodo                   
                               # E13.0  [2] (🦣..🦤)
+1F9A5..1F9AA  ; Basic_Emoji                  ; sloth..oyster                   
                               # E12.0  [6] (🦥..🦪)
+1F9AB..1F9AD  ; Basic_Emoji                  ; beaver..seal                    
                               # E13.0  [3] (🦫..🦭)
+1F9AE..1F9AF  ; Basic_Emoji                  ; guide dog..white cane           
                               # E12.0  [2] (🦮..🦯)
+1F9B0..1F9B9  ; Basic_Emoji                  ; red hair..supervillain          
                               # E11.0 [10] (🦰..🦹)
+1F9BA..1F9BF  ; Basic_Emoji                  ; safety vest..mechanical leg     
                               # E12.0  [6] (🦺..🦿)
 1F9C0         ; Basic_Emoji                  ; cheese wedge                    
                               # E1.0   [1] (🧀)
-1F9C1..1F9C2  ; Basic_Emoji                  ; cupcake                         
                               # E11.0  [2] (🧁..🧂)
-1F9C3..1F9CA  ; Basic_Emoji                  ; beverage box                    
                               # E12.0  [8] (🧃..🧊)
+1F9C1..1F9C2  ; Basic_Emoji                  ; cupcake..salt                   
                               # E11.0  [2] (🧁..🧂)
+1F9C3..1F9CA  ; Basic_Emoji                  ; beverage box..ice               
                               # E12.0  [8] (🧃..🧊)
 1F9CB         ; Basic_Emoji                  ; bubble tea                      
                               # E13.0  [1] (🧋)
 1F9CC         ; Basic_Emoji                  ; troll                           
                               # E14.0  [1] (🧌)
-1F9CD..1F9CF  ; Basic_Emoji                  ; person standing                 
                               # E12.0  [3] (🧍..🧏)
-1F9D0..1F9E6  ; Basic_Emoji                  ; face with monocle               
                               # E5.0  [23] (🧐..🧦)
-1F9E7..1F9FF  ; Basic_Emoji                  ; red envelope                    
                               # E11.0 [25] (🧧..🧿)
-1FA70..1FA73  ; Basic_Emoji                  ; ballet shoes                    
                               # E12.0  [4] (🩰..🩳)
+1F9CD..1F9CF  ; Basic_Emoji                  ; person standing..deaf person    
                               # E12.0  [3] (🧍..🧏)
+1F9D0..1F9E6  ; Basic_Emoji                  ; face with monocle..socks        
                               # E5.0  [23] (🧐..🧦)
+1F9E7..1F9FF  ; Basic_Emoji                  ; red envelope..nazar amulet      
                               # E11.0 [25] (🧧..🧿)
+1FA70..1FA73  ; Basic_Emoji                  ; ballet shoes..shorts            
                               # E12.0  [4] (🩰..🩳)
 1FA74         ; Basic_Emoji                  ; thong sandal                    
                               # E13.0  [1] (🩴)
-1FA78..1FA7A  ; Basic_Emoji                  ; drop of blood                   
                               # E12.0  [3] (🩸..🩺)
-1FA7B..1FA7C  ; Basic_Emoji                  ; x-ray                           
                               # E14.0  [2] (🩻..🩼)
-1FA80..1FA82  ; Basic_Emoji                  ; yo-yo                           
                               # E12.0  [3] (🪀..🪂)
-1FA83..1FA86  ; Basic_Emoji                  ; boomerang                       
                               # E13.0  [4] (🪃..🪆)
-1FA90..1FA95  ; Basic_Emoji                  ; ringed planet                   
                               # E12.0  [6] (🪐..🪕)
-1FA96..1FAA8  ; Basic_Emoji                  ; military helmet                 
                               # E13.0 [19] (🪖..🪨)
-1FAA9..1FAAC  ; Basic_Emoji                  ; mirror ball                     
                               # E14.0  [4] (🪩..🪬)
-1FAB0..1FAB6  ; Basic_Emoji                  ; fly                             
                               # E13.0  [7] (🪰..🪶)
-1FAB7..1FABA  ; Basic_Emoji                  ; lotus                           
                               # E14.0  [4] (🪷..🪺)
-1FAC0..1FAC2  ; Basic_Emoji                  ; anatomical heart                
                               # E13.0  [3] (🫀..🫂)
-1FAC3..1FAC5  ; Basic_Emoji                  ; pregnant man                    
                               # E14.0  [3] (🫃..🫅)
-1FAD0..1FAD6  ; Basic_Emoji                  ; blueberries                     
                               # E13.0  [7] (🫐..🫖)
-1FAD7..1FAD9  ; Basic_Emoji                  ; pouring liquid                  
                               # E14.0  [3] (🫗..🫙)
-1FAE0..1FAE7  ; Basic_Emoji                  ; melting face                    
                               # E14.0  [8] (🫠..🫧)
-1FAF0..1FAF6  ; Basic_Emoji                  ; hand with index finger and 
thumb crossed                       # E14.0  [7] (🫰..🫶)
+1FA75..1FA77  ; Basic_Emoji                  ; light blue heart..pink heart    
                               # E15.0  [3] (🩵..🩷)
+1FA78..1FA7A  ; Basic_Emoji                  ; drop of blood..stethoscope      
                               # E12.0  [3] (🩸..🩺)
+1FA7B..1FA7C  ; Basic_Emoji                  ; x-ray..crutch                   
                               # E14.0  [2] (🩻..🩼)
+1FA80..1FA82  ; Basic_Emoji                  ; yo-yo..parachute                
                               # E12.0  [3] (🪀..🪂)
+1FA83..1FA86  ; Basic_Emoji                  ; boomerang..nesting dolls        
                               # E13.0  [4] (🪃..🪆)
+1FA87..1FA88  ; Basic_Emoji                  ; maracas..flute                  
                               # E15.0  [2] (🪇..🪈)
+1FA90..1FA95  ; Basic_Emoji                  ; ringed planet..banjo            
                               # E12.0  [6] (🪐..🪕)
+1FA96..1FAA8  ; Basic_Emoji                  ; military helmet..rock           
                               # E13.0 [19] (🪖..🪨)
+1FAA9..1FAAC  ; Basic_Emoji                  ; mirror ball..hamsa              
                               # E14.0  [4] (🪩..🪬)
+1FAAD..1FAAF  ; Basic_Emoji                  ; folding hand fan..khanda        
                               # E15.0  [3] (🪭..🪯)
+1FAB0..1FAB6  ; Basic_Emoji                  ; fly..feather                    
                               # E13.0  [7] (🪰..🪶)
+1FAB7..1FABA  ; Basic_Emoji                  ; lotus..nest with eggs           
                               # E14.0  [4] (🪷..🪺)
+1FABB..1FABD  ; Basic_Emoji                  ; hyacinth..wing                  
                               # E15.0  [3] (🪻..🪽)
+1FABF         ; Basic_Emoji                  ; goose                           
                               # E15.0  [1] (🪿)
+1FAC0..1FAC2  ; Basic_Emoji                  ; anatomical heart..people 
hugging                               # E13.0  [3] (🫀..🫂)
+1FAC3..1FAC5  ; Basic_Emoji                  ; pregnant man..person with crown 
                               # E14.0  [3] (🫃..🫅)
+1FACE..1FACF  ; Basic_Emoji                  ; moose..donkey                   
                               # E15.0  [2] (🫎..🫏)
+1FAD0..1FAD6  ; Basic_Emoji                  ; blueberries..teapot             
                               # E13.0  [7] (🫐..🫖)
+1FAD7..1FAD9  ; Basic_Emoji                  ; pouring liquid..jar             
                               # E14.0  [3] (🫗..🫙)
+1FADA..1FADB  ; Basic_Emoji                  ; ginger root..pea pod            
                               # E15.0  [2] (🫚..🫛)
+1FAE0..1FAE7  ; Basic_Emoji                  ; melting face..bubbles           
                               # E14.0  [8] (🫠..🫧)
+1FAE8         ; Basic_Emoji                  ; shaking face                    
                               # E15.0  [1] (🫨)
+1FAF0..1FAF6  ; Basic_Emoji                  ; hand with index finger and 
thumb crossed..heart hands          # E14.0  [7] (🫰..🫶)
+1FAF7..1FAF8  ; Basic_Emoji                  ; leftwards pushing 
hand..rightwards pushing hand                # E15.0  [2] (🫷..🫸)
 00A9 FE0F     ; Basic_Emoji                  ; copyright                       
                               # E0.6   [1] (©️)
 00AE FE0F     ; Basic_Emoji                  ; registered                      
                               # E0.6   [1] (®️)
 203C FE0F     ; Basic_Emoji                  ; double exclamation mark         
                               # E0.6   [1] (‼️)
@@ -517,12 +528,13 @@
 1F6F0 FE0F    ; Basic_Emoji                  ; satellite                       
                               # E0.7   [1] (🛰️)
 1F6F3 FE0F    ; Basic_Emoji                  ; passenger ship                  
                               # E0.7   [1] (🛳️)
 
-# Total elements: 1366
+# Total elements: 1386
 
 # ================================================
 
 # Emoji_Keycap_Sequence
 
+
 0023 FE0F 20E3; Emoji_Keycap_Sequence        ; keycap: \x{23}                  
                               # E0.6   [1] (#️⃣)
 002A FE0F 20E3; Emoji_Keycap_Sequence        ; keycap: *                       
                               # E2.0   [1] (*️⃣)
 0030 FE0F 20E3; Emoji_Keycap_Sequence        ; keycap: 0                       
                               # E0.6   [1] (0️⃣)
@@ -543,6 +555,7 @@
 # RGI_Emoji_Flag_Sequence: This list does not include deprecated or 
macroregion flags, except for UN and EU.
 # See Annex B of TR51 for more information.
 
+
 1F1E6 1F1E8   ; RGI_Emoji_Flag_Sequence      ; flag: Ascension Island          
                               # E2.0   [1] (🇦🇨)
 1F1E6 1F1E9   ; RGI_Emoji_Flag_Sequence      ; flag: Andorra                   
                               # E2.0   [1] (🇦🇩)
 1F1E6 1F1EA   ; RGI_Emoji_Flag_Sequence      ; flag: United Arab Emirates      
                               # E2.0   [1] (🇦🇪)
@@ -808,6 +821,7 @@
 
 # RGI_Emoji_Tag_Sequence: See Annex C of TR51 for more information.
 
+
 1F3F4 E0067 E0062 E0065 E006E E0067 E007F; RGI_Emoji_Tag_Sequence; flag: 
England                              # E5.0   [1] (🏴󠁧󠁢󠁥󠁮󠁧󠁿)
 1F3F4 E0067 E0062 E0073 E0063 E0074 E007F; RGI_Emoji_Tag_Sequence; flag: 
Scotland                             # E5.0   [1] (🏴󠁧󠁢󠁳󠁣󠁴󠁿)
 1F3F4 E0067 E0062 E0077 E006C E0073 E007F; RGI_Emoji_Tag_Sequence; flag: Wales 
                               # E5.0   [1] (🏴󠁧󠁢󠁷󠁬󠁳󠁿)
@@ -818,6 +832,7 @@
 
 # RGI_Emoji_Modifier_Sequence
 
+
 261D 1F3FB    ; RGI_Emoji_Modifier_Sequence  ; index pointing up: light skin 
tone                             # E1.0   [1] (☝🏻)
 261D 1F3FC    ; RGI_Emoji_Modifier_Sequence  ; index pointing up: medium-light 
skin tone                      # E1.0   [1] (☝🏼)
 261D 1F3FD    ; RGI_Emoji_Modifier_Sequence  ; index pointing up: medium skin 
tone                            # E1.0   [1] (☝🏽)
@@ -1223,11 +1238,11 @@
 1F91C 1F3FD   ; RGI_Emoji_Modifier_Sequence  ; right-facing fist: medium skin 
tone                            # E3.0   [1] (🤜🏽)
 1F91C 1F3FE   ; RGI_Emoji_Modifier_Sequence  ; right-facing fist: medium-dark 
skin tone                       # E3.0   [1] (🤜🏾)
 1F91C 1F3FF   ; RGI_Emoji_Modifier_Sequence  ; right-facing fist: dark skin 
tone                              # E3.0   [1] (🤜🏿)
-1F91D 1F3FB   ; RGI_Emoji_Modifier_Sequence  ; handshake: light skin tone      
                               # E3.0   [1] (🤝🏻)
-1F91D 1F3FC   ; RGI_Emoji_Modifier_Sequence  ; handshake: medium-light skin 
tone                              # E3.0   [1] (🤝🏼)
-1F91D 1F3FD   ; RGI_Emoji_Modifier_Sequence  ; handshake: medium skin tone     
                               # E3.0   [1] (🤝🏽)
-1F91D 1F3FE   ; RGI_Emoji_Modifier_Sequence  ; handshake: medium-dark skin 
tone                               # E3.0   [1] (🤝🏾)
-1F91D 1F3FF   ; RGI_Emoji_Modifier_Sequence  ; handshake: dark skin tone       
                               # E3.0   [1] (🤝🏿)
+1F91D 1F3FB   ; RGI_Emoji_Modifier_Sequence  ; handshake: light skin tone      
                               # E14.0  [1] (🤝🏻)
+1F91D 1F3FC   ; RGI_Emoji_Modifier_Sequence  ; handshake: medium-light skin 
tone                              # E14.0  [1] (🤝🏼)
+1F91D 1F3FD   ; RGI_Emoji_Modifier_Sequence  ; handshake: medium skin tone     
                               # E14.0  [1] (🤝🏽)
+1F91D 1F3FE   ; RGI_Emoji_Modifier_Sequence  ; handshake: medium-dark skin 
tone                               # E14.0  [1] (🤝🏾)
+1F91D 1F3FF   ; RGI_Emoji_Modifier_Sequence  ; handshake: dark skin tone       
                               # E14.0  [1] (🤝🏿)
 1F91E 1F3FB   ; RGI_Emoji_Modifier_Sequence  ; crossed fingers: light skin 
tone                               # E3.0   [1] (🤞🏻)
 1F91E 1F3FC   ; RGI_Emoji_Modifier_Sequence  ; crossed fingers: medium-light 
skin tone                        # E3.0   [1] (🤞🏼)
 1F91E 1F3FD   ; RGI_Emoji_Modifier_Sequence  ; crossed fingers: medium skin 
tone                              # E3.0   [1] (🤞🏽)
@@ -1463,7 +1478,17 @@
 1FAF6 1F3FD   ; RGI_Emoji_Modifier_Sequence  ; heart hands: medium skin tone   
                               # E14.0  [1] (🫶🏽)
 1FAF6 1F3FE   ; RGI_Emoji_Modifier_Sequence  ; heart hands: medium-dark skin 
tone                             # E14.0  [1] (🫶🏾)
 1FAF6 1F3FF   ; RGI_Emoji_Modifier_Sequence  ; heart hands: dark skin tone     
                               # E14.0  [1] (🫶🏿)
+1FAF7 1F3FB   ; RGI_Emoji_Modifier_Sequence  ; leftwards pushing hand: light 
skin tone                        # E15.0  [1] (🫷🏻)
+1FAF7 1F3FC   ; RGI_Emoji_Modifier_Sequence  ; leftwards pushing hand: 
medium-light skin tone                 # E15.0  [1] (🫷🏼)
+1FAF7 1F3FD   ; RGI_Emoji_Modifier_Sequence  ; leftwards pushing hand: medium 
skin tone                       # E15.0  [1] (🫷🏽)
+1FAF7 1F3FE   ; RGI_Emoji_Modifier_Sequence  ; leftwards pushing hand: 
medium-dark skin tone                  # E15.0  [1] (🫷🏾)
+1FAF7 1F3FF   ; RGI_Emoji_Modifier_Sequence  ; leftwards pushing hand: dark 
skin tone                         # E15.0  [1] (🫷🏿)
+1FAF8 1F3FB   ; RGI_Emoji_Modifier_Sequence  ; rightwards pushing hand: light 
skin tone                       # E15.0  [1] (🫸🏻)
+1FAF8 1F3FC   ; RGI_Emoji_Modifier_Sequence  ; rightwards pushing hand: 
medium-light skin tone                # E15.0  [1] (🫸🏼)
+1FAF8 1F3FD   ; RGI_Emoji_Modifier_Sequence  ; rightwards pushing hand: medium 
skin tone                      # E15.0  [1] (🫸🏽)
+1FAF8 1F3FE   ; RGI_Emoji_Modifier_Sequence  ; rightwards pushing hand: 
medium-dark skin tone                 # E15.0  [1] (🫸🏾)
+1FAF8 1F3FF   ; RGI_Emoji_Modifier_Sequence  ; rightwards pushing hand: dark 
skin tone                        # E15.0  [1] (🫸🏿)
 
-# Total elements: 645
+# Total elements: 655
 
 #EOF
diff --git a/admin/unidata/emoji-test.txt b/admin/unidata/emoji-test.txt
index 42e6210cd2..bc8b52c2fb 100644
--- a/admin/unidata/emoji-test.txt
+++ b/admin/unidata/emoji-test.txt
@@ -1,13 +1,13 @@
 # emoji-test.txt
-# Date: 2021-08-26, 17:22:23 GMT
-# © 2021 Unicode®, Inc.
+# Date: 2022-08-12, 20:24:39 GMT
+# © 2022 Unicode®, Inc.
 # Unicode and the Unicode Logo are registered trademarks of Unicode, Inc. in 
the U.S. and other countries.
-# For terms of use, see http://www.unicode.org/terms_of_use.html
+# For terms of use, see https://www.unicode.org/terms_of_use.html
 #
 # Emoji Keyboard/Display Test Data for UTS #51
-# Version: 14.0
+# Version: 15.0
 #
-# For documentation and usage, see http://www.unicode.org/reports/tr51
+# For documentation and usage, see https://www.unicode.org/reports/tr51
 #
 # This file provides data for testing which emoji forms should be in keyboards 
and which should also be displayed/processed.
 # Format: code points; status # emoji name
@@ -92,6 +92,7 @@
 1F62C                                                  ; fully-qualified     # 
😬 E1.0 grimacing face
 1F62E 200D 1F4A8                                       ; fully-qualified     # 
😮‍💨 E13.1 face exhaling
 1F925                                                  ; fully-qualified     # 
🤥 E3.0 lying face
+1FAE8                                                  ; fully-qualified     # 
🫨 E15.0 shaking face
 
 # subgroup: face-sleepy
 1F60C                                                  ; fully-qualified     # 
😌 E0.6 relieved face
@@ -155,7 +156,7 @@
 
 # subgroup: face-negative
 1F624                                                  ; fully-qualified     # 
😤 E0.6 face with steam from nose
-1F621                                                  ; fully-qualified     # 
😡 E0.6 pouting face
+1F621                                                  ; fully-qualified     # 
😡 E0.6 enraged face
 1F620                                                  ; fully-qualified     # 
😠 E0.6 angry face
 1F92C                                                  ; fully-qualified     # 
🤬 E5.0 face with symbols on mouth
 1F608                                                  ; fully-qualified     # 
😈 E1.0 smiling face with horns
@@ -190,8 +191,7 @@
 1F649                                                  ; fully-qualified     # 
🙉 E0.6 hear-no-evil monkey
 1F64A                                                  ; fully-qualified     # 
🙊 E0.6 speak-no-evil monkey
 
-# subgroup: emotion
-1F48B                                                  ; fully-qualified     # 
💋 E0.6 kiss mark
+# subgroup: heart
 1F48C                                                  ; fully-qualified     # 
💌 E0.6 love letter
 1F498                                                  ; fully-qualified     # 
💘 E0.6 heart with arrow
 1F49D                                                  ; fully-qualified     # 
💝 E0.6 heart with ribbon
@@ -210,14 +210,20 @@
 2764 200D 1FA79                                        ; unqualified         # 
❤‍🩹 E13.1 mending heart
 2764 FE0F                                              ; fully-qualified     # 
❤️ E0.6 red heart
 2764                                                   ; unqualified         # 
❤ E0.6 red heart
+1FA77                                                  ; fully-qualified     # 
🩷 E15.0 pink heart
 1F9E1                                                  ; fully-qualified     # 
🧡 E5.0 orange heart
 1F49B                                                  ; fully-qualified     # 
💛 E0.6 yellow heart
 1F49A                                                  ; fully-qualified     # 
💚 E0.6 green heart
 1F499                                                  ; fully-qualified     # 
💙 E0.6 blue heart
+1FA75                                                  ; fully-qualified     # 
🩵 E15.0 light blue heart
 1F49C                                                  ; fully-qualified     # 
💜 E0.6 purple heart
 1F90E                                                  ; fully-qualified     # 
🤎 E12.0 brown heart
 1F5A4                                                  ; fully-qualified     # 
🖤 E3.0 black heart
+1FA76                                                  ; fully-qualified     # 
🩶 E15.0 grey heart
 1F90D                                                  ; fully-qualified     # 
🤍 E12.0 white heart
+
+# subgroup: emotion
+1F48B                                                  ; fully-qualified     # 
💋 E0.6 kiss mark
 1F4AF                                                  ; fully-qualified     # 
💯 E0.6 hundred points
 1F4A2                                                  ; fully-qualified     # 
💢 E0.6 anger symbol
 1F4A5                                                  ; fully-qualified     # 
💥 E0.6 collision
@@ -226,21 +232,20 @@
 1F4A8                                                  ; fully-qualified     # 
💨 E0.6 dashing away
 1F573 FE0F                                             ; fully-qualified     # 
🕳️ E0.7 hole
 1F573                                                  ; unqualified         # 
🕳 E0.7 hole
-1F4A3                                                  ; fully-qualified     # 
💣 E0.6 bomb
 1F4AC                                                  ; fully-qualified     # 
💬 E0.6 speech balloon
 1F441 FE0F 200D 1F5E8 FE0F                             ; fully-qualified     # 
👁️‍🗨️ E2.0 eye in speech bubble
 1F441 200D 1F5E8 FE0F                                  ; unqualified         # 
👁‍🗨️ E2.0 eye in speech bubble
-1F441 FE0F 200D 1F5E8                                  ; unqualified         # 
👁️‍🗨 E2.0 eye in speech bubble
+1F441 FE0F 200D 1F5E8                                  ; minimally-qualified # 
👁️‍🗨 E2.0 eye in speech bubble
 1F441 200D 1F5E8                                       ; unqualified         # 
👁‍🗨 E2.0 eye in speech bubble
 1F5E8 FE0F                                             ; fully-qualified     # 
🗨️ E2.0 left speech bubble
 1F5E8                                                  ; unqualified         # 
🗨 E2.0 left speech bubble
 1F5EF FE0F                                             ; fully-qualified     # 
🗯️ E0.7 right anger bubble
 1F5EF                                                  ; unqualified         # 
🗯 E0.7 right anger bubble
 1F4AD                                                  ; fully-qualified     # 
💭 E1.0 thought balloon
-1F4A4                                                  ; fully-qualified     # 
💤 E0.6 zzz
+1F4A4                                                  ; fully-qualified     # 
💤 E0.6 ZZZ
 
-# Smileys & Emotion subtotal:          177
-# Smileys & Emotion subtotal:          177     w/o modifiers
+# Smileys & Emotion subtotal:          180
+# Smileys & Emotion subtotal:          180     w/o modifiers
 
 # group: People & Body
 
@@ -300,6 +305,18 @@
 1FAF4 1F3FD                                            ; fully-qualified     # 
🫴🏽 E14.0 palm up hand: medium skin tone
 1FAF4 1F3FE                                            ; fully-qualified     # 
🫴🏾 E14.0 palm up hand: medium-dark skin tone
 1FAF4 1F3FF                                            ; fully-qualified     # 
🫴🏿 E14.0 palm up hand: dark skin tone
+1FAF7                                                  ; fully-qualified     # 
🫷 E15.0 leftwards pushing hand
+1FAF7 1F3FB                                            ; fully-qualified     # 
🫷🏻 E15.0 leftwards pushing hand: light skin tone
+1FAF7 1F3FC                                            ; fully-qualified     # 
🫷🏼 E15.0 leftwards pushing hand: medium-light skin tone
+1FAF7 1F3FD                                            ; fully-qualified     # 
🫷🏽 E15.0 leftwards pushing hand: medium skin tone
+1FAF7 1F3FE                                            ; fully-qualified     # 
🫷🏾 E15.0 leftwards pushing hand: medium-dark skin tone
+1FAF7 1F3FF                                            ; fully-qualified     # 
🫷🏿 E15.0 leftwards pushing hand: dark skin tone
+1FAF8                                                  ; fully-qualified     # 
🫸 E15.0 rightwards pushing hand
+1FAF8 1F3FB                                            ; fully-qualified     # 
🫸🏻 E15.0 rightwards pushing hand: light skin tone
+1FAF8 1F3FC                                            ; fully-qualified     # 
🫸🏼 E15.0 rightwards pushing hand: medium-light skin tone
+1FAF8 1F3FD                                            ; fully-qualified     # 
🫸🏽 E15.0 rightwards pushing hand: medium skin tone
+1FAF8 1F3FE                                            ; fully-qualified     # 
🫸🏾 E15.0 rightwards pushing hand: medium-dark skin tone
+1FAF8 1F3FF                                            ; fully-qualified     # 
🫸🏿 E15.0 rightwards pushing hand: dark skin tone
 
 # subgroup: hand-fingers-partial
 1F44C                                                  ; fully-qualified     # 
👌 E0.6 OK hand
@@ -473,11 +490,11 @@
 1F932 1F3FE                                            ; fully-qualified     # 
🤲🏾 E5.0 palms up together: medium-dark skin tone
 1F932 1F3FF                                            ; fully-qualified     # 
🤲🏿 E5.0 palms up together: dark skin tone
 1F91D                                                  ; fully-qualified     # 
🤝 E3.0 handshake
-1F91D 1F3FB                                            ; fully-qualified     # 
🤝🏻 E3.0 handshake: light skin tone
-1F91D 1F3FC                                            ; fully-qualified     # 
🤝🏼 E3.0 handshake: medium-light skin tone
-1F91D 1F3FD                                            ; fully-qualified     # 
🤝🏽 E3.0 handshake: medium skin tone
-1F91D 1F3FE                                            ; fully-qualified     # 
🤝🏾 E3.0 handshake: medium-dark skin tone
-1F91D 1F3FF                                            ; fully-qualified     # 
🤝🏿 E3.0 handshake: dark skin tone
+1F91D 1F3FB                                            ; fully-qualified     # 
🤝🏻 E14.0 handshake: light skin tone
+1F91D 1F3FC                                            ; fully-qualified     # 
🤝🏼 E14.0 handshake: medium-light skin tone
+1F91D 1F3FD                                            ; fully-qualified     # 
🤝🏽 E14.0 handshake: medium skin tone
+1F91D 1F3FE                                            ; fully-qualified     # 
🤝🏾 E14.0 handshake: medium-dark skin tone
+1F91D 1F3FF                                            ; fully-qualified     # 
🤝🏿 E14.0 handshake: dark skin tone
 1FAF1 1F3FB 200D 1FAF2 1F3FC                           ; fully-qualified     # 
🫱🏻‍🫲🏼 E14.0 handshake: light skin tone, medium-light skin tone
 1FAF1 1F3FB 200D 1FAF2 1F3FD                           ; fully-qualified     # 
🫱🏻‍🫲🏽 E14.0 handshake: light skin tone, medium skin tone
 1FAF1 1F3FB 200D 1FAF2 1F3FE                           ; fully-qualified     # 
🫱🏻‍🫲🏾 E14.0 handshake: light skin tone, medium-dark skin tone
@@ -1455,7 +1472,7 @@
 1F575 1F3FF                                            ; fully-qualified     # 
🕵🏿 E2.0 detective: dark skin tone
 1F575 FE0F 200D 2642 FE0F                              ; fully-qualified     # 
🕵️‍♂️ E4.0 man detective
 1F575 200D 2642 FE0F                                   ; unqualified         # 
🕵‍♂️ E4.0 man detective
-1F575 FE0F 200D 2642                                   ; unqualified         # 
🕵️‍♂ E4.0 man detective
+1F575 FE0F 200D 2642                                   ; minimally-qualified # 
🕵️‍♂ E4.0 man detective
 1F575 200D 2642                                        ; unqualified         # 
🕵‍♂ E4.0 man detective
 1F575 1F3FB 200D 2642 FE0F                             ; fully-qualified     # 
🕵🏻‍♂️ E4.0 man detective: light skin tone
 1F575 1F3FB 200D 2642                                  ; minimally-qualified # 
🕵🏻‍♂ E4.0 man detective: light skin tone
@@ -1469,7 +1486,7 @@
 1F575 1F3FF 200D 2642                                  ; minimally-qualified # 
🕵🏿‍♂ E4.0 man detective: dark skin tone
 1F575 FE0F 200D 2640 FE0F                              ; fully-qualified     # 
🕵️‍♀️ E4.0 woman detective
 1F575 200D 2640 FE0F                                   ; unqualified         # 
🕵‍♀️ E4.0 woman detective
-1F575 FE0F 200D 2640                                   ; unqualified         # 
🕵️‍♀ E4.0 woman detective
+1F575 FE0F 200D 2640                                   ; minimally-qualified # 
🕵️‍♀ E4.0 woman detective
 1F575 200D 2640                                        ; unqualified         # 
🕵‍♀ E4.0 woman detective
 1F575 1F3FB 200D 2640 FE0F                             ; fully-qualified     # 
🕵🏻‍♀️ E4.0 woman detective: light skin tone
 1F575 1F3FB 200D 2640                                  ; minimally-qualified # 
🕵🏻‍♀ E4.0 woman detective: light skin tone
@@ -2302,7 +2319,7 @@
 1F3CC 1F3FF                                            ; fully-qualified     # 
🏌🏿 E4.0 person golfing: dark skin tone
 1F3CC FE0F 200D 2642 FE0F                              ; fully-qualified     # 
🏌️‍♂️ E4.0 man golfing
 1F3CC 200D 2642 FE0F                                   ; unqualified         # 
🏌‍♂️ E4.0 man golfing
-1F3CC FE0F 200D 2642                                   ; unqualified         # 
🏌️‍♂ E4.0 man golfing
+1F3CC FE0F 200D 2642                                   ; minimally-qualified # 
🏌️‍♂ E4.0 man golfing
 1F3CC 200D 2642                                        ; unqualified         # 
🏌‍♂ E4.0 man golfing
 1F3CC 1F3FB 200D 2642 FE0F                             ; fully-qualified     # 
🏌🏻‍♂️ E4.0 man golfing: light skin tone
 1F3CC 1F3FB 200D 2642                                  ; minimally-qualified # 
🏌🏻‍♂ E4.0 man golfing: light skin tone
@@ -2316,7 +2333,7 @@
 1F3CC 1F3FF 200D 2642                                  ; minimally-qualified # 
🏌🏿‍♂ E4.0 man golfing: dark skin tone
 1F3CC FE0F 200D 2640 FE0F                              ; fully-qualified     # 
🏌️‍♀️ E4.0 woman golfing
 1F3CC 200D 2640 FE0F                                   ; unqualified         # 
🏌‍♀️ E4.0 woman golfing
-1F3CC FE0F 200D 2640                                   ; unqualified         # 
🏌️‍♀ E4.0 woman golfing
+1F3CC FE0F 200D 2640                                   ; minimally-qualified # 
🏌️‍♀ E4.0 woman golfing
 1F3CC 200D 2640                                        ; unqualified         # 
🏌‍♀ E4.0 woman golfing
 1F3CC 1F3FB 200D 2640 FE0F                             ; fully-qualified     # 
🏌🏻‍♀️ E4.0 woman golfing: light skin tone
 1F3CC 1F3FB 200D 2640                                  ; minimally-qualified # 
🏌🏻‍♀ E4.0 woman golfing: light skin tone
@@ -2427,7 +2444,7 @@
 26F9 1F3FF                                             ; fully-qualified     # 
⛹🏿 E2.0 person bouncing ball: dark skin tone
 26F9 FE0F 200D 2642 FE0F                               ; fully-qualified     # 
⛹️‍♂️ E4.0 man bouncing ball
 26F9 200D 2642 FE0F                                    ; unqualified         # 
⛹‍♂️ E4.0 man bouncing ball
-26F9 FE0F 200D 2642                                    ; unqualified         # 
⛹️‍♂ E4.0 man bouncing ball
+26F9 FE0F 200D 2642                                    ; minimally-qualified # 
⛹️‍♂ E4.0 man bouncing ball
 26F9 200D 2642                                         ; unqualified         # 
⛹‍♂ E4.0 man bouncing ball
 26F9 1F3FB 200D 2642 FE0F                              ; fully-qualified     # 
⛹🏻‍♂️ E4.0 man bouncing ball: light skin tone
 26F9 1F3FB 200D 2642                                   ; minimally-qualified # 
⛹🏻‍♂ E4.0 man bouncing ball: light skin tone
@@ -2441,7 +2458,7 @@
 26F9 1F3FF 200D 2642                                   ; minimally-qualified # 
⛹🏿‍♂ E4.0 man bouncing ball: dark skin tone
 26F9 FE0F 200D 2640 FE0F                               ; fully-qualified     # 
⛹️‍♀️ E4.0 woman bouncing ball
 26F9 200D 2640 FE0F                                    ; unqualified         # 
⛹‍♀️ E4.0 woman bouncing ball
-26F9 FE0F 200D 2640                                    ; unqualified         # 
⛹️‍♀ E4.0 woman bouncing ball
+26F9 FE0F 200D 2640                                    ; minimally-qualified # 
⛹️‍♀ E4.0 woman bouncing ball
 26F9 200D 2640                                         ; unqualified         # 
⛹‍♀ E4.0 woman bouncing ball
 26F9 1F3FB 200D 2640 FE0F                              ; fully-qualified     # 
⛹🏻‍♀️ E4.0 woman bouncing ball: light skin tone
 26F9 1F3FB 200D 2640                                   ; minimally-qualified # 
⛹🏻‍♀ E4.0 woman bouncing ball: light skin tone
@@ -2462,7 +2479,7 @@
 1F3CB 1F3FF                                            ; fully-qualified     # 
🏋🏿 E2.0 person lifting weights: dark skin tone
 1F3CB FE0F 200D 2642 FE0F                              ; fully-qualified     # 
🏋️‍♂️ E4.0 man lifting weights
 1F3CB 200D 2642 FE0F                                   ; unqualified         # 
🏋‍♂️ E4.0 man lifting weights
-1F3CB FE0F 200D 2642                                   ; unqualified         # 
🏋️‍♂ E4.0 man lifting weights
+1F3CB FE0F 200D 2642                                   ; minimally-qualified # 
🏋️‍♂ E4.0 man lifting weights
 1F3CB 200D 2642                                        ; unqualified         # 
🏋‍♂ E4.0 man lifting weights
 1F3CB 1F3FB 200D 2642 FE0F                             ; fully-qualified     # 
🏋🏻‍♂️ E4.0 man lifting weights: light skin tone
 1F3CB 1F3FB 200D 2642                                  ; minimally-qualified # 
🏋🏻‍♂ E4.0 man lifting weights: light skin tone
@@ -2476,7 +2493,7 @@
 1F3CB 1F3FF 200D 2642                                  ; minimally-qualified # 
🏋🏿‍♂ E4.0 man lifting weights: dark skin tone
 1F3CB FE0F 200D 2640 FE0F                              ; fully-qualified     # 
🏋️‍♀️ E4.0 woman lifting weights
 1F3CB 200D 2640 FE0F                                   ; unqualified         # 
🏋‍♀️ E4.0 woman lifting weights
-1F3CB FE0F 200D 2640                                   ; unqualified         # 
🏋️‍♀ E4.0 woman lifting weights
+1F3CB FE0F 200D 2640                                   ; minimally-qualified # 
🏋️‍♀ E4.0 woman lifting weights
 1F3CB 200D 2640                                        ; unqualified         # 
🏋‍♀ E4.0 woman lifting weights
 1F3CB 1F3FB 200D 2640 FE0F                             ; fully-qualified     # 
🏋🏻‍♀️ E4.0 woman lifting weights: light skin tone
 1F3CB 1F3FB 200D 2640                                  ; minimally-qualified # 
🏋🏻‍♀ E4.0 woman lifting weights: light skin tone
@@ -3262,8 +3279,8 @@
 1FAC2                                                  ; fully-qualified     # 
🫂 E13.0 people hugging
 1F463                                                  ; fully-qualified     # 
👣 E0.6 footprints
 
-# People & Body subtotal:              2986
-# People & Body subtotal:              506     w/o modifiers
+# People & Body subtotal:              2998
+# People & Body subtotal:              508     w/o modifiers
 
 # group: Component
 
@@ -3306,6 +3323,8 @@
 1F405                                                  ; fully-qualified     # 
🐅 E1.0 tiger
 1F406                                                  ; fully-qualified     # 
🐆 E1.0 leopard
 1F434                                                  ; fully-qualified     # 
🐴 E0.6 horse face
+1FACE                                                  ; fully-qualified     # 
🫎 E15.0 moose
+1FACF                                                  ; fully-qualified     # 
🫏 E15.0 donkey
 1F40E                                                  ; fully-qualified     # 
🐎 E0.6 horse
 1F984                                                  ; fully-qualified     # 
🦄 E1.0 unicorn
 1F993                                                  ; fully-qualified     # 
🦓 E5.0 zebra
@@ -3373,6 +3392,9 @@
 1F9A9                                                  ; fully-qualified     # 
🦩 E12.0 flamingo
 1F99A                                                  ; fully-qualified     # 
🦚 E11.0 peacock
 1F99C                                                  ; fully-qualified     # 
🦜 E11.0 parrot
+1FABD                                                  ; fully-qualified     # 
🪽 E15.0 wing
+1F426 200D 2B1B                                        ; fully-qualified     # 
🐦‍⬛ E15.0 black bird
+1FABF                                                  ; fully-qualified     # 
🪿 E15.0 goose
 
 # subgroup: animal-amphibian
 1F438                                                  ; fully-qualified     # 
🐸 E0.6 frog
@@ -3399,6 +3421,7 @@
 1F419                                                  ; fully-qualified     # 
🐙 E0.6 octopus
 1F41A                                                  ; fully-qualified     # 
🐚 E0.6 spiral shell
 1FAB8                                                  ; fully-qualified     # 
🪸 E14.0 coral
+1FABC                                                  ; fully-qualified     # 
🪼 E15.0 jellyfish
 
 # subgroup: animal-bug
 1F40C                                                  ; fully-qualified     # 
🐌 E0.6 snail
@@ -3433,6 +3456,7 @@
 1F33B                                                  ; fully-qualified     # 
🌻 E0.6 sunflower
 1F33C                                                  ; fully-qualified     # 
🌼 E0.6 blossom
 1F337                                                  ; fully-qualified     # 
🌷 E0.6 tulip
+1FABB                                                  ; fully-qualified     # 
🪻 E15.0 hyacinth
 
 # subgroup: plant-other
 1F331                                                  ; fully-qualified     # 
🌱 E0.6 seedling
@@ -3451,9 +3475,10 @@
 1F343                                                  ; fully-qualified     # 
🍃 E0.6 leaf fluttering in wind
 1FAB9                                                  ; fully-qualified     # 
🪹 E14.0 empty nest
 1FABA                                                  ; fully-qualified     # 
🪺 E14.0 nest with eggs
+1F344                                                  ; fully-qualified     # 
🍄 E0.6 mushroom
 
-# Animals & Nature subtotal:           151
-# Animals & Nature subtotal:           151     w/o modifiers
+# Animals & Nature subtotal:           159
+# Animals & Nature subtotal:           159     w/o modifiers
 
 # group: Food & Drink
 
@@ -3492,10 +3517,11 @@
 1F966                                                  ; fully-qualified     # 
🥦 E5.0 broccoli
 1F9C4                                                  ; fully-qualified     # 
🧄 E12.0 garlic
 1F9C5                                                  ; fully-qualified     # 
🧅 E12.0 onion
-1F344                                                  ; fully-qualified     # 
🍄 E0.6 mushroom
 1F95C                                                  ; fully-qualified     # 
🥜 E3.0 peanuts
 1FAD8                                                  ; fully-qualified     # 
🫘 E14.0 beans
 1F330                                                  ; fully-qualified     # 
🌰 E0.6 chestnut
+1FADA                                                  ; fully-qualified     # 
🫚 E15.0 ginger root
+1FADB                                                  ; fully-qualified     # 
🫛 E15.0 pea pod
 
 # subgroup: food-prepared
 1F35E                                                  ; fully-qualified     # 
🍞 E0.6 bread
@@ -3607,8 +3633,8 @@
 1FAD9                                                  ; fully-qualified     # 
🫙 E14.0 jar
 1F3FA                                                  ; fully-qualified     # 
🏺 E1.0 amphora
 
-# Food & Drink subtotal:               134
-# Food & Drink subtotal:               134     w/o modifiers
+# Food & Drink subtotal:               135
+# Food & Drink subtotal:               135     w/o modifiers
 
 # group: Travel & Places
 
@@ -3974,11 +4000,10 @@
 1F3AF                                                  ; fully-qualified     # 
🎯 E0.6 bullseye
 1FA80                                                  ; fully-qualified     # 
🪀 E12.0 yo-yo
 1FA81                                                  ; fully-qualified     # 
🪁 E12.0 kite
+1F52B                                                  ; fully-qualified     # 
🔫 E0.6 water pistol
 1F3B1                                                  ; fully-qualified     # 
🎱 E0.6 pool 8 ball
 1F52E                                                  ; fully-qualified     # 
🔮 E0.6 crystal ball
 1FA84                                                  ; fully-qualified     # 
🪄 E13.0 magic wand
-1F9FF                                                  ; fully-qualified     # 
🧿 E11.0 nazar amulet
-1FAAC                                                  ; fully-qualified     # 
🪬 E14.0 hamsa
 1F3AE                                                  ; fully-qualified     # 
🎮 E0.6 video game
 1F579 FE0F                                             ; fully-qualified     # 
🕹️ E0.7 joystick
 1F579                                                  ; unqualified         # 
🕹 E0.7 joystick
@@ -4013,8 +4038,8 @@
 1F9F6                                                  ; fully-qualified     # 
🧶 E11.0 yarn
 1FAA2                                                  ; fully-qualified     # 
🪢 E13.0 knot
 
-# Activities subtotal:         97
-# Activities subtotal:         97      w/o modifiers
+# Activities subtotal:         96
+# Activities subtotal:         96      w/o modifiers
 
 # group: Objects
 
@@ -4040,6 +4065,7 @@
 1FA73                                                  ; fully-qualified     # 
🩳 E12.0 shorts
 1F459                                                  ; fully-qualified     # 
👙 E0.6 bikini
 1F45A                                                  ; fully-qualified     # 
👚 E0.6 woman’s clothes
+1FAAD                                                  ; fully-qualified     # 
🪭 E15.0 folding hand fan
 1F45B                                                  ; fully-qualified     # 
👛 E0.6 purse
 1F45C                                                  ; fully-qualified     # 
👜 E0.6 handbag
 1F45D                                                  ; fully-qualified     # 
👝 E0.6 clutch bag
@@ -4055,6 +4081,7 @@
 1F461                                                  ; fully-qualified     # 
👡 E0.6 woman’s sandal
 1FA70                                                  ; fully-qualified     # 
🩰 E12.0 ballet shoes
 1F462                                                  ; fully-qualified     # 
👢 E0.6 woman’s boot
+1FAAE                                                  ; fully-qualified     # 
🪮 E15.0 hair pick
 1F451                                                  ; fully-qualified     # 
👑 E0.6 crown
 1F452                                                  ; fully-qualified     # 
👒 E0.6 woman’s hat
 1F3A9                                                  ; fully-qualified     # 
🎩 E0.6 top hat
@@ -4103,6 +4130,8 @@
 1FA95                                                  ; fully-qualified     # 
🪕 E12.0 banjo
 1F941                                                  ; fully-qualified     # 
🥁 E3.0 drum
 1FA98                                                  ; fully-qualified     # 
🪘 E13.0 long drum
+1FA87                                                  ; fully-qualified     # 
🪇 E15.0 maracas
+1FA88                                                  ; fully-qualified     # 
🪈 E15.0 flute
 
 # subgroup: phone
 1F4F1                                                  ; fully-qualified     # 
📱 E0.6 mobile phone
@@ -4275,7 +4304,7 @@
 1F5E1                                                  ; unqualified         # 
🗡 E0.7 dagger
 2694 FE0F                                              ; fully-qualified     # 
⚔️ E1.0 crossed swords
 2694                                                   ; unqualified         # 
⚔ E1.0 crossed swords
-1F52B                                                  ; fully-qualified     # 
🔫 E0.6 water pistol
+1F4A3                                                  ; fully-qualified     # 
💣 E0.6 bomb
 1FA83                                                  ; fully-qualified     # 
🪃 E13.0 boomerang
 1F3F9                                                  ; fully-qualified     # 
🏹 E1.0 bow and arrow
 1F6E1 FE0F                                             ; fully-qualified     # 
🛡️ E0.7 shield
@@ -4354,12 +4383,14 @@
 1FAA6                                                  ; fully-qualified     # 
🪦 E13.0 headstone
 26B1 FE0F                                              ; fully-qualified     # 
⚱️ E1.0 funeral urn
 26B1                                                   ; unqualified         # 
⚱ E1.0 funeral urn
+1F9FF                                                  ; fully-qualified     # 
🧿 E11.0 nazar amulet
+1FAAC                                                  ; fully-qualified     # 
🪬 E14.0 hamsa
 1F5FF                                                  ; fully-qualified     # 
🗿 E0.6 moai
 1FAA7                                                  ; fully-qualified     # 
🪧 E13.0 placard
 1FAAA                                                  ; fully-qualified     # 
🪪 E14.0 identification card
 
-# Objects subtotal:            304
-# Objects subtotal:            304     w/o modifiers
+# Objects subtotal:            310
+# Objects subtotal:            310     w/o modifiers
 
 # group: Symbols
 
@@ -4455,6 +4486,7 @@
 262E                                                   ; unqualified         # 
☮ E1.0 peace symbol
 1F54E                                                  ; fully-qualified     # 
🕎 E1.0 menorah
 1F52F                                                  ; fully-qualified     # 
🔯 E0.6 dotted six-pointed star
+1FAAF                                                  ; fully-qualified     # 
🪯 E15.0 khanda
 
 # subgroup: zodiac
 2648                                                   ; fully-qualified     # 
♈ E0.6 Aries
@@ -4503,6 +4535,7 @@
 1F505                                                  ; fully-qualified     # 
🔅 E1.0 dim button
 1F506                                                  ; fully-qualified     # 
🔆 E1.0 bright button
 1F4F6                                                  ; fully-qualified     # 
📶 E0.6 antenna bars
+1F6DC                                                  ; fully-qualified     # 
🛜 E15.0 wireless
 1F4F3                                                  ; fully-qualified     # 
📳 E0.6 vibration mode
 1F4F4                                                  ; fully-qualified     # 
📴 E0.6 mobile phone off
 
@@ -4693,8 +4726,8 @@
 1F533                                                  ; fully-qualified     # 
🔳 E0.6 white square button
 1F532                                                  ; fully-qualified     # 
🔲 E0.6 black square button
 
-# Symbols subtotal:            302
-# Symbols subtotal:            302     w/o modifiers
+# Symbols subtotal:            304
+# Symbols subtotal:            304     w/o modifiers
 
 # group: Flags
 
@@ -4709,7 +4742,7 @@
 1F3F3 200D 1F308                                       ; unqualified         # 
🏳‍🌈 E4.0 rainbow flag
 1F3F3 FE0F 200D 26A7 FE0F                              ; fully-qualified     # 
🏳️‍⚧️ E13.0 transgender flag
 1F3F3 200D 26A7 FE0F                                   ; unqualified         # 
🏳‍⚧️ E13.0 transgender flag
-1F3F3 FE0F 200D 26A7                                   ; unqualified         # 
🏳️‍⚧ E13.0 transgender flag
+1F3F3 FE0F 200D 26A7                                   ; minimally-qualified # 
🏳️‍⚧ E13.0 transgender flag
 1F3F3 200D 26A7                                        ; unqualified         # 
🏳‍⚧ E13.0 transgender flag
 1F3F4 200D 2620 FE0F                                   ; fully-qualified     # 
🏴‍☠️ E11.0 pirate flag
 1F3F4 200D 2620                                        ; minimally-qualified # 
🏴‍☠ E11.0 pirate flag
@@ -4983,9 +5016,9 @@
 # Flags subtotal:              275     w/o modifiers
 
 # Status Counts
-# fully-qualified : 3624
-# minimally-qualified : 817
-# unqualified : 252
+# fully-qualified : 3655
+# minimally-qualified : 827
+# unqualified : 242
 # component : 9
 
 #EOF
diff --git a/admin/unidata/emoji-zwj-sequences.txt 
b/admin/unidata/emoji-zwj-sequences.txt
index 6f94721a73..4125bec62e 100644
--- a/admin/unidata/emoji-zwj-sequences.txt
+++ b/admin/unidata/emoji-zwj-sequences.txt
@@ -1,13 +1,13 @@
 # emoji-zwj-sequences.txt
-# Date: 2021-06-08, 05:19:16 GMT
-# © 2021 Unicode®, Inc.
+# Date: 2022-05-06, 16:14:52 GMT
+# © 2022 Unicode®, Inc.
 # Unicode and the Unicode Logo are registered trademarks of Unicode, Inc. in 
the U.S. and other countries.
-# For terms of use, see http://www.unicode.org/terms_of_use.html
+# For terms of use, see https://www.unicode.org/terms_of_use.html
 #
 # Emoji ZWJ Sequences for UTS #51
-# Version: 14.0
+# Version: 15.0
 #
-# For documentation and usage, see http://www.unicode.org/reports/tr51
+# For documentation and usage, see https://www.unicode.org/reports/tr51
 #
 # Format:
 #   code_point(s) ; type_field ; description # comments
@@ -1398,6 +1398,7 @@
 1F3F4 200D 2620 FE0F                        ; RGI_Emoji_ZWJ_Sequence  ; pirate 
flag                                                    # E11.0  [1] (🏴‍☠️)
 1F408 200D 2B1B                             ; RGI_Emoji_ZWJ_Sequence  ; black 
cat                                                      # E13.0  [1] (🐈‍⬛)
 1F415 200D 1F9BA                            ; RGI_Emoji_ZWJ_Sequence  ; 
service dog                                                    # E12.0  [1] 
(🐕‍🦺)
+1F426 200D 2B1B                             ; RGI_Emoji_ZWJ_Sequence  ; black 
bird                                                     # E15.0  [1] (🐦‍⬛)
 1F43B 200D 2744 FE0F                        ; RGI_Emoji_ZWJ_Sequence  ; polar 
bear                                                     # E13.0  [1] (🐻‍❄️)
 1F441 FE0F 200D 1F5E8 FE0F                  ; RGI_Emoji_ZWJ_Sequence  ; eye in 
speech bubble                                           # E2.0   [1] (👁️‍🗨️)
 1F62E 200D 1F4A8                            ; RGI_Emoji_ZWJ_Sequence  ; face 
exhaling                                                  # E13.1  [1] (😮‍💨)
@@ -1405,6 +1406,6 @@
 1F636 200D 1F32B FE0F                       ; RGI_Emoji_ZWJ_Sequence  ; face 
in clouds                                                 # E13.1  [1] (😶‍🌫️)
 1F9D1 200D 1F384                            ; RGI_Emoji_ZWJ_Sequence  ; mx 
claus                                                       # E13.0  [1] (🧑‍🎄)
 
-# Total elements: 13
+# Total elements: 14
 
 #EOF
diff --git a/admin/unidata/unidata-gen.el b/admin/unidata/unidata-gen.el
index 78dd1c3728..5927760ad5 100644
--- a/admin/unidata/unidata-gen.el
+++ b/admin/unidata/unidata-gen.el
@@ -212,12 +212,12 @@ Property value is one of the following symbols:
       ;; Character Database (UCD).
       (L (#x0600 #x07BF AL) (#x0860 #x08FF AL) (#xFB50 #xFDCF AL)
          (#xFDF0 #xFDFF AL) (#xFE70 #xFEFF AL) (#x10D00 #x10D3F AL)
-         (#x10F30 #x10F6F AL) (#x1EC70 #x1ECBF AL) (#x1ED00 #x1ED4F AL)
-         (#x1EE00 #x1EEFF AL)
+         (#x10EC0 #x10EFF AL) (#x10F30 #x10F6F AL) (#x1EC70 #x1ECBF AL)
+         (#x1ED00 #x1ED4F AL) (#x1EE00 #x1EEFF AL)
         (#x0590 #x05FF R) (#x07C0 #x085F R) (#xFB1D #xFB4F R)
-         (#x10800 #x10CFF R) (#x10D40 #x10F2F R) (#x10F70 #x10FFF R)
-         (#x1E800 #x1EC6F R) (#x1ECC0 #x1ECFF R) (#x1ED50 #x1EDFF R)
-         (#x1EF00 #x1EFFF R)
+         (#x10800 #x10CFF R) (#x10D40 #x10EBF R) (#x10F00 #x10F2F R)
+         (#x10F70 #x10FFF R) (#x1E800 #x1EC6F R) (#x1ECC0 #x1ECFF R)
+         (#x1ED50 #x1EDFF R) (#x1EF00 #x1EFFF R)
          (#x20A0 #x20CF ET))
       ;; The order of elements must be in sync with bidi_type_t in
       ;; src/dispextern.h.
diff --git a/admin/update-copyright b/admin/update-copyright
index 5a04847a66..8b7c05749d 100755
--- a/admin/update-copyright
+++ b/admin/update-copyright
@@ -31,6 +31,8 @@
 # updated and some should not be, due to registration numbers, so
 # this script leaves these copyright years alone for now.
 
+set -o nounset
+
 : ${UPDATE_COPYRIGHT_USE_INTERVALS=1}
 export UPDATE_COPYRIGHT_USE_INTERVALS
 
diff --git a/admin/update_autogen b/admin/update_autogen
index 2451367171..d1f49d9f25 100755
--- a/admin/update_autogen
+++ b/admin/update_autogen
@@ -32,6 +32,8 @@
 
 ### Code:
 
+set -o nounset
+
 die ()                 # write error to stderr and exit
 {
     [ $# -gt 0 ] && echo "$PN: $@" >&2
@@ -93,6 +95,7 @@ genfiles="
 ## msdos-only:
 genfiles="src/config.in"
 
+basegen=""
 for g in $genfiles; do
     basegen="$basegen ${g##*/}"
 done
@@ -144,6 +147,7 @@ status ()
 
     local stat file modified
 
+    modified=""
     while read stat file; do
 
         [ "$stat" != "M" ] && \
diff --git a/admin/upload-manuals b/admin/upload-manuals
index 1b7950ede8..50336ee64c 100755
--- a/admin/upload-manuals
+++ b/admin/upload-manuals
@@ -36,6 +36,7 @@
 
 ### Code:
 
+set -o nounset
 
 die ()                          # write error to stderr and exit
 {
diff --git a/build-aux/config.guess b/build-aux/config.guess
index 1817bdce90..a419d8643b 100755
--- a/build-aux/config.guess
+++ b/build-aux/config.guess
@@ -4,7 +4,7 @@
 
 # shellcheck disable=SC2006,SC2268 # see below for rationale
 
-timestamp='2022-05-25'
+timestamp='2022-08-01'
 
 # This file is free software; you can redistribute it and/or modify it
 # under the terms of the GNU General Public License as published by
@@ -1036,7 +1036,7 @@ EOF
     k1om:Linux:*:*)
        GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
        ;;
-    loongarch32:Linux:*:* | loongarch64:Linux:*:* | loongarchx32:Linux:*:*)
+    loongarch32:Linux:*:* | loongarch64:Linux:*:*)
        GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
        ;;
     m32r*:Linux:*:*)
diff --git a/build-aux/config.sub b/build-aux/config.sub
index dba16e84c7..fbaa37f235 100755
--- a/build-aux/config.sub
+++ b/build-aux/config.sub
@@ -4,7 +4,7 @@
 
 # shellcheck disable=SC2006,SC2268 # see below for rationale
 
-timestamp='2022-01-03'
+timestamp='2022-08-01'
 
 # This file is free software; you can redistribute it and/or modify it
 # under the terms of the GNU General Public License as published by
@@ -1207,7 +1207,7 @@ case $cpu-$vendor in
                        | k1om \
                        | le32 | le64 \
                        | lm32 \
-                       | loongarch32 | loongarch64 | loongarchx32 \
+                       | loongarch32 | loongarch64 \
                        | m32c | m32r | m32rle \
                        | m5200 | m68000 | m680[012346]0 | m68360 | m683?2 | 
m68k \
                        | m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x \
diff --git a/config.bat b/config.bat
index 4adc477bc9..7f2060ce00 100644
--- a/config.bat
+++ b/config.bat
@@ -276,6 +276,7 @@ cd lib
 Rem Rename files like djtar on plain DOS filesystem would.
 If Exist c++defs.h update c++defs.h cxxdefs.h
 If Exist alloca.in.h update alloca.in.h alloca.in-h
+If Exist assert.in.h update assert.in.h assert.in-h
 If Exist byteswap.in.h update byteswap.in.h byteswap.in-h
 If Exist dirent.in.h update dirent.in.h dirent.in-h
 If Exist errno.in.h update errno.in.h errno.in-h
diff --git a/configure.ac b/configure.ac
index 43827e07a5..7d751fd6a8 100644
--- a/configure.ac
+++ b/configure.ac
@@ -495,7 +495,6 @@ OPTION_DEFAULT_ON([gnutls],[don't use -lgnutls for SSL/TLS 
support])
 OPTION_DEFAULT_ON([zlib],[don't compile with zlib decompression support])
 OPTION_DEFAULT_ON([modules],[don't compile with dynamic modules support])
 OPTION_DEFAULT_ON([threads],[don't compile with elisp threading support])
-OPTION_DEFAULT_OFF([native-compilation],[compile with Emacs Lisp native 
compiler support])
 OPTION_DEFAULT_OFF([cygwin32-native-compilation],[use native compilation on 
32-bit Cygwin])
 OPTION_DEFAULT_ON([xinput2],[don't use version 2 of the X Input Extension for 
input])
 OPTION_DEFAULT_OFF([small-ja-dic],[generate a smaller-size Japanese 
dictionary])
@@ -1010,6 +1009,29 @@ AC_ARG_ENABLE([gcc-warnings],
          [gl_GCC_VERSION_IFELSE([5], [3], [gl_gcc_warnings=warn-only])])
    fi])
 
+NATIVE_COMPILATION_AOT=no
+AC_ARG_WITH([native-compilation],
+  [AS_HELP_STRING([--with-native-compilation@<:@=TYPE@:>@],
+                  [compile with Emacs Lisp native compiler support.  The TYPE
+                  'yes' (or empty) means to enable it and compile natively
+                  preloaded Lisp files; 'no' means to disable it;
+                  'aot' will make the build process compile all the Lisp
+                  files in the tree natively ahead of time.  (This will
+                  usually be quite slow.)])],
+  [
+  case $withval in
+     aot)
+       withval=yes
+       NATIVE_COMPILATION_AOT=yes
+       ;;
+     yes|no) ;;
+     *)      AC_MSG_ERROR([bad value $withval for native-compilation option]) 
;;
+   esac
+   with_native_compilation=$withval],
+  [with_native_compilation=no]
+)
+AC_SUBST([NATIVE_COMPILATION_AOT])
+
 AC_ARG_ENABLE([check-lisp-object-type],
   [AS_HELP_STRING([--enable-check-lisp-object-type],
      [Enable compile time checks for the Lisp_Object data type,
diff --git a/doc/emacs/ack.texi b/doc/emacs/ack.texi
index d0f2cc343b..f0a45fd315 100644
--- a/doc/emacs/ack.texi
+++ b/doc/emacs/ack.texi
@@ -244,6 +244,11 @@ into Emacs.
 Theresa O'Connor wrote @file{json.el}, a file for parsing and
 generating JSON files.
 
+@item
+Andrea Corallo wrote the native compilation support in @file{comp.c}
+and @file{comp.el}, for compiling Emacs Lisp to native code using
+@samp{libgccjit}.
+
 @item
 Georges Brun-Cottan and Stefan Monnier wrote @file{easy-mmode.el}, a
 package for easy definition of major and minor modes.
@@ -519,8 +524,9 @@ Denis Howe wrote @file{browse-url.el}, a package for 
invoking a WWW
 browser to display a URL.
 
 @item
-Lars Magne Ingebrigtsen did a major redesign of the Gnus news-reader and
-wrote many of its parts.  Several of these are now general components of
+Lars Magne Ingebrigtsen was the Emacs (co-)maintainer from Emacs 27.2
+onwards.  He did a major redesign of the Gnus news-reader and wrote
+many of its parts.  Several of these are now general components of
 Emacs, including: @file{dns.el} for Domain Name Service lookups;
 @file{format-spec.el} for formatting arbitrary format strings;
 @file{netrc.el} for parsing of @file{.netrc} files; and
@@ -1435,7 +1441,8 @@ Victor Zandy wrote @file{zone.el}, a package for people 
who like to
 zone out in front of Emacs.
 
 @item
-Eli Zaretskii made many standard Emacs features work on MS-DOS and
+Eli Zaretskii was the Emacs (co-)maintainer from Emacs 25
+onwards.  He made many standard Emacs features work on MS-DOS and
 Microsoft Windows.  He also wrote @file{tty-colors.el}, which
 implements transparent mapping of X colors to tty colors; and
 @file{rxvt.el}.  He implemented support for bidirectional text, menus
diff --git a/doc/emacs/buffers.texi b/doc/emacs/buffers.texi
index 120c957ff8..8b21b6457c 100644
--- a/doc/emacs/buffers.texi
+++ b/doc/emacs/buffers.texi
@@ -631,13 +631,11 @@ buffer, but killing an indirect buffer has no effect on 
its base buffer.
 outline.  @xref{Outline Views}.
 
   A quick and handy way to make an indirect buffer is with the command
-@kbd{M-x clone-indirect-buffer}.  It creates and selects an indirect
-buffer whose base buffer is the current buffer.  With a numeric
-argument, it prompts for the name of the indirect buffer; otherwise it
-uses the name of the current buffer, with a @samp{<@var{n}>} suffix
-added.  @kbd{C-x 4 c} (@code{clone-indirect-buffer-other-window})
-works like @kbd{M-x clone-indirect-buffer}, but it selects the new
-buffer in another window.
+@kbd{C-x 4 c} (@code{clone-indirect-buffer-other-window}).  It creates
+and selects an indirect buffer whose base buffer is the current
+buffer.  With a numeric argument, it prompts for the name of the
+indirect buffer; otherwise it uses the name of the current buffer,
+with a @samp{<@var{n}>} suffix added.
 
   The more general way to make an indirect buffer is with the command
 @kbd{M-x make-indirect-buffer}.  It creates an indirect buffer
diff --git a/doc/emacs/commands.texi b/doc/emacs/commands.texi
index 431cc2e5ce..df3c47504a 100644
--- a/doc/emacs/commands.texi
+++ b/doc/emacs/commands.texi
@@ -24,8 +24,8 @@ input.
 
   GNU Emacs is primarily designed for use with the keyboard.  While it
 is possible to use the mouse to issue editing commands through the
-menu bar and tool bar, that is not as efficient as using the keyboard.
-Therefore, this manual mainly documents how to edit with the keyboard.
+menu bar and tool bar, that is usually not as efficient as using the
+keyboard.
 
 @cindex control character
   Keyboard input into Emacs is based on a heavily-extended version of
@@ -67,6 +67,10 @@ where the @key{Meta} key does not function reliably.
 
   Emacs supports 3 additional modifier keys, see @ref{Modifier Keys}.
 
+  Emacs has extensive support for using mouse buttons, mouse wheels
+and other pointing devices like touchpads and touch screens.
+@xref{Mouse Input}, for details.
+
 @cindex keys stolen by window manager
 @cindex window manager, keys stolen by
   On graphical displays, the window manager might block some keyboard
@@ -135,6 +139,47 @@ exception to this rule is @key{ESC}: @kbd{@key{ESC} C-h} 
is equivalent
 to @kbd{C-M-h}, which does something else entirely.  You can, however,
 use @key{F1} to display a list of commands starting with @key{ESC}.
 
+@node Mouse Input
+@section Mouse Input
+@cindex mouse input
+
+  By default, Emacs supports all the normal mouse actions like setting
+the cursor by clicking on the left mouse button, and selecting an area
+by dragging the mouse pointer.  All mouse actions can be used to bind
+commands in the same way you bind them to keyboard events
+(@pxref{Keys}).  This section provides a general overview of using the
+mouse in Emacs; @pxref{Mouse Commands}, and the sections that follow
+it, for more details about mouse commands in Emacs.
+
+  When you click the left mouse button, Emacs receives a
+@code{mouse-1} event.  To see what command is bound to that event, you
+can type @kbd{C-h c} and then press the left mouse button.  Similarly,
+the middle mouse button is @code{mouse-2} and the right mouse button is
+@code{mouse-3}.  If you have a mouse with a wheel, the wheel events
+are commonly bound to either @code{wheel-down} or @code{wheel-up}, or
+@code{mouse-4} and @code{mouse-5}, but that depends on the operating
+system configuration.
+
+  In general, legacy X systems and terminals (@pxref{Text-Only Mouse})
+will report @code{mouse-4} and @code{mouse-5}, while all other systems
+will report @code{wheel-down} and @code{wheel-up}.
+
+  Some mice also have a horizontal scroll wheel, and touchpads usually
+support scrolling horizontally as well.  These events are reported as
+@code{wheel-left} and @code{wheel-right} on all systems other than
+terminals and legacy X systems, where they are @code{mouse-6} and
+@code{mouse-7}.
+
+  You can also combine keyboard modifiers with mouse events, so you
+can bind a special command that triggers when you, for instance, holds
+down the Meta key and then uses the middle mouse button.  In that
+case, the event name will be @code{M-mouse-2}.
+
+@cindex touchscreen events
+  On some systems, you can also bind commands for handling touch
+screen events.  In that case, the events are called
+@code{touchscreen-update} and @code{touchscreen-end}.
+
 @node Commands
 @section Keys and Commands
 
diff --git a/doc/emacs/dired.texi b/doc/emacs/dired.texi
index 33e9270d42..a9b4ff783d 100644
--- a/doc/emacs/dired.texi
+++ b/doc/emacs/dired.texi
@@ -1641,7 +1641,7 @@ or through an external viewer.  This is different from 
Image mode
   To enter Image-Dired, mark the image files you want to look at in
 the Dired buffer, using @kbd{m} as usual.  Then type @kbd{C-t d}
 (@code{image-dired-display-thumbs}).  This creates and switches to a
-buffer containing image-dired, corresponding to the marked files.
+buffer containing Image-Dired, corresponding to the marked files.
 
   You can also enter Image-Dired directly by typing @kbd{M-x
 image-dired}.  This prompts for a directory; specify one that has
@@ -1650,20 +1650,21 @@ directory, and displays them all in the thumbnail 
buffer.  The
 thumbnails are generated in the background and are loaded as they
 become available.
 
+@findex image-dired-display-this
+@findex image-dired-display-next
+@findex image-dired-display-previous
   With point in the thumbnail buffer, you can type @key{RET}
-(@code{image-dired-display-thumbnail-original-image}) to display the
-image in another window.  Use the arrow keys to move around in the
-thumbnail buffer.  For easy browsing, use @key{SPC}
-(@code{image-dired-display-next-thumbnail-original}) to advance and
-display the next image.  Typing @key{DEL}
-(@code{image-dired-display-previous-thumbnail-original}) backs up to
-the previous thumbnail and displays that instead.
+(@code{image-dired-display-this}) to display the image in another
+window.  Use the arrow keys to move around in the thumbnail buffer.
+For easy browsing, use @key{SPC} (@code{image-dired-display-next}) to
+advance and display the next image.  Typing @key{DEL}
+(@code{image-dired-display-previous}) backs up to the previous
+thumbnail and displays that instead.
 
 @vindex image-dired-external-viewer
-  To view the image in its original size, either provide a prefix
-argument (@kbd{C-u}) before pressing @key{RET}, or type
-@kbd{C-@key{RET}} (@code{image-dired-thumbnail-display-external}) to
-display the image in an external viewer.  You must first configure
+  Type @kbd{C-@key{RET}}
+(@code{image-dired-thumbnail-display-external}) to display the image
+in an external viewer.  You must first configure
 @code{image-dired-external-viewer}.
 
   You can delete images through Image-Dired also.  Type @kbd{d}
@@ -1674,9 +1675,9 @@ image from the thumbnail buffer with @kbd{C-d}
 
   More advanced features include @dfn{image tags}, which are metadata
 used to categorize image files.  The tags are stored in a plain text
-file configured by @code{image-dired-db-file}.
+file configured by @code{image-dired-tags-db-file}.
 
-  To tag image files, mark them in the dired buffer (you can also mark
+  To tag image files, mark them in the Dired buffer (you can also mark
 files in Dired from the thumbnail buffer by typing @kbd{m}) and type
 @kbd{C-t t} (@code{image-dired-tag-files}).  This reads the tag name
 in the minibuffer.  To mark files having a certain tag, type @kbd{C-t f}
diff --git a/doc/emacs/emacs.texi b/doc/emacs/emacs.texi
index b43c966f87..727f5f93bf 100644
--- a/doc/emacs/emacs.texi
+++ b/doc/emacs/emacs.texi
@@ -148,6 +148,7 @@ Important General Concepts
                           function keys).
 * Keys::                Key sequences: what you type to request one
                           editing action.
+* Mouse Input::         Using the mouse and keypads.
 * Commands::            Named functions run by key sequences to do editing.
 * Entering Emacs::      Starting Emacs from the shell.
 * Exiting::             Stopping or killing Emacs.
@@ -852,6 +853,7 @@ Miscellaneous Commands and Features of VC
 * VC Delete/Rename::      Deleting and renaming version-controlled files.
 * Revision Tags::         Symbolic names for revisions.
 * Version Headers::       Inserting version control headers into working files.
+* Editing VC Commands:: Editing the VC shell commands that Emacs will run.
 
 Customizing VC
 
@@ -1414,23 +1416,23 @@ USA
 
 @c It's hard to update this fairly.
 @c I wonder if it would be better to drop it in favor of AUTHORS?
-Contributors to GNU Emacs include Jari Aalto, Per Abrahamsen, Tomas
+Contributors to GNU Emacs include Jari Aalto, Eric Abrahamsen, Per Abrahamsen, 
Tomas
 Abrahamsson, Jay K. Adams, Alon Albert, Michael Albinus, Nagy
 Andras, Benjamin Andresen, Ralf Angeli, Dmitry Antipov, Joe Arceneaux, Emil 
Åström,
 Miles Bader, David Bakhash, Juanma Barranquero, Eli Barzilay, Thomas
 Baumann, Steven L. Baur, Jay Belanger, Alexander L. Belikoff,
-Thomas Bellman, Scott Bender, Boaz Ben-Zvi, Sergey Berezin, Stephen Berman, 
Karl
+Thomas Bellman, Scott Bender, Boaz Ben-Zvi, Sergey Berezin, Stephen Berman, 
Jonas Bernoulli, Karl
 Berry, Anna M. Bigatti, Ray Blaak, Martin Blais, Jim Blandy, Johan
 Bockgård, Jan Böcker, Joel Boehland, Lennart Borgman, Per Bothner,
 Terrence Brannon, Frank Bresz, Peter Breton, Emmanuel Briot, Kevin
 Broadey, Vincent Broman, Michael Brouwer, David M. Brown, Ken Brown, Stefan 
Bruda,
-Daniel Colascione,
+Damien Cassou, Daniel Colascione,
 Georges Brun-Cottan, Joe Buehler, Scott Byer, Włodek Bzyl, Tino Calancha,
 Bill Carpenter, Per Cederqvist, Hans Chalupsky, Chris Chase, Bob
 Chassell, Andrew Choi, Chong Yidong, Sacha Chua, Stewart Clamen, James
-Clark, Mike Clarkson, Glynn Clements, Andrew Cohen, Daniel Colascione,
+Clark, Mike Clarkson, Glynn Clements, Andrea Corallo, Andrew Cohen, Daniel 
Colascione,
 Christoph Conrad, Ludovic Courtès, Andrew Csillag,
-Toby Cubitt, Baoqiu Cui, Doug Cutting, Mathias Dahl, Julien Danjou, Satyaki
+Toby Cubitt, Baoqiu Cui, Doug Cutting, Mathias Dahl, Yue Daian, Julien Danjou, 
Satyaki
 Das, Vivek Dasmohapatra, Dan Davison, Michael DeCorte, Gary Delp, Nachum
 Dershowitz, Dave Detlefs, Matthieu Devin, Christophe de Dinechin, Eri
 Ding, Jan Djärv, Lawrence R. Dodd, Carsten Dominik, Scott Draves,
@@ -1438,36 +1440,36 @@ Benjamin Drieu, Viktor Dukhovni, Jacques Duthen, Dmitry 
Dzhus, John
 Eaton, Rolf Ebert, Carl Edman, David Edmondson, Paul Eggert, Stephen
 Eglen, Christian Egli, Torbjörn Einarsson, Tsugutomo Enami, David
 Engster, Hans Henrik Eriksen, Michael Ernst, Ata Etemadi, Frederick
-Farnbach, Oscar Figueiredo, Fred Fish, Steve Fisk, Karl Fogel, Gary
+Farnbach, Oscar Figueiredo, Fred Fish, Steve Fisk, Thomas Fitzsimmons, Karl 
Fogel, Gary
 Foster, Eric S. Fraga, Romain Francoise, Noah Friedman, Andreas
 Fuchs, Shigeru Fukaya, Xue Fuqiao, Hallvard Furuseth, Keith Gabryelski, Peter 
S.
 Galbraith, Kevin Gallagher, Fabián E. Gallina, Kevin Gallo, Juan León Lahoz 
García,
 Howard Gayle, Daniel German, Stephen Gildea, Julien Gilles, David
-Gillespie, Bob Glickstein, Deepak Goel, David De La Harpe Golden, Boris
+Gillespie, Bob Glickstein, Nicolas Goaziou, Deepak Goel, David De La Harpe 
Golden, Boris
 Goldowsky, David Goodger, Chris Gray, Kevin Greiner, Michelangelo Grigni, Odd
 Gripenstam, Kai Großjohann, Michael Gschwind, Bastien Guerry, Henry
 Guillaume, Dmitry Gutov, Doug Gwyn, Bruno Haible, Ken'ichi Handa, Lars Hansen, 
Chris
 Hanson, Jesper Harder, Alexandru Harsanyi, K. Shane Hartman, John
 Heidemann, Jon K. Hellan, Magnus Henoch, Markus Heritsch, Dirk
-Herrmann, Karl Heuer, Manabu Higashida, Konrad Hinsen, Anders Holst,
-Jeffrey C. Honig, Tassilo Horn, Kurt Hornik, Khaled Hosny, Tom Houlder, Joakim
+Herrmann, Karl Heuer, Manabu Higashida, Konrad Hinsen, Torsten Hilbrich, 
Anders Holst,
+Jeffrey C. Honig, Jürgen Hötzel, Tassilo Horn, Kurt Hornik, Khaled Hosny, Tom 
Houlder, Joakim
 Hove, Denis Howe, Lars Ingebrigtsen, Andrew Innes, Seiichiro Inoue,
 Philip Jackson, Martyn Jago, Pavel Janik, Paul Jarc, Ulf Jasper,
 Thorsten Jolitz, Michael K. Johnson, Kyle Jones, Terry Jones, Simon
 Josefsson, Alexandre Julliard, Arne Jørgensen, Tomoji Kagatani,
-Brewster Kahle, Tokuya Kameshima, Lute Kamstra, Ivan Kanis, David
+Brewster Kahle, Tokuya Kameshima, Lute Kamstra, Stefan Kangas, Ivan Kanis, 
David
 Kastrup, David Kaufman, Henry Kautz, Taichi Kawabata, Taro Kawagishi,
 Howard Kaye, Michael Kifer, Richard King, Peter Kleiweg, Karel
 Klíč, Shuhei Kobayashi, Pavel Kobyakov, Larry K. Kolodney, David
 M. Koppelman, Koseki Yoshinori, Robert Krawitz, Sebastian Kremer,
-Ryszard Kubiak, Igor Kuzmin, David Kågedal, Daniel LaLiberte, Karl
-Landstrom, Mario Lang, Aaron Larson, James R. Larus, Vinicius Jose
+Ryszard Kubiak, Tak Kunihiro, Igor Kuzmin, David Kågedal, Daniel LaLiberte, 
Karl
+Landstrom, Mario Lang, Aaron Larson, James R. Larus, Gemini Lasswell, Vinicius 
Jose
 Latorre, Werner Lemberg, Frederic Lepied, Peter Liljenberg, Christian
 Limpach, Lars Lindberg, Chris Lindblad, Anders Lindgren, Thomas Link,
 Juri Linkov, Francis Litterio, Sergey Litvinov, Leo Liu, Emilio C. Lopes,
-Martin Lorentzon, Dave Love, Eric Ludlam, Károly Lőrentey, Sascha
+Martin Lorentzson, Dave Love, Eric Ludlam, Károly Lőrentey, Sascha
 Lüdecke, Greg McGary, Roland McGrath, Michael McNamara, Alan Mackenzie,
-Christopher J. Madsen, Neil M. Mager, Artur Malabarba, Ken Manheimer, Bill 
Mann,
+Christopher J. Madsen, Neil M. Mager, Arni Magnusson, Artur Malabarba, Ken 
Manheimer, Bill Mann,
 Brian Marick, Simon Marshall, Bengt Martensson, Charlie Martin,
 Yukihiro Matsumoto, Tomohiro Matsuyama, David Maus, Thomas May, Will 
Mengarini, David
 Megginson, Jimmy Aguilar Mena, Stefan Merten, Ben A. Mesander, Wayne Mesard, 
Brad
@@ -1483,7 +1485,7 @@ Jeff Peck, Damon Anton Permezel, Tom Perrine, William M. 
Perry, Per
 Persson, Jens Petersen, Nicolas Petton, Daniel Pfeiffer, Justus Piater, 
Richard L.
 Pieri, Fred Pierresteguy, François Pinard, Daniel Pittman, Christian
 Plaunt, Alexander Pohoyda, David Ponce, Noam Postavsky, Francesco A. Potortì,
-Michael D. Prange, Mukesh Prasad, Ken Raeburn, Marko Rahamaa, Ashwin
+Michael D. Prange, Mukesh Prasad, Steve Purcell, Ken Raeburn, Marko Rahamaa, 
Ashwin
 Ram, Eric S. Raymond, Paul Reilly, Edward M. Reingold, David
 Reitter, Alex Rezinsky, Rob Riepel, Lara Rios, Adrian Robert, Nick
 Roberts, Roland B. Roberts, John Robinson, Denis B. Roegel, Danny
@@ -1497,7 +1499,7 @@ Rainer Schöpf, Raymond Scholz, Eric Schulte, Andreas 
Schwab, Randal
 Schwartz, Oliver Seidel, Manuel Serrano, Paul Sexton, Hovav Shacham,
 Stanislav Shalunov, Marc Shapiro, Richard Sharman, Olin Shivers, Tibor
 Šimko, Espen Skoglund, Rick Sladkey, Lynn Slater, Chris Smith,
-David Smith, Paul D. Smith, Wilson Snyder, William Sommerfeld, Simon
+David Smith, JD Smith, Paul D. Smith, Wilson Snyder, William Sommerfeld, Simon
 South, Andre Spiegel, Michael Staats, Thomas Steffen, Ulf Stegemann,
 Reiner Steib, Sam Steingold, Ake Stenhoff, Philipp Stephani, Peter Stephenson, 
Ken
 Stevens, Andy Stewart, Jonathan Stigelman, Martin Stjernholm, Kim F.
diff --git a/doc/emacs/files.texi b/doc/emacs/files.texi
index 5b3b15cd38..1717c5c25b 100644
--- a/doc/emacs/files.texi
+++ b/doc/emacs/files.texi
@@ -2270,17 +2270,20 @@ behavior by using the options @code{image-auto-resize} 
and
 @code{image-auto-resize-on-window-resize}.
 
 @findex image-transform-fit-to-window
+@findex image-transform-set-percent
 @findex image-transform-set-scale
-@findex image-transform-reset
+@findex image-transform-reset-to-initial
+@findex image-transform-reset-to-original
 To resize the image manually you can use the command
 @code{image-transform-fit-to-window} bound to @kbd{s w} that fits the
 image to both the window height and width.  To scale the image to a
 percentage of its original size, use the command
-@code{image-transform-set-percent} bound to @kbd{s p}.  To scale
-the image specifying a scale factor, use the command
+@code{image-transform-set-percent} bound to @kbd{s p}.  To scale the
+image specifying a scale factor, use the command
 @code{image-transform-set-scale} bound to @kbd{s s}.  To reset all
-transformations to the initial state, use @code{image-transform-reset}
-bound to @kbd{s 0}.
+transformations to the initial state, use
+@code{image-transform-reset-to-initial} bound to @kbd{s 0}, or
+@code{image-transform-reset-to-original} bound to @kbd{s o}.
 
 @findex image-next-file
 @findex image-previous-file
diff --git a/doc/emacs/frames.texi b/doc/emacs/frames.texi
index d78cbffaa7..3ff47c6ffc 100644
--- a/doc/emacs/frames.texi
+++ b/doc/emacs/frames.texi
@@ -215,6 +215,10 @@ deactivating the mark.  @xref{Shift Selection}.
 @vindex mouse-wheel-follow-mouse
 @vindex mouse-wheel-scroll-amount
 @vindex mouse-wheel-progressive-speed
+@cindex wheel-up, a mouse event
+@cindex wheel-down, a mouse event
+@cindex wheel-left, a mouse event
+@cindex wheel-right, a mouse event
   Some mice have a ``wheel'' which can be used for scrolling.  Emacs
 supports scrolling windows with the mouse wheel, by default, on most
 graphical displays.  To toggle this feature, use @kbd{M-x
@@ -224,7 +228,11 @@ buffers are scrolled.  The variable
 @code{mouse-wheel-progressive-speed} determines whether the scroll
 speed is linked to how fast you move the wheel.  This mode also
 supports increasing or decreasing the font size, by default bound to
-scrolling with the @key{Ctrl} modifier.
+scrolling with the @key{Ctrl} modifier.  When this mode is enabled,
+mouse wheel produces special events like @code{wheel-up} and
+@code{wheel-down}.  (Some older systems report them as @code{mouse-4}
+and @code{mouse-5}.)  If the mouse has a horizontal scroll wheel, it
+produces @code{wheel-left} and @code{wheel-right} events as well.
 
 @vindex mouse-wheel-scroll-amount-horizontal
 Emacs also supports horizontal scrolling with the @key{Shift}
@@ -644,14 +652,15 @@ resources file to take effect.  @xref{Resources}.  Do not 
quote
 font names in X resource files.
 
 @item
-If you are running Emacs on the GNOME desktop, you can tell Emacs to
-use the default system font by setting the variable
+If you are running Emacs on the GNOME desktop or Haiku, you can tell
+Emacs to adjust the frame's default font along with changes to the
+default system font by setting the variable
 @code{font-use-system-font} to @code{t} (the default is @code{nil}).
 For this to work, Emacs must have been compiled with support for
 Gsettings (or the older Gconf).  (To be specific, the Gsettings
-configuration names used are
-@samp{org.gnome.desktop.interface monospace-font-name} and
-@samp{org.gnome.desktop.interface font-name}.)
+configuration names used are @samp{org.gnome.desktop.interface
+monospace-font-name} and @samp{org.gnome.desktop.interface
+font-name}.)
 
 @item
 Use the command line option @samp{-fn} (or @samp{--font}).  @xref{Font
diff --git a/doc/emacs/help.texi b/doc/emacs/help.texi
index d206dee385..6d9c028b74 100644
--- a/doc/emacs/help.texi
+++ b/doc/emacs/help.texi
@@ -47,7 +47,7 @@ window displaying the @samp{*Help*} buffer will be reused 
instead.
   If you are looking for a certain feature, but don't know what it is
 called or where to look, we recommend three methods.  First, try an
 apropos command, then try searching the manual index, then look in the
-FAQ and the package keywords.
+FAQ and the package keywords, and finally try listing external packages.
 
 @table @kbd
 @item C-h a @var{topics} @key{RET}
@@ -70,6 +70,9 @@ This displays the Emacs FAQ, using Info.
 @item C-h p
 This displays the available Emacs packages based on keywords.
 @xref{Package Keywords}.
+
+@item M-x list-packages
+This displays a list of external packages.  @xref{Packages}.
 @end table
 
   @kbd{C-h} or @key{F1} mean ``help'' in various other contexts as
@@ -308,12 +311,11 @@ doc string to display.  In that case, if
 to load the file in which the function is defined to see whether
 there's a doc string there.
 
-@findex shortdoc-display-group
+@findex shortdoc
   You can get an overview of functions relevant for a particular topic
-by using the @kbd{M-x shortdoc-display-group} command.  This will
-prompt you for an area of interest, e.g., @code{string}, and pop you
-to a buffer where many of the functions relevant for handling strings
-are listed.
+by using the @kbd{M-x shortdoc} command.  This will prompt you for an
+area of interest, e.g., @code{string}, and pop you to a buffer where
+many of the functions relevant for handling strings are listed.
 
 @kindex C-h v
 @findex describe-variable
diff --git a/doc/emacs/maintaining.texi b/doc/emacs/maintaining.texi
index 60169d8d8c..6857e67def 100644
--- a/doc/emacs/maintaining.texi
+++ b/doc/emacs/maintaining.texi
@@ -170,26 +170,12 @@ which it refers to as @dfn{back ends}:
 
 @itemize @bullet
 
-@cindex SCCS
-@item
-SCCS was the first version control system ever built, and was long ago
-superseded by more advanced ones.  VC compensates for certain features
-missing in SCCS (e.g., tag names for releases) by implementing them
-itself.  Other VC features, such as multiple branches, are simply
-unavailable.  Since SCCS is non-free, we recommend avoiding it.
-
-@cindex CSSC
-@item
-CSSC is a free replacement for SCCS@.  You should use CSSC only if, for
-some reason, you cannot use a more recent and better-designed version
-control system.
-
-@cindex RCS
+@cindex git
 @item
-RCS is the free version control system around which VC was initially
-built.  It is relatively primitive: it cannot be used over the
-network, and works at the level of individual files.  Almost
-everything you can do with RCS can be done through VC.
+Git is a decentralized version control system originally invented by
+Linus Torvalds to support development of Linux (his kernel).  VC
+supports many common Git operations, but others, such as repository
+syncing, must be done from the command line.
 
 @cindex CVS
 @item
@@ -208,12 +194,26 @@ similar to CVS but without its problems (e.g., it 
supports atomic
 commits of filesets, and versioning of directories, symbolic links,
 meta-data, renames, copies, and deletes).
 
-@cindex git
+@cindex SCCS
 @item
-Git is a decentralized version control system originally invented by
-Linus Torvalds to support development of Linux (his kernel).  VC
-supports many common Git operations, but others, such as repository
-syncing, must be done from the command line.
+SCCS was the first version control system ever built, and was long ago
+superseded by more advanced ones.  VC compensates for certain features
+missing in SCCS (e.g., tag names for releases) by implementing them
+itself.  Other VC features, such as multiple branches, are simply
+unavailable.  Since SCCS is non-free, we recommend avoiding it.
+
+@cindex CSSC
+@item
+CSSC is a free replacement for SCCS@.  You should use CSSC only if, for
+some reason, you cannot use a more recent and better-designed version
+control system.
+
+@cindex RCS
+@item
+RCS is the free version control system around which VC was initially
+built.  It is relatively primitive: it cannot be used over the
+network, and works at the level of individual files.  Almost
+everything you can do with RCS can be done through VC.
 
 @cindex hg
 @cindex Mercurial
@@ -690,11 +690,15 @@ started editing (@pxref{Old Revisions}), type @kbd{C-c 
C-d}
 
 @kindex C-c C-w @r{(Log Edit mode)}
 @findex log-edit-generate-changelog-from-diff
+@vindex diff-add-log-use-relative-names
   To help generate ChangeLog entries, type @kbd{C-c C-w}
 (@code{log-edit-generate-changelog-from-diff}), to generate skeleton
 ChangeLog entries, listing all changed file and function names based
 on the diff of the VC fileset.  Consecutive entries left empty will be
-combined by @kbd{C-q} (@code{fill-paragraph}).
+combined by @kbd{C-q} (@code{fill-paragraph}).  By default the
+skeleton will just include the file name, without any leading
+directories.  If you wish to prepend the leading directories up to the
+VC root, customize @code{diff-add-log-use-relative-names}.
 
 @kindex C-c C-a @r{(Log Edit mode)}
 @findex log-edit-insert-changelog
@@ -897,7 +901,14 @@ is non-@code{nil}, the colors expressing the age of each 
line are
 applied to the background color, leaving the foreground at its default
 color.
 
-  When you give a prefix argument to this command, Emacs reads two
+@vindex vc-annotate-switches
+  You can customize the @code{annotate} options that @kbd{C-x v g}
+uses by customizing @code{vc-@var{backend}-annotate-switches} and
+@code{vc-annotate-switches}.  They function similarly to
+@code{vc-@var{backend}-diff-switches} and @code{vc-diff-switches},
+described above.
+
+  When you give a prefix argument to @kbd{C-x v g}, Emacs reads two
 arguments using the minibuffer: the revision to display and annotate
 (instead of the current file contents), and the time span in days the
 color range should cover.
@@ -1030,13 +1041,14 @@ revision at point.  A second @key{RET} hides it again.
 (@code{vc-log-incoming}) command displays a log buffer showing the
 changes that will be applied, the next time you run the version
 control system's pull command to get new revisions from another
-repository (@pxref{Pulling / Pushing}).  This other repository is the default
+remote location (@pxref{Pulling / Pushing}).  This other remote location is 
the default
 one from which changes are pulled, as defined by the version control
 system; with a prefix argument, @code{vc-log-incoming} prompts for a
-specific repository.  Similarly, @kbd{C-x v O}
+specific remote location.  Similarly, @kbd{C-x v O}
 (@code{vc-log-outgoing}) shows the changes that will be sent to
-another repository, the next time you run the push command; with a
-prefix argument, it prompts for a specific destination repository.
+another remote location, the next time you run the push command; with a
+prefix argument, it prompts for a specific destination that
+in case of some version control system can be a branch name.
 
 @cindex VC log buffer, commands in
 @cindex vc-log buffer
@@ -1394,18 +1406,19 @@ Apart from acting on multiple files, these commands 
behave much like
 their single-buffer counterparts (@pxref{Search}).
 
   The VC Directory buffer additionally defines some branch-related
-commands starting with the prefix @kbd{B}:
+commands starting with the prefix @kbd{b}:
 
 @table @kbd
-@item B c
-Create a new branch (@code{vc-create-tag}).
+@item b c
+Create a new branch (@code{vc-create-branch}).  @xref{Creating
+Branches}.
 
-@item B l
+@item b l
 Prompt for the name of a branch and display the change history of that
 branch (@code{vc-print-branch-log}).
 
-@item B s
-Switch to a branch (@code{vc-retrieve-tag}).  @xref{Switching
+@item b s
+Switch to a branch (@code{vc-switch-branch}).  @xref{Switching
 Branches}.
 
 @item d
@@ -1469,7 +1482,7 @@ Mercurial, command @kbd{hg update} is used to switch to 
another
 branch.
 
   The VC command to switch to another branch in the current directory
-is @kbd{C-x v r @var{branch-name} @key{RET}} (@code{vc-retrieve-tag}).
+is @kbd{C-x v b s @var{branch-name} @key{RET}} (@code{vc-switch-branch}).
 
   On centralized version control systems, you can also switch between
 branches by typing @kbd{C-u C-x v v} in an up-to-date work file
@@ -1619,8 +1632,8 @@ if the current revision is 2.5, the branch ID should be 
2.5.1, 2.5.2,
 and so on, depending on the number of existing branches at that point.
 
   This procedure will not work for distributed version control systems
-like git or Mercurial.  For those systems you should use the prefix
-argument to @code{vc-create-tag} (@kbd{C-u C-x v s}) instead.
+like git or Mercurial.  For those systems you should use the command
+@code{vc-create-branch} (@kbd{C-x v b c}) instead.
 
   To create a new branch at an older revision (one that is no longer
 the head of a branch), first select that revision (@pxref{Switching
diff --git a/doc/emacs/mark.texi b/doc/emacs/mark.texi
index ad25ed6a8a..db96093a17 100644
--- a/doc/emacs/mark.texi
+++ b/doc/emacs/mark.texi
@@ -8,26 +8,29 @@
 @cindex setting a mark
 @cindex region
 
-  Many Emacs commands operate on an arbitrary contiguous part of the
-current buffer.  To specify the text for such a command to operate on,
-you set @dfn{the mark} at one end of it, and move point to the other
-end.  The text between point and the mark is called @dfn{the region}.
-The region always extends between point and the mark, no matter which
-one comes earlier in the text; each time you move point, the region
-changes.
+  Emacs, like many other applications, lets you select some arbitrary
+part of the buffer text and invoke commands that operate on such
+@dfn{selected text}.  In Emacs, we call the selected text @dfn{the
+region}; its handling is very similar to that of selected text in
+other programs, but there are also important differences.
 
 @cindex active region
 @cindex activating the mark
-  Setting the mark at a position in the text also @dfn{activates} it.
-When the mark is active, we say also that the region is active; Emacs
+  The region is the portion of the buffer between @dfn{the mark} and
+the current @dfn{point}.  You define a region by setting the mark
+somewhere (with, for instance, the @kbd{C-SPC} command), and then
+moving point to where you want the region to end.  (Or you can use the
+mouse to define a region.)
+
+  The region always extends between point and the mark, no matter
+which of them comes earlier in the text; each time you move point, the
+region changes.
+
+  Setting the mark at a position in the text @dfn{activates} it.  When
+the mark is active, we say also that the region is active; Emacs
 indicates its extent by highlighting the text within it, using the
 @code{region} face (@pxref{Face Customization}).
 
-This is one of the few faces that has the @code{:extend t} attribute
-by default, which implies that the same face is used to highlight the
-text and space between end of line and the window border.  To
-highlight only the text you could set this attribute to @code{nil}.
-
 @cindex deactivating the mark
   After certain non-motion commands, including any command that
 changes the text in the buffer, Emacs automatically @dfn{deactivates}
@@ -35,6 +38,18 @@ the mark; this turns off the highlighting.  You can also 
explicitly
 deactivate the mark at any time, by typing @kbd{C-g}
 (@pxref{Quitting}).
 
+  Many commands limit the text on which they operate to the active
+region.  For instance, the @kbd{M-%} command (which replaces matching
+text) normally works on the entire accessible portion of the buffer,
+but if you have an active region, it'll work only on that region
+instead.
+
+  The mark is useful even if it is not active.  For example, you can
+move to previous mark locations using the mark ring.  @xref{Mark
+Ring}.  Additionally, some commands will have an effect even on an
+inactive region (for example @dfn{upcase-region}).  You can also
+reactivate the region with commands like @kbd{C-x C-x}.
+
   The above default behavior is known as Transient Mark mode.
 Disabling Transient Mark mode switches Emacs to an alternative
 behavior, in which the region is usually not highlighted.
diff --git a/doc/emacs/mini.texi b/doc/emacs/mini.texi
index e71d653210..90e50a41d5 100644
--- a/doc/emacs/mini.texi
+++ b/doc/emacs/mini.texi
@@ -58,12 +58,8 @@ the default argument is shown with the user option
 Emacs hides the default argument as soon as you modify the contents of
 the minibuffer (since typing @key{RET} would no longer submit that
 default).  If you ever bring back the original minibuffer text, the
-prompt again shows the default.  Furthermore, if you change the
-variable @code{minibuffer-eldef-shorten-default} to a non-@code{nil}
-value, the default argument is displayed as @samp{[@var{default-arg}]}
-instead of @samp{(default @var{default-arg})}, saving some screen
-space.  To enable this minor mode, type @kbd{M-x
-minibuffer-electric-default-mode}.
+prompt again shows the default.  To enable this minor mode, type
+@kbd{M-x minibuffer-electric-default-mode}.
 
   Since the minibuffer appears in the echo area, it can conflict with
 other uses of the echo area.  If an error message or an informative
diff --git a/doc/emacs/misc.texi b/doc/emacs/misc.texi
index da1b87b48b..1514e316f8 100644
--- a/doc/emacs/misc.texi
+++ b/doc/emacs/misc.texi
@@ -162,12 +162,12 @@ List killed groups (@code{gnus-group-list-killed}).
 List zombie groups (@code{gnus-group-list-zombies}).
 
 @kindex u @r{(Gnus Group mode)}
-@findex gnus-group-toggle-subscription
+@findex gnus-group-toggle-subscription-at-point
 @cindex subscribe groups
 @cindex unsubscribe groups
 @item u
 Toggle the subscription status of the group
-(@code{gnus-group-toggle-subscription}) on the current line.
+(@code{gnus-group-toggle-subscription-at-point}) on the current line.
 Invoking this on a killed or zombie group turns it into an
 unsubscribed group.
 
@@ -245,7 +245,7 @@ buffer and typed @kbd{C-s} (@pxref{Incremental Search}).
 
 @kindex M-s M-s @r{(Gnus Summary mode)}
 @findex gnus-summary-search-article-forward
-@item M-s @var{regexp} @key{RET}
+@item M-s M-s @var{regexp} @key{RET}
 Search forward for articles containing a match for @var{regexp}
 (@code{gnus-summary-search-article-forward}).
 
@@ -584,6 +584,18 @@ you instead want the image to be re-rendered at the new 
size, set
 default size for DocView, customize the variable
 @code{doc-view-resolution}.
 
+@vindex doc-view-imenu-enabled
+@vindex doc-view-imenu-flatten
+@vindex doc-view-imenu-format
+  When the @command{mutool} program is available, DocView will use it
+to generate entries for an outline menu, making it accessible via the
+@code{imenu} facility (@pxref{Imenu}).  To disable this functionality
+even when @command{mutool} can be found on your system, customize the
+variable @code{doc-view-imenu-enabled} to the @code{nil} value.  You
+can further customize how @code{imenu} items are formatted and
+displayed using the variables @code{doc-view-imenu-format} and
+@code{doc-view-flatten}.
+
 @node DocView Searching
 @subsection DocView Searching
 
@@ -936,7 +948,7 @@ Coding}.
 @cindex @env{INSIDE_EMACS} environment variable
   Emacs sets the environment variable @env{INSIDE_EMACS} in the
 subshell to @samp{@var{version},comint}, where @var{version} is the
-Emacs version (e.g., @samp{24.1}).  Programs can check this variable
+Emacs version (e.g., @samp{28.1}).  Programs can check this variable
 to determine whether they are running inside an Emacs subshell.
 
 @node Shell Mode
@@ -2089,7 +2101,14 @@ all server buffers are finished.  You can take as long 
as you like to
 edit the server buffers within Emacs, and they are @emph{not} killed
 when you type @kbd{C-x #} in them.
 
-@item --parent-id @var{id}
+@item -w
+@itemx --timeout=@var{N}
+Wait for a response from Emacs for @var{N} seconds before giving up.
+If there is no response within that time, @command{emacsclient} will
+display a warning and exit.  The default is @samp{0}, which means to
+wait forever.
+
+@item --parent-id=@var{id}
 Open an @command{emacsclient} frame as a client frame in the parent X
 window with id @var{id}, via the XEmbed protocol.  Currently, this
 option is mainly useful for developers.
diff --git a/doc/emacs/modes.texi b/doc/emacs/modes.texi
index c348130807..56b779f8de 100644
--- a/doc/emacs/modes.texi
+++ b/doc/emacs/modes.texi
@@ -454,6 +454,13 @@ only @emph{after} @code{auto-mode-alist}.  By default,
 files, HTML/XML/SGML files, PostScript files, and Unix style Conf
 files.
 
+@vindex major-mode-remap-alist
+  Once a major mode is found, Emacs does a final check to see if the
+mode has been remapped by @code{major-mode-remap-alist}, in which case
+it uses the remapped mode instead.  This is used when several
+different major modes can be used for the same file type, so you can
+specify which mode you prefer.
+
 @findex normal-mode
   If you have changed the major mode of a buffer, you can return to
 the major mode Emacs would have chosen automatically, by typing
diff --git a/doc/emacs/mule.texi b/doc/emacs/mule.texi
index 5f30341838..1bbd7440f3 100644
--- a/doc/emacs/mule.texi
+++ b/doc/emacs/mule.texi
@@ -61,7 +61,7 @@ can also be input by using the @kbd{C-x 8} prefix, see 
@ref{Unibyte Mode}.
 
 With the X Window System, your locale should be set to an appropriate
 value to make sure Emacs interprets keyboard input correctly; see
-@ref{Language Environments, locales}.
+@ref{Language Environments, locales}, and @ref{X Coding}.
 @end itemize
 
   The rest of this chapter describes these issues in detail.
@@ -79,6 +79,7 @@ value to make sure Emacs interprets keyboard input correctly; 
see
 * Text Coding::             Choosing conversion to use for file text.
 * Communication Coding::    Coding systems for interprocess communication.
 * File Name Coding::        Coding systems for file @emph{names}.
+* X Coding::                Coding systems for X input methods.
 * Terminal Coding::         Specifying coding systems for converting
                               terminal input and output.
 * Fontsets::                Fontsets are collections of fonts
@@ -1241,15 +1242,14 @@ current language environment.
   The variable @code{locale-coding-system} specifies a coding system
 to use when encoding and decoding system strings such as system error
 messages and @code{format-time-string} formats and time stamps.  That
-coding system is also used for decoding non-@acronym{ASCII} keyboard
-input on the X Window System and for encoding text sent to the
-standard output and error streams when in batch mode.  You should
-choose a coding system that is compatible
-with the underlying system's text representation, which is normally
-specified by one of the environment variables @env{LC_ALL},
-@env{LC_CTYPE}, and @env{LANG}.  (The first one, in the order
-specified above, whose value is nonempty is the one that determines
-the text representation.)
+coding system might also be used for decoding non-@acronym{ASCII}
+keyboard input on the X Window System and will also be used to encode
+text sent to the standard output and error streams in batch mode.  You
+should choose a coding system that is compatible with the underlying
+system's text representation, which is normally specified by one of
+the environment variables @env{LC_ALL}, @env{LC_CTYPE}, and
+@env{LANG}.  (The first one, in the order specified above, whose value
+is nonempty is the one that determines the text representation.)
 
 @node File Name Coding
 @section Coding Systems for File Names
@@ -1311,6 +1311,26 @@ C-w} to specify a new file name for that buffer.
 system.  This prompts for an existing file name, its old coding
 system, and the coding system to which you wish to convert.
 
+@node X Coding
+@section Coding Systems for X Keyboard Input
+@cindex X input method coding systems
+  Input methods under the X Window System specify their own coding
+systems that must be used to decode keyboard input.  By default, Emacs
+determines the coding system used for each input method automatically
+upon establishing the connection to the input method server, and uses
+that specific coding system to decode keyboard input.  However, that
+determination can sometimes fail; in that situation, the locale coding
+system (@pxref{Communication Coding}) is used instead.
+
+@cindex X input method coding systems, overriding
+@vindex x-input-coding-system
+  If the input method does not correctly announce the coding system it
+uses to encode text, then the coding system used by Emacs to decode
+text from input methods must be manually specified.  The value of the
+variable @code{x-input-coding-system}, when set to a symbol, is
+unconditionally used as the coding system used to decode keyboard
+input from input methods.
+
 @node Terminal Coding
 @section Coding Systems for Terminal I/O
 
diff --git a/doc/emacs/package.texi b/doc/emacs/package.texi
index 7e16c82cf5..420da09097 100644
--- a/doc/emacs/package.texi
+++ b/doc/emacs/package.texi
@@ -421,13 +421,13 @@ lower-priority archives will not be shown in the menu, if 
the same
 package is available from a higher-priority archive.  (This is
 controlled by the value of @code{package-menu-hide-low-priority}.)
 
-  Once a package is downloaded and installed, it is made available to
-the current Emacs session.  Making a package available adds its
-directory to @code{load-path} and loads its autoloads.  The effect of
-a package's autoloads varies from package to package.  Most packages
-just make some new commands available, while others have more
-wide-ranging effects on the Emacs session.  For such information,
-consult the package's help buffer.
+  Once a package is downloaded, byte-compiled and installed, it is
+made available to the current Emacs session.  Making a package
+available adds its directory to @code{load-path} and loads its
+autoloads.  The effect of a package's autoloads varies from package to
+package.  Most packages just make some new commands available, while
+others have more wide-ranging effects on the Emacs session.  For such
+information, consult the package's help buffer.
 
   Installed packages are automatically made available by Emacs in all
 subsequent sessions.  This happens at startup, before processing the
diff --git a/doc/emacs/programs.texi b/doc/emacs/programs.texi
index 795aabee74..03f565808d 100644
--- a/doc/emacs/programs.texi
+++ b/doc/emacs/programs.texi
@@ -508,9 +508,9 @@ Reindent each line in the balanced expression that follows 
point
 about invalid syntax.
 
 @item @key{TAB}
-@findex c-indent-command
-Reindent the current line, and/or in some cases insert a tab character
-(@code{c-indent-command}).
+@findex c-indent-line-or-region
+Reindent the current line, active region, or block starting on this
+line (@code{c-indent-line-or-region}).
 
 @vindex c-tab-always-indent
 If @code{c-tab-always-indent} is @code{t}, this command always reindents
@@ -834,10 +834,16 @@ of automatic matching.  Whenever point is before an 
opening delimiter
 or after a closing delimiter, the delimiter, its matching delimiter,
 and optionally the text between them are highlighted.  To toggle Show
 Paren mode globally, type @kbd{M-x show-paren-mode}.  To toggle it
-only in the current buffer, type @kbd{M-x show-paren-local-mode}.  To
-customize it, type @w{@kbd{M-x customize-group @key{RET} paren-showing}}.
-The customizable options which control the operation of this mode
-include:
+only in the current buffer, type @kbd{M-x show-paren-local-mode}.
+
+@vindex show-paren-predicate
+  By default, this mode is switched on in all buffers that are meant
+for editing, but is not enabled in buffers that show data.  This is
+controlled by the @code{show-paren-predicate} user option.
+
+  To customize the mode, type @w{@kbd{M-x customize-group @key{RET}
+paren-showing}}.  The customizable options which control the operation
+of this mode include:
 
 @itemize @bullet
 @item
diff --git a/doc/emacs/regs.texi b/doc/emacs/regs.texi
index fb93601879..ef9187bb9a 100644
--- a/doc/emacs/regs.texi
+++ b/doc/emacs/regs.texi
@@ -381,7 +381,8 @@ jump to the bookmark.
 @code{bookmark-jump} can find the proper position even if the file is
 modified slightly.  The variable @code{bookmark-search-size} says how
 many characters of context to record on each side of the bookmark's
-position.
+position.  (In buffers that are visiting encrypted files, no context
+is saved in the bookmarks file no matter the value of this variable.)
 
   Here are some additional commands for working with bookmarks:
 
diff --git a/doc/emacs/text.texi b/doc/emacs/text.texi
index fa8eaf0924..b103e22e39 100644
--- a/doc/emacs/text.texi
+++ b/doc/emacs/text.texi
@@ -1003,6 +1003,11 @@ addition to ellipsis to show that a section is hidden.  
Using
 @kbd{RET} (or clicking on the button with a mouse) will toggle
 displaying the section.
 
+@vindex outline-minor-mode-use-margins
+  If @code{outline-minor-mode-use-margins} is non-@code{nil}, Outline
+minor mode will use the window margins in addition to ellipsis to show
+that a section is hidden.
+
 @vindex outline-minor-mode-cycle
   If the @code{outline-minor-mode-cycle} user option is
 non-@code{nil}, the @kbd{TAB} and @kbd{S-@key{TAB}} keys are enabled on the
@@ -1509,15 +1514,15 @@ etc.
 @subsection Org as an authoring system
 @cindex Org exporting
 
-@findex org-export
+@findex org-export-dispatch
 @kindex C-c C-e @r{(Org mode)}
   You may want to format your Org notes nicely and to prepare them for
 export and publication.  To export the current buffer, type @kbd{C-c
-C-e} (@code{org-export}) anywhere in an Org buffer.  This command
-prompts for an export format; currently supported formats include
-HTML, @LaTeX{}, Texinfo, OpenDocument (@file{.odt}), iCalendar,
-Markdown, man-page, and PDF@.  Some formats, such as PDF, require
-certain system tools to be installed.
+C-e} (@code{org-export-dispatch}) anywhere in an Org buffer.  This
+command prompts for an export format; currently supported formats
+include HTML, @LaTeX{}, Texinfo, OpenDocument (@file{.odt}),
+iCalendar, Markdown, man-page, and PDF@.  Some formats, such as PDF,
+require certain system tools to be installed.
 
 @vindex org-publish-project-alist
   To export several files at once to a specific directory, either
diff --git a/doc/emacs/vc1-xtra.texi b/doc/emacs/vc1-xtra.texi
index 3ccad50715..05d2144380 100644
--- a/doc/emacs/vc1-xtra.texi
+++ b/doc/emacs/vc1-xtra.texi
@@ -15,6 +15,7 @@
 * VC Delete/Rename::    Deleting and renaming version-controlled files.
 * Revision Tags::       Symbolic names for revisions.
 * Version Headers::     Inserting version control headers into working files.
+* Editing VC Commands:: Editing the VC shell commands that Emacs will run.
 @end menu
 
 @node Change Logs and VC
@@ -263,6 +264,24 @@ elements of the form @code{(@var{regexp} . @var{format})}. 
 Whenever
 part of the version header.  A @samp{%s} in @var{format} is replaced
 with the file's version control type.
 
+@node Editing VC Commands
+@subsubsection Editing VC Commands
+
+@findex vc-edit-next-command
+@kindex C-x v !
+You can use the @kbd{C-x v !} (@code{vc-edit-next-command}) prefix
+command to edit the shell command line that VC is about to run.  This
+is primarily intended to make it possible to add optional command-line
+arguments to VCS commands without unnecessary complications of the VC
+command set and its interfaces with the backend.
+
+For example, Git can produce logs of more than one branch, but
+@kbd{C-x v b l} (@code{vc-print-branch-log}) prompts for the name of
+just one branch.  To obtain a log of more than one branch, you can
+type @w{@kbd{C-x v ! C-x v b l}} and then append the names of
+additional branches to the end of the @samp{git log} command that VC
+is about to run.
+
 @node Customizing VC
 @subsection Customizing VC
 
diff --git a/doc/lispintro/emacs-lisp-intro.texi 
b/doc/lispintro/emacs-lisp-intro.texi
index 47a5a870fd..df8fa2f8e7 100644
--- a/doc/lispintro/emacs-lisp-intro.texi
+++ b/doc/lispintro/emacs-lisp-intro.texi
@@ -10100,9 +10100,8 @@ resources; as it happens, methods that people find 
easy---that are
 frugal of mental resources---sometimes use considerable computer
 resources.  Emacs was designed to run on machines that we now consider
 limited and its default settings are conservative.  You may want to
-increase the values of @code{max-specpdl-size} and
-@code{max-lisp-eval-depth}.  In my @file{.emacs} file, I set them to
-15 and 30 times their default value.}.
+increase the value of @code{max-lisp-eval-depth}.  In my @file{.emacs}
+file, I set it to 30 times its default value.}.
 
 @menu
 * while::                       Causing a stretch of code to repeat.
diff --git a/doc/lispref/compile.texi b/doc/lispref/compile.texi
index 60fc11a22e..7ccee08e53 100644
--- a/doc/lispref/compile.texi
+++ b/doc/lispref/compile.texi
@@ -973,6 +973,24 @@ whether native-compilation is available should use this 
predicate.
   This section documents the variables that control
 native-compilation.
 
+@defvar inhibit-automatic-native-compilation
+If your Emacs has support for native compilation, Emacs will (by
+default) compile the Lisp files you're loading in the background, and
+then install the native-compiled versions of the functions.  If you
+wish to disable this, you can set this variable to non-@code{nil}.  If
+you want to set it permanently, this should probably be done from the
+early init file, since setting it in the normal init file is probably
+too late.
+
+While setting this variable disables automatic compilation of Lisp
+files, the compiler may still be invoked to install @dfn{trampolines}
+if any built-in functions are redefined.  However, these trampolines
+will not get written to disk.
+
+You can also use the @samp{EMACS_INHIBIT_AUTOMATIC_NATIVE_COMPILATION}
+environment variable to disable native compilation.
+@end defvar
+
 @defopt native-comp-speed
 This variable specifies the optimization level for native compilation.
 Its value should be a number between @minus{}1 and 3.  Values between
diff --git a/doc/lispref/control.texi b/doc/lispref/control.texi
index d4520ebdee..9035e7f6bb 100644
--- a/doc/lispref/control.texi
+++ b/doc/lispref/control.texi
@@ -294,6 +294,48 @@ For example:
 @end group
 @end example
 
+It can be convenient to bind variables in conjunction with using a
+conditional.  It's often the case that you compute a value, and then
+want to do something with that value if it's non-@code{nil}.  The
+straightforward way to do that is to just write, for instance:
+
+@example
+(let ((result1 (do-computation)))
+  (when result1
+    (let ((result2 (do-more result1)))
+      (when result2
+        (do-something result2)))))
+@end example
+
+Since this is a very common pattern, Emacs provides a number of macros
+to make this easier and more readable.  The above can be written the
+following way instead:
+
+@example
+(when-let ((result1 (do-computation))
+           (result2 (do-more result1)))
+  (do-something result2))
+@end example
+
+There's a number of variations on this theme, and they're briefly
+described below.
+
+@defmac if-let spec then-form else-forms...
+Evaluate each binding in @var{spec} in turn, like in @code{let*}
+(@pxref{Local Variables}, stopping if a binding value is @code{nil}.
+If all are non-@code{nil}, return the value of @var{then-form},
+otherwise the last form in @var{else-forms}.
+@end defmac
+
+@defmac when-let spec then-forms...
+Like @code{if-let}, but without @var{else-forms}.
+@end defmac
+
+@defmac while-let spec then-forms...
+Like @code{when-let}, but repeat until a binding in @var{spec} is
+@code{nil}.  The return value is always @code{nil}.
+@end defmac
+
 @node Combining Conditions
 @section Constructs for Combining Conditions
 @cindex combining conditions
@@ -2366,11 +2408,6 @@ of the @var{cleanup-forms} themselves exits nonlocally 
(via a
 guaranteed to evaluate the rest of them.  If the failure of one of the
 @var{cleanup-forms} has the potential to cause trouble, then protect
 it with another @code{unwind-protect} around that form.
-
-The number of currently active @code{unwind-protect} forms counts,
-together with the number of local variable bindings, against the limit
-@code{max-specpdl-size} (@pxref{Definition of max-specpdl-size,, Local
-Variables}).
 @end defspec
 
   For example, here we make an invisible buffer for temporary use, and
diff --git a/doc/lispref/display.texi b/doc/lispref/display.texi
index a56f467e0b..64400ef931 100644
--- a/doc/lispref/display.texi
+++ b/doc/lispref/display.texi
@@ -5646,10 +5646,9 @@ Every image descriptor must include this property.
 
 @item :file @var{file}
 This says to load the image from file @var{file}.  If @var{file} is
-not an absolute file name, it is expanded relative to the
-@file{images} subdirectory of @code{data-directory}, and failing that,
-relative to the directories listed by @code{x-bitmap-file-path}
-(@pxref{Face Attributes}).
+not an absolute file name, it is expanded relative to each of the
+directories mentioned by @code{image-load-path} (@pxref{Defining
+Images}).
 
 @item :data @var{data}
 This specifies the raw image data.  Each image descriptor must have
@@ -6596,7 +6595,9 @@ Image type @code{webp}.
 This function creates and returns an image descriptor which uses the
 data in @var{file-or-data}.  @var{file-or-data} can be a file name or
 a string containing the image data; @var{data-p} should be @code{nil}
-for the former case, non-@code{nil} for the latter case.
+for the former case, non-@code{nil} for the latter case.  If
+@var{file-or-data} is a relative file name, the function will search
+for it in directories mentioned in @code{image-load-path}.
 
 The optional argument @var{type} is a symbol specifying the image type.
 If @var{type} is omitted or @code{nil}, @code{create-image} tries to
@@ -6849,22 +6850,37 @@ keymap installed in the text properties (or overlays) 
that span the
 displayed image.  This keymap defines the following commands:
 
 @table @kbd
-@item +
+@item i +
 Increase the image size (@code{image-increase-size}).  A prefix value
 of @samp{4} means to increase the size by 40%.  The default is 20%.
 
-@item -
+@item i -
 Decrease the image size (@code{image-increase-size}).  A prefix value
 of @samp{4} means to decrease the size by 40%.  The default is 20%.
 
-@item r
+@item i r
 Rotate the image by 90 degrees clockwise (@code{image-rotate}).
 A prefix means to rotate by 90 degrees counter-clockwise instead.
 
-@item o
+@item i h
+Flip the image horizontally (@code{image-flip-horizontally}).
+
+@item i v
+Flip the image vertically (@code{image-flip-vertically}).
+
+@item i o
 Save the image to a file (@code{image-save}).
+
+@item i c
+Crop the image interactively (@code{image-crop}).
+
+@item i x
+Cut a rectangle from the image interactively (@code{image-cut}).
 @end table
 
+The size and rotation commands are ``repeating'', which means that you
+can continue adjusting the image without using the @kbd{i} prefix.
+
 @node Multi-Frame Images
 @subsection Multi-Frame Images
 @cindex multi-frame images
@@ -8554,6 +8570,7 @@ Display with @var{graphical} on graphical displays, and 
with
 must be one of the display methods described above.
 @end table
 
+@vindex glyphless-char@r{ face}
 @noindent
 The @code{thin-space}, @code{empty-box}, @code{hex-code}, and
 @acronym{ASCII} string display methods are drawn with the
@@ -8563,7 +8580,7 @@ square brackets, @samp{[]}.
 The char-table has one extra slot, which determines how to display any
 character that cannot be displayed with any available font, or cannot
 be encoded by the terminal's coding system.  Its value should be one
-of the above display methods, except @code{zero-width} or a cons cell.
+of the above display methods, except @code{zero-width}.
 
 If a character has a non-@code{nil} entry in an active display table,
 the display table takes effect; in this case, Emacs does not consult
@@ -8618,7 +8635,8 @@ codepoints (typically emojis).
 
 @item no-font
 Characters for which there is no suitable font, or which cannot be
-encoded by the terminal's coding system.
+encoded by the terminal's coding system, or those for which the
+text-mode terminal has no glyphs.
 @end table
 
 @c FIXME: this can also be 'acronym', but that's not currently
diff --git a/doc/lispref/edebug.texi b/doc/lispref/edebug.texi
index 56f7b7bdfa..6a51489d8a 100644
--- a/doc/lispref/edebug.texi
+++ b/doc/lispref/edebug.texi
@@ -1032,9 +1032,8 @@ program.
 @itemize @bullet
 @item
 @vindex edebug-max-depth
-@code{max-lisp-eval-depth} (@pxref{Eval}) and @code{max-specpdl-size}
-(@pxref{Local Variables}) are both increased to reduce Edebug's impact
-on the stack.  You could, however, still run out of stack space when
+@code{max-lisp-eval-depth} (@pxref{Eval}) is increased to reduce Edebug's
+impact on the stack.  You could, however, still run out of stack space when
 using Edebug.  You can also enlarge the value of
 @code{edebug-max-depth} if Edebug reaches the limit of recursion depth
 instrumenting code that contains very large quoted lists.
diff --git a/doc/lispref/eval.texi b/doc/lispref/eval.texi
index ed3cf56e09..11c321b32e 100644
--- a/doc/lispref/eval.texi
+++ b/doc/lispref/eval.texi
@@ -830,7 +830,7 @@ This variable defines the maximum depth allowed in calls to 
@code{eval},
 @code{apply}, and @code{funcall} before an error is signaled (with error
 message @code{"Lisp nesting exceeds max-lisp-eval-depth"}).
 
-This limit, with the associated error when it is exceeded, is one way
+This limit, with the associated error when it is exceeded, is how
 Emacs Lisp avoids infinite recursion on an ill-defined function.  If
 you increase the value of @code{max-lisp-eval-depth} too much, such
 code can cause stack overflow instead.  On some systems, this overflow
@@ -846,14 +846,11 @@ The depth limit counts internal uses of @code{eval}, 
@code{apply}, and
 expressions, and recursive evaluation of function call arguments and
 function body forms, as well as explicit calls in Lisp code.
 
-The default value of this variable is 800.  If you set it to a value
+The default value of this variable is 1600.  If you set it to a value
 less than 100, Lisp will reset it to 100 if the given value is
 reached.  Entry to the Lisp debugger increases the value, if there is
 little room left, to make sure the debugger itself has room to
 execute.
-
-@code{max-specpdl-size} provides another limit on nesting.
-@xref{Definition of max-specpdl-size,, Local Variables}.
 @end defopt
 
 @defvar values
diff --git a/doc/lispref/files.texi b/doc/lispref/files.texi
index 986fb22c75..e1aa2de523 100644
--- a/doc/lispref/files.texi
+++ b/doc/lispref/files.texi
@@ -2445,7 +2445,7 @@ You can use this function for directory names and for 
file names,
 because it recognizes abbreviations even as part of the name.
 @end defun
 
-@defun file-parent-directory filename
+@defun file-name-parent-directory filename
 This function returns the directory name of the parent directory of
 @var{filename}.  If @var{filename} is at the root directory of the
 filesystem, it returns @code{nil}.  A relative @var{filename} is
diff --git a/doc/lispref/frames.texi b/doc/lispref/frames.texi
index 262b86672d..8db6ad0fd3 100644
--- a/doc/lispref/frames.texi
+++ b/doc/lispref/frames.texi
@@ -407,7 +407,7 @@ Name of the physical monitor as @var{string}.
 
 @item source
 Source of the multi-monitor information as @var{string};
-e.g., @samp{XRandr} or @samp{Xinerama}.
+e.g., @samp{XRandR 1.5}, @samp{XRandr} or @samp{Xinerama}.
 @end table
 
 @var{x}, @var{y}, @var{width}, and @var{height} are integers.
@@ -2997,17 +2997,25 @@ explicit focus notifications.)
 @end defun
 
 @defvar after-focus-change-function
-This function is an extension point that code can use to receive a
-notification that focus has changed.
-
-This function is called with no arguments when Emacs notices that the
-set of focused frames may have changed.  Code wanting to do something
-when frame focus changes should use @code{add-function} to add a
-function to this one, and in this added function, re-scan the set of
-focused frames, calling @code{frame-focus-state} to retrieve the last
-known focus state of each frame.  Focus events are delivered
-asynchronously, and frame input focus according to an external system
-may not correspond to the notion of the Emacs selected frame.
+This function is called with no arguments when Emacs notices that a
+frame may have gotten or lost focus.  Focus events are delivered
+asynchronously, and may not be delivered in the expected order, so
+code that wants to do something depending on the state of focused
+frames have go through all the frames and check.
+
+For instance, here's a simple example function that sets the
+background color based on whether the frame has focus or not:
+
+@lisp
+(add-function :after after-focus-change-function
+              #'my-change-background)
+(defun my-change-background ()
+  (dolist (frame (frame-list))
+    (pcase (frame-focus-state frame)
+      (`t (set-face-background 'default "black" frame))
+      (`nil (set-face-background 'default "#404040" frame)))))
+@end lisp
+
 Multiple frames may appear to have input focus simultaneously due to
 focus event delivery differences, the presence of multiple Emacs
 terminals, and other factors, and code should be robust in the face of
diff --git a/doc/lispref/functions.texi b/doc/lispref/functions.texi
index 983dfe2ec5..8b858e0aa0 100644
--- a/doc/lispref/functions.texi
+++ b/doc/lispref/functions.texi
@@ -2476,11 +2476,12 @@ function, of the form @code{(@var{function} 
@var{args}@dots{})}, the macro
 expander will call @var{expander} with that form as well as with
 @var{args}@dots{}, and @var{expander} can either return a new expression to use
 instead of the function call, or it can return just the form unchanged,
-to indicate that the function call should be left alone.  @var{expander} can
-be a symbol, or it can be a form @code{(lambda (@var{arg}) @var{body})} in
-which case @var{arg} will hold the original function call expression, and the
-(unevaluated) arguments to the function can be accessed using the function's
-formal arguments.
+to indicate that the function call should be left alone.
+
+When @var{expander} is a lambda form it should be written with
+a single argument (i.e., be of the form @code{(lambda (@var{arg})
+@var{body})}) because the function's formal arguments are
+automatically added to the lambda's list of arguments for you.
 
 @item (gv-expander @var{expander})
 Declare @var{expander} to be the function to handle calls to the macro (or
diff --git a/doc/lispref/help.texi b/doc/lispref/help.texi
index 463039c5a0..65ad5f0554 100644
--- a/doc/lispref/help.texi
+++ b/doc/lispref/help.texi
@@ -374,25 +374,6 @@ as link in the @file{*Help*} buffer.
 @strong{Please note:} Each @samp{\} must be doubled when written in a
 string in Emacs Lisp.
 
-@defopt text-quoting-style
-@cindex curved quotes
-@cindex curly quotes
-The value of this variable is a symbol that specifies the style Emacs
-should use for single quotes in the wording of help and messages.  If
-the variable's value is @code{curve}, the style is @t{‘like this’}
-with curved single quotes.  If the value is @code{straight}, the style
-is @t{'like this'} with straight apostrophes.  If the value is
-@code{grave}, quotes are not translated and the style is @t{`like
-this'} with grave accent and apostrophe, the standard style before
-Emacs version 25.  The default value @code{nil} acts like @code{curve}
-if curved single quotes seem to be displayable, and like @code{grave}
-otherwise.
-
-This option is useful on platforms that have problems with curved
-quotes.  You can customize it freely according to your personal
-preference.
-@end defopt
-
 @defun substitute-command-keys string &optional no-face include-menus
 @vindex help-key-binding@r{ (face)}
 This function scans @var{string} for the above special sequences and
@@ -403,6 +384,11 @@ given a special face @code{help-key-binding}, but if the 
optional
 argument @var{no-face} is non-@code{nil}, the function doesn't add
 this face to the produced string.
 
+@defun substitute-quotes string
+This function works like @code{substitute-command-keys}, but only
+replaces quote characters.
+@end defun
+
 @cindex advertised binding
 If a command has multiple bindings, this function normally uses the
 first one it finds.  You can specify one particular key binding by
@@ -505,6 +491,13 @@ quotes.  You can customize it freely according to your 
personal
 preference.
 @end defopt
 
+@defun text-quoting-style
+You should not read the value of the variable
+@code{text-quoting-style} directly.  Instead, use this function with
+the same name to dynamically compute the correct quoting style on the
+current terminal in the @code{nil} case described above.
+@end defun
+
 @node Describing Characters
 @section Describing Characters for Help Messages
 @cindex describe characters and events
@@ -834,7 +827,7 @@ if the user types the help character again.
 
 Emacs can list functions based on various groupings.  For instance,
 @code{string-trim} and @code{mapconcat} are ``string'' functions, so
-@kbd{M-x shortdoc-display-group RET string RET} will give an overview
+@kbd{M-x shortdoc RET string RET} will give an overview
 of functions that operate on strings.
 
 The documentation groups are created with the
diff --git a/doc/lispref/internals.texi b/doc/lispref/internals.texi
index 8d2089bad8..ea1679f693 100644
--- a/doc/lispref/internals.texi
+++ b/doc/lispref/internals.texi
@@ -3004,8 +3004,8 @@ Using @code{bool} can make programs easier to read and a 
bit faster than
 using @code{int}.  Although it is also OK to use @code{int}, @code{0}
 and @code{1}, this older style is gradually being phased out.  When
 using @code{bool}, respect the limitations of the replacement
-implementation of @code{bool}, as documented in the source file
-@file{lib/stdbool.in.h}.  In particular, boolean bitfields should be of type
+implementation of @code{bool}.  In particular,
+boolean bitfields should be of type
 @code{bool_bf}, not @code{bool}, so that they work correctly even when
 compiling Objective C with standard GCC.
 
diff --git a/doc/lispref/keymaps.texi b/doc/lispref/keymaps.texi
index 2be31d63a6..1e4bf4eb86 100644
--- a/doc/lispref/keymaps.texi
+++ b/doc/lispref/keymaps.texi
@@ -2911,6 +2911,10 @@ The @code{:rtl} property specifies an alternative image 
to use for
 right-to-left languages.  Only the GTK+ version of Emacs supports this
 at present.
 
+Some toolkits display both an image and a text in the toolbar.  If you
+want to force using only the image, use a @code{:vert-only}
+non-@code{nil} property.
+
 Like the menu bar, the tool bar can display separators (@pxref{Menu
 Separators}).  Tool bar separators are vertical rather than
 horizontal, though, and only a single style is supported.  They are
diff --git a/doc/lispref/minibuf.texi b/doc/lispref/minibuf.texi
index f2adc01c8f..089ae41f32 100644
--- a/doc/lispref/minibuf.texi
+++ b/doc/lispref/minibuf.texi
@@ -490,6 +490,9 @@ If @var{default} is @code{nil}, there is no default value, 
and
 therefore no ``default value'' string is included in the result value.
 If @var{default} is a non-@code{nil} list, the first element of the
 list is used in the prompt.
+
+Both @var{prompt} and @code{minibuffer-default-prompt-format} are run
+through @code{substitute-command-keys} (@pxref{Keys in Documentation}).
 @end defun
 
 @defvar read-minibuffer-restore-windows
diff --git a/doc/lispref/nonascii.texi b/doc/lispref/nonascii.texi
index 6dc23637a7..006966daed 100644
--- a/doc/lispref/nonascii.texi
+++ b/doc/lispref/nonascii.texi
@@ -404,9 +404,12 @@ This returns @code{t} if @var{charcode} is a valid 
character, and
 
 @cindex maximum value of character codepoint
 @cindex codepoint, largest value
-@defun max-char
+@defun max-char &optional unicode
 This function returns the largest value that a valid character
-codepoint can have.
+codepoint can have in Emacs.  If the optional argument @var{unicode}
+is non-@code{nil}, it returns the largest character codepoint defined
+by the Unicode Standard (which is smaller than the maximum codepoint
+supported by Emacs).
 
 @example
 @group
@@ -460,7 +463,7 @@ of character properties.  In particular, Emacs supports the
 @uref{https://www.unicode.org/reports/tr23/, Unicode Character Property
 Model}, and the Emacs character property database is derived from the
 Unicode Character Database (@acronym{UCD}).  See the
-@uref{https://www.unicode.org/versions/Unicode14.0.0/ch04.pdf, Character
+@uref{https://www.unicode.org/versions/Unicode15.0.0/ch04.pdf, Character
 Properties chapter of the Unicode Standard}, for a detailed
 description of Unicode character properties and their meaning.  This
 section assumes you are already familiar with that chapter of the
diff --git a/doc/lispref/os.texi b/doc/lispref/os.texi
index 3582801841..3e16ac0eb4 100644
--- a/doc/lispref/os.texi
+++ b/doc/lispref/os.texi
@@ -3175,6 +3175,9 @@ This is not detected by this function, and so a 
non-@code{nil} return
 value does not guarantee that changes on @var{file} will be actually
 notified.
 
+If @var{file} is a symlink, it doesn't follow that link.  Just
+@var{file} itself will be watched.
+
 @var{flags} is a list of conditions to set what will be watched for.
 It can include the following symbols:
 
diff --git a/doc/lispref/sequences.texi b/doc/lispref/sequences.texi
index 39230d0adc..bc5a4cf24a 100644
--- a/doc/lispref/sequences.texi
+++ b/doc/lispref/sequences.texi
@@ -446,8 +446,7 @@ useful example of @code{sort}.
 @cindex seq library
 @cindex sequences, generalized
   The @file{seq.el} library provides the following additional sequence
-manipulation macros and functions, prefixed with @code{seq-}.  To use
-them, you must first load the @file{seq} library.
+manipulation macros and functions, prefixed with @code{seq-}.
 
   All functions defined in this library are free of side-effects;
 i.e., they do not modify any sequence (list, vector, or string) that
@@ -681,6 +680,37 @@ for which @var{predicate} returns @code{nil}.
 @end example
 @end defun
 
+@defun seq-remove-at-position sequence n
+@cindex removing from sequences
+This function returns a copy of @var{sequence} where the element at
+(zero-based) index @var{n} got removed.  The result is a sequence of
+the same type as @var{sequence}.
+
+@example
+@group
+(seq-remove-at-position [1 -1 3 -3 5] 0)
+@result{} [-1 3 -3 5]
+@end group
+@group
+(seq-remove-at-position [1 -1 3 -3 5] 3)
+@result{} [1 -1 3 5]
+@end group
+@end example
+@end defun
+
+@defun seq-keep function sequence
+  This function returns a list of all non-@code{nil} results from
+calling @var{function} on the elements in @var{sequence}.
+
+@example
+@group
+(seq-keep #'cl-digit-char-p '(?6 ?a ?7))
+@result{} (6 7)
+@end group
+@end example
+
+@end defun
+
 @defun seq-reduce function sequence initial-value
 @cindex reducing sequences
   This function returns the result of calling @var{function} with
@@ -864,7 +894,7 @@ arguments to use instead of the default @code{equal}.
 @end defun
 
 @defun seq-position sequence elt &optional function
-  This function returns the index of the first element in
+  This function returns the (zero-based) index of the first element in
 @var{sequence} that is equal to @var{elt}.  If the optional argument
 @var{function} is non-@code{nil}, it is a function of two arguments to
 use instead of the default @code{equal}.
@@ -881,6 +911,27 @@ use instead of the default @code{equal}.
 @end example
 @end defun
 
+@defun seq-positions sequence elt &optional testfn
+  This function returns a list of the (zero-based) indices of the
+elements in @var{sequence} for which @var{testfn} returns
+non-@code{nil} when passed the element and @var{elt} as
+arguments. @var{testfn} defaults to @code{equal}.
+
+@example
+@group
+(seq-positions '(a b c a d) 'a)
+@result{} (0 3)
+@end group
+@group
+(seq-positions '(a b c a d) 'z)
+@result{} nil
+@end group
+@group
+(seq-positions '(11 5 7 12 9 15) 10 #'>=)
+@result{} (0 3 5)
+@end group
+@end example
+@end defun
 
 @defun seq-uniq sequence &optional function
   This function returns a list of the elements of @var{sequence} with
diff --git a/doc/lispref/strings.texi b/doc/lispref/strings.texi
index 374381e595..cf961e9e7c 100644
--- a/doc/lispref/strings.texi
+++ b/doc/lispref/strings.texi
@@ -539,21 +539,10 @@ string or symbol, @code{string=} signals an error.
      @result{} nil
 @end example
 
-For technical reasons, a unibyte and a multibyte string are
-@code{equal} if and only if they contain the same sequence of
-character codes and all these codes are either in the range 0 through
-127 (@acronym{ASCII}) or 160 through 255 (@code{eight-bit-graphic}).
-However, when a unibyte string is converted to a multibyte string, all
-characters with codes in the range 160 through 255 are converted to
-characters with higher codes, whereas @acronym{ASCII} characters
-remain unchanged.  Thus, a unibyte string and its conversion to
-multibyte are only @code{equal} if the string is all @acronym{ASCII}.
-Character codes 160 through 255 are not entirely proper in multibyte
-text, even though they can occur.  As a consequence, the situation
-where a unibyte and a multibyte string are @code{equal} without both
-being all @acronym{ASCII} is a technical oddity that very few Emacs
-Lisp programmers ever get confronted with.  @xref{Text
-Representations}.
+A unibyte and a multibyte string are equal in the sense of
+@code{string=} if and only if they contain the same sequence of
+character codes all being in the range 0--127 (@acronym{ASCII}).
+@xref{Text Representations}.
 @end defun
 
 @defun string-equal string1 string2
@@ -1293,6 +1282,11 @@ The order of specifications in @var{template} need not 
correspond to
 the order of associations in @var{spec-alist}.
 @end itemize
 
+REPLACEMENT can also be a function taking no arguments, and returning
+a string to be used for the replacement.  It will only be called when
+the corresponding LETTER is used in the TEMPLATE.  This is useful, for
+example, to avoid prompting for input unless it is needed.
+
 The optional argument @var{ignore-missing} indicates how to handle
 specification characters in @var{template} that are not found in
 @var{spec-alist}.  If it is @code{nil} or omitted, the function
diff --git a/doc/lispref/symbols.texi b/doc/lispref/symbols.texi
index 336fa9c918..ea1e086ebf 100644
--- a/doc/lispref/symbols.texi
+++ b/doc/lispref/symbols.texi
@@ -613,7 +613,10 @@ file-local evaluation forms.  @xref{File Local Variables}.
 
 @item safe-local-variable
 The value specifies a function for determining safe file-local values
-for the named variable.  @xref{File Local Variables}.
+for the named variable.  @xref{File Local Variables}.  Since this
+value is consulted when loading files, the function should be
+efficient and should ideally not lead to loading any libraries to
+determine the safeness (e.g., it should not be an autoloaded function).
 
 @item side-effect-free
 @cindex @code{side-effect-free} property
diff --git a/doc/lispref/variables.texi b/doc/lispref/variables.texi
index 80d6a01412..1d891618da 100644
--- a/doc/lispref/variables.texi
+++ b/doc/lispref/variables.texi
@@ -358,27 +358,6 @@ Variables}); a few variables have terminal-local bindings
 like ordinary local bindings, but they are localized depending on
 where you are in Emacs.
 
-@defopt max-specpdl-size
-@anchor{Definition of max-specpdl-size}
-@cindex variable limit error
-@cindex evaluation error
-@cindex infinite recursion
-This variable defines the limit on the total number of local variable
-bindings and @code{unwind-protect} cleanups (@pxref{Cleanups,,
-Cleaning Up from Nonlocal Exits}) that are allowed before Emacs
-signals an error (with data @code{"Variable binding depth exceeds
-max-specpdl-size"}).
-
-This limit, with the associated error when it is exceeded, is one way
-that Lisp avoids infinite recursion on an ill-defined function.
-@code{max-lisp-eval-depth} provides another limit on depth of nesting.
-@xref{Definition of max-lisp-eval-depth,, Eval}.
-
-The default value is 1600.  Entry to the Lisp debugger increases the
-value, if there is little room left, to make sure the debugger itself
-has room to execute.
-@end defopt
-
 @node Void Variables
 @section When a Variable is Void
 @cindex @code{void-variable} error
@@ -2635,15 +2614,15 @@ is a set of forms that can be generalized variables in 
Lisp.
 
 The @code{setf} macro is the most basic way to operate on generalized
 variables.  The @code{setf} form is like @code{setq}, except that it
-accepts arbitrary place forms on the left side rather than just
-symbols.  For example, @code{(setf (car a) b)} sets the car of
-@code{a} to @code{b}, doing the same operation as @code{(setcar a b)},
-but without you having to use two separate functions for setting and
-accessing this type of place.
+accepts arbitrary place forms in the first (left) argument of each
+pair rather than just symbols.  For example, @code{(setf (car a) b)}
+sets the car of @code{a} to @code{b}, doing the same operation as
+@code{(setcar a b)}, but without you having to use two separate
+functions for setting and accessing this type of place.
 
 @defmac setf [place form]@dots{}
-This macro evaluates @var{form} and stores it in @var{place}, which
-must be a valid generalized variable form.  If there are several
+This macro evaluates @var{form} and stores its value in @var{place},
+which must be a valid generalized variable form.  If there are several
 @var{place} and @var{form} pairs, the assignments are done sequentially
 just as with @code{setq}.  @code{setf} returns the value of the last
 @var{form}.
@@ -2718,7 +2697,17 @@ a
      @result{} ("hello" "wood")
 @end example
 
-@c FIXME?  Also 'eq'? (see gv.el)
+@item
+The @code{if} and @code{cond} conditionals will work as generalized
+variables.  For instance, this will set either the @code{foo} or the
+@code{bar} variable to @code{zot}:
+
+@example
+(setf (if (zerop (random 2))
+         foo
+       bar)
+      'zot)
+@end example
 @end itemize
 
 @noindent
diff --git a/doc/lispref/windows.texi b/doc/lispref/windows.texi
index c7f014e2f3..ee3b15992b 100644
--- a/doc/lispref/windows.texi
+++ b/doc/lispref/windows.texi
@@ -1472,20 +1472,36 @@ the new root window.
    For interactive use, Emacs provides two commands which always split
 the selected window.  These call @code{split-window} internally.
 
-@deffn Command split-window-right &optional size
-This function splits the selected window into two side-by-side
-windows, putting the selected window on the left.  If @var{size} is
-positive, the left window gets @var{size} columns; if @var{size} is
+@deffn Command split-window-right &optional size window-to-split
+This function splits the window @var{window-to-split} into two
+side-by-side windows, putting @var{window-to-split} on the left.
+@var{window-to-split} defaults to the selected window.  If @var{size}
+is positive, the left window gets @var{size} columns; if @var{size} is
 negative, the right window gets @minus{}@var{size} columns.
 @end deffn
 
-@deffn Command split-window-below &optional size
-This function splits the selected window into two windows, one above
-the other, leaving the upper window selected.  If @var{size} is
-positive, the upper window gets @var{size} lines; if @var{size} is
+@deffn Command split-window-below &optional size window-to-split
+This function splits the window @var{window-to-split} into two
+windows, one above the other, leaving the upper window selected.
+@var{window-to-split} defaults to the selected window.  If @var{size}
+is positive, the upper window gets @var{size} lines; if @var{size} is
 negative, the lower window gets @minus{}@var{size} lines.
 @end deffn
 
+@deffn Command split-root-window-below &optional size
+This function splits the whole frame in two.  The current window
+configuration is retained on the top, and a new window is created
+below, taking up the whole width of the frame.  @var{size} is treated
+as by @code{split-window-below}.
+@end deffn
+
+@deffn Command split-root-window-right &optional size
+This function splits the whole frame in two.  The current window
+configuration is retained on the left, and a new window is created on
+the right, taking up the whole height of the frame.  @var{size} is treated
+as by @code{split-window-right}.
+@end deffn
+
 @defopt split-window-keep-point
 If the value of this variable is non-@code{nil} (the default),
 @code{split-window-below} behaves as described above.
@@ -6476,7 +6492,7 @@ during redisplay provided a significant, non-scrolling 
change of a
 window has been detected.  For simplicity, these hooks and the
 functions they call will be collectively referred to as @dfn{window
 change functions}.  As any hook, these hooks can be set either
-globally of buffer-locally via the @var{local} argument of
+globally or buffer-locally via the @var{local} argument of
 @code{add-hook} (@pxref{Setting Hooks}) when the hook is installed.
 
 @cindex window buffer change
diff --git a/doc/man/emacsclient.1 b/doc/man/emacsclient.1
index e5d1bbe09a..83c8a366f8 100644
--- a/doc/man/emacsclient.1
+++ b/doc/man/emacsclient.1
@@ -1,5 +1,5 @@
 .\" See section COPYING for conditions for redistribution.
-.TH EMACSCLIENT 1 "2021-11-05" "GNU Emacs" "GNU"
+.TH EMACSCLIENT 1 "2022-09-05" "GNU Emacs" "GNU"
 .\" NAME should be all caps, SECTION should be 1-8, maybe w/ subsection
 .\" other params are allowed: see man(7), man(1)
 .SH NAME
@@ -87,9 +87,12 @@ Use TCP configuration file FILENAME for communication.
 This can also be specified via the EMACS_SERVER_FILE environment variable.
 .TP
 .B \-n, \-\-no-wait
-Return
-immediately without waiting for you to "finish" the buffer in Emacs.
-If combined with --eval, this option is ignored.
+Return immediately without waiting for you to "finish" the buffer in
+Emacs.  If combined with --eval, this option is ignored.
+.TP
+.B \-w, \-\-timeout=N
+How long to wait, in seconds, for Emacs to respond before giving up.
+The default is 0, which means to wait forever.
 .TP
 .B \-nw, \-t, \-\-tty
 Open a new Emacs frame on the current terminal.
diff --git a/doc/misc/calc.texi b/doc/misc/calc.texi
index 98f59b89c0..89a340e734 100644
--- a/doc/misc/calc.texi
+++ b/doc/misc/calc.texi
@@ -10392,7 +10392,6 @@ memory than it would otherwise, but it's guaranteed to 
fix the problem.
 @cindex Recursion depth
 @cindex ``Computation got stuck'' message
 @cindex @code{max-lisp-eval-depth}
-@cindex @code{max-specpdl-size}
 Calc uses recursion in many of its calculations.  Emacs Lisp keeps a
 variable @code{max-lisp-eval-depth} which limits the amount of recursion
 possible in an attempt to recover from program bugs.  If a calculation
@@ -10406,9 +10405,6 @@ is also an @kbd{I M} (@code{calc-less-recursion-depth}) 
command which
 decreases this limit by a factor of two, down to a minimum value of 200.
 The default value is 1000.
 
-These commands also double or halve @code{max-specpdl-size}, another
-internal Lisp recursion limit.  The minimum value for this limit is 600.
-
 @node Caches
 @subsection Caches
 
diff --git a/doc/misc/ede.texi b/doc/misc/ede.texi
index 9867883b24..c0c2ef93d9 100644
--- a/doc/misc/ede.texi
+++ b/doc/misc/ede.texi
@@ -1031,8 +1031,9 @@ superclasses.  In this way, specific behaviors such as 
how a project
 is saved, or how a target is compiled can be customized by a project
 author in detail.  @ede{} communicates to these project objects via an
 API using methods.  The commands you use in @ede{} mode are high-level
-functional wrappers over these methods.  @xref{Top,,, eieio, EIEIO manual}. For
-details on using @eieio{} to extending classes, and writing methods.
+functional wrappers over these methods.  @xref{Top,,, eieio, EIEIO
+manual} for details on using @eieio{} to extending classes, and
+writing methods.
 
 If you intend to extend @ede{}, it is most likely that a new target type is
 needed in one of the existing project types.  The rest of this chapter
diff --git a/doc/misc/ediff.texi b/doc/misc/ediff.texi
index cbc7556aa8..23334479b0 100644
--- a/doc/misc/ediff.texi
+++ b/doc/misc/ediff.texi
@@ -50,7 +50,7 @@ modify this GNU manual.''
 @titlepage
 @title Ediff User's Manual
 @sp 4
-@subtitle Ediff version 2.81.2
+@subtitle Ediff version 2.81.6
 @sp 1
 @subtitle November 2008
 @sp 5
diff --git a/doc/misc/efaq.texi b/doc/misc/efaq.texi
index a3459abd04..0da397919d 100644
--- a/doc/misc/efaq.texi
+++ b/doc/misc/efaq.texi
@@ -1602,6 +1602,7 @@ is better to write ``Emacs and XEmacs.''
 * Filling paragraphs with a single space::
 * Escape sequences in shell output::
 * Fullscreen mode on MS-Windows::
+* Emacs in a Linux console::
 @end menu
 
 @node Setting up a customization file
@@ -1809,9 +1810,6 @@ optional display.  Alternatively, you can use the
 customize @code{display-line-numbers-type} with the same value as you
 would use with @code{display-line-numbers}.
 
-There is also the @samp{linum} package which will henceforth become
-obsolete.  We recommend using @samp{display-line-numbers} instead.
-
 @node Displaying the current file name in the titlebar
 @section How can I modify the titlebar to contain the current file name?
 @cindex Titlebar, displaying the current file name in
@@ -2363,16 +2361,7 @@ new paragraph.  There are many packages available to 
deal with this
 @cindex Pairs of parentheses, highlighting
 @cindex Matching parentheses
 
-Call @code{show-paren-mode} in your init file (@pxref{Setting up a
-customization file}):
-
-@lisp
-(show-paren-mode 1)
-@end lisp
-
-You can also enable this mode by selecting the @samp{Paren Match
-Highlighting} option from the @samp{Options} menu of the Emacs menu bar
-at the top of any Emacs frame.
+By default, @code{show-paren-mode} is enabled in all editing buffers.
 
 Alternatives to this mode include:
 
@@ -3031,6 +3020,115 @@ To compute the correct values for width and height, 
first maximize the
 Emacs frame and then evaluate @code{(frame-height)} and
 @code{(frame-width)} with @kbd{M-:}.
 
+@node Emacs in a Linux console
+@section How can I alleviate the limitations of the Linux console?
+@cindex Console, Linux console, TTY, fbterm
+
+If possible, we recommend running Emacs inside @command{fbterm}, when
+in a Linux console.  This brings the Linux console on par with most
+terminal emulators under X.  To do this, install @command{fbterm}, for
+example with the package manager of your GNU/Linux distribution, and
+execute the command
+
+@example
+$ fbterm
+@end example
+
+This will create a sample configuration file @file{~/.fbtermrc} in
+your home directory.  Edit that file and change the options
+@code{font-names} and @code{font-size} if necessary.  For the former,
+you can choose one or more of the lines in the output of the following
+command, separated by commas:
+
+@example
+$ fc-list :spacing=mono family | sed 's/ /\\ /g'
+@end example
+
+@noindent
+Note that you can fine-tune the appearance of the fonts by adding
+attribute-value pairs, separated by colons, after each font name.  For
+example,
+
+@example
+font-names=DejaVu\ Sans\ Mono:style=bold:antialias=false
+@end example
+
+@noindent
+selects the bold style of the DejaVu Sans Mono font, and disables
+anti-aliasing.
+
+You can now start Emacs inside @command{fbterm} with the command
+
+@example
+$ fbterm -- env TERM=fbterm emacs
+@end example
+
+In some versions of @command{fbterm}, setting @env{TERM} to
+@samp{fbterm} can be omitted.  To check whether it is needed, start
+Emacs inside @command{fbterm} with the command
+
+@example
+$ fbterm -- emacs
+@end example
+
+@noindent
+and type @kbd{M-x list-colors-display}.  If only 8 colors are
+displayed, it is necessary; if 256 colors are displayed, it isn't.
+
+You may want to add an alias for that command in your shell
+configuration file.  For example, if you use Bash, you can add the
+following line to your @file{~/.bashrc} file:
+
+@example
+alias emacs="fbterm -- env TERM=fbterm emacs"
+@end example
+
+@noindent
+or, if you use Emacs both in the Linux console and under X:
+
+@example
+[[ "$(tty)" =~ "/dev/tty" ]] && alias emacs="fbterm -- env TERM=fbterm emacs"
+@end example
+
+The @command{fbterm} terminal emulator may define a number of key
+bindings for its own use, some of which conflict with those that Emacs
+uses.  Execute the following two commands as root to ensure that
+@command{fbterm} does not define these key bindings:
+
+@example
+# chmod a-s `which fbterm`
+# setcap cap_sys_tty_config=-ep `which fbterm`
+@end example
+
+If you use Emacs as root, the above is not enough however, because the
+root user has all privileges.  You can use the following command to
+start Emacs inside @command{fbterm} as root while ensuring that
+@command{fbterm} does not define any key bindings for its own use:
+
+@example
+# capsh --drop=cap_sys_tty_config -- -c "fbterm -- env TERM=fbterm emacs"
+@end example
+
+Again you may want to add a shortcut for that command in the shell
+configuration file of the root user.  In this case however, it is not
+possible to use an alias, because the command line arguments passed to
+Emacs need to be inserted in the string at the end of the command.  A
+wrapper script or a function can be used to do that.  For example, if
+you use Bash, you can add the following function in the root user
+@file{~/.bashrc} file:
+
+@example
+function emacs ()
+@{
+  CMD="fbterm -- env TERM=fbterm emacs "
+  for ARG in "$@@"
+  do
+    CMD="$CMD '$ARG' "
+  done
+  capsh --drop=cap_sys_tty_config -- -c "$CMD"
+@}
+@end example
+
 @c ------------------------------------------------------------
 @node Bugs and problems
 @chapter Bugs and problems
diff --git a/doc/misc/eieio.texi b/doc/misc/eieio.texi
index 18a2b74033..b1ec5c0dce 100644
--- a/doc/misc/eieio.texi
+++ b/doc/misc/eieio.texi
@@ -193,7 +193,7 @@ also differs in some other aspects which are mentioned 
below (also
 @enumerate
 @item
 A structured framework for the creation of basic classes with attributes
-and methods using singular inheritance similar to CLOS.
+and methods using inheritance similar to CLOS.
 @item
 Type checking, and slot unbinding.
 @item
@@ -225,11 +225,6 @@ lacks:
 
 @table @asis
 
-@item Method dispatch
-EIEO does not support method dispatch for built-in types and multiple
-arguments types.  In other words, method dispatch only looks at the
-first argument, and this one must be an @eieio{} type.
-
 @item Support for metaclasses
 There is just one default metaclass, @code{eieio-default-superclass},
 and you cannot define your own.  The @code{:metaclass} tag in
@@ -856,11 +851,6 @@ You can also create a generic method with 
@code{cl-defmethod}
 (@pxref{Methods}).  When a method is created and there is no generic
 method in place with that name, then a new generic will be created,
 and the new method will use it.
-
-In CLOS, a generic method can also be used to provide an argument list
-and dispatch precedence for all the arguments.  In @eieio{},
-dispatching only occurs for the first argument, so the @var{arglist}
-is not used.
 @end defmac
 
 @node Methods
diff --git a/doc/misc/eshell.texi b/doc/misc/eshell.texi
index 13f13163dd..0ee33f2c2a 100644
--- a/doc/misc/eshell.texi
+++ b/doc/misc/eshell.texi
@@ -256,7 +256,6 @@ as an argument will ``spread'' the elements into multiple 
arguments:
 @end example
 
 @subsection Quoting and escaping
-
 As with other shells, you can escape special characters and spaces
 with by prefixing the character with a backslash (@code{\}), or by
 surrounding the string with apostrophes (@code{''}) or double quotes
@@ -268,6 +267,40 @@ When using expansions (@pxref{Expansion}) in an Eshell 
command, the
 result may potentially be of any data type.  To ensure that the result
 is always a string, the expansion can be surrounded by double quotes.
 
+@subsection Special argument types
+In addition to strings and numbers, Eshell supports a number of
+special argument types.  These let you refer to various other Emacs
+Lisp data types, such as lists or buffers.
+
+@table @code
+
+@item #'@var{lisp-form}
+This refers to the quoted Emacs Lisp form @var{lisp-form}.  Though
+this looks similar to the ``sharp quote'' syntax for functions
+(@pxref{Special Read Syntax, , , elisp, The Emacs Lisp Reference
+Manual}), it instead corresponds to @code{quote} and can be used for
+any quoted form.@footnote{Eshell would interpret a bare apostrophe
+(@code{'}) as the start of a single-quoted string.}
+
+@item `@var{lisp-form}
+This refers to the backquoted Emacs Lisp form @var{lisp-form}
+(@pxref{Backquote, , , elisp, The Emacs Lisp Reference Manual}).  As
+in Emacs Lisp, you can use @samp{,} and @samp{,@@} to refer to
+non-constant values.
+
+@item #<buffer @var{name}>
+@itemx #<@var{name}>
+Return the buffer named @var{name}.  This is equivalent to
+@samp{$(get-buffer-create "@var{name}")} (@pxref{Creating Buffers, , ,
+elisp, The Emacs Lisp Reference Manual}).
+
+@item #<process @var{name}>
+Return the process named @var{name}.  This is equivalent to
+@samp{$(get-process "@var{name}")}  (@pxref{Process Information, , ,
+elisp, The Emacs Lisp Reference Manual}).
+
+@end table
+
 @node Built-ins
 @section Built-in commands
 Several commands are built-in in Eshell.  In order to call the
@@ -1560,6 +1593,13 @@ Reverses the order of a list of values.
 Since Eshell does not communicate with a terminal like most command
 shells, IO is a little different.
 
+@menu
+* Visual Commands::
+* Redirection::
+* Pipelines::
+@end menu
+
+@node Visual Commands
 @section Visual Commands
 If you try to run programs from within Eshell that are not
 line-oriented, such as programs that use ncurses, you will just get
@@ -1592,40 +1632,142 @@ program exits, customize the variable
 @code{eshell-destroy-buffer-when-process-dies} to a non-@code{nil}
 value; the default is @code{nil}.
 
+@node Redirection
 @section Redirection
-Redirection is mostly the same in Eshell as it is in other command
-shells.  The output redirection operators @code{>} and @code{>>} as
-well as pipes are supported, but there is not yet any support for
-input redirection.  Output can also be redirected to buffers, using
-the @code{>>>} redirection operator, and Elisp functions, using
-virtual devices.
-
-The buffer redirection operator, @code{>>>}, expects a buffer object
-on the right-hand side, into which it inserts the output of the
-left-hand side.  e.g., @samp{echo hello >>> #<buffer *scratch*>}
-inserts the string @code{"hello"} into the @file{*scratch*} buffer.
-The convenience shorthand variant @samp{#<@var{buffer-name}>}, as in
-@samp{#<*scratch*>}, is also accepted.
-
-@code{eshell-virtual-targets} is a list of mappings of virtual device
-names to functions.  Eshell comes with two virtual devices:
-@file{/dev/kill}, which sends the text to the kill ring, and
-@file{/dev/clip}, which sends text to the clipboard.
+Redirection in Eshell is similar to that of other command shells.  You
+can use the output redirection operators @code{>} and @code{>>}, but
+there is not yet any support for input redirection.  In the cases
+below, @var{fd} specifies the file descriptor to redirect; if not
+specified, file descriptor 1 (standard output) will be used by
+default.
+
+@table @code
+
+@item > @var{dest}
+@itemx @var{fd}> @var{dest}
+Redirect output to @var{dest}, overwriting its contents with the new
+output.
+
+@item >> @var{dest}
+@itemx @var{fd}>> @var{dest}
+Redirect output to @var{dest}, appending it to the existing contents
+of @var{dest}.
+
+@item >>> @var{buffer}
+@itemx @var{fd}>>> @var{buffer}
+Redirect output to @var{dest}, inserting it at the current mark if
+@var{dest} is a buffer, at the beginning of the file if @var{dest} is
+a file, or otherwise behaving the same as @code{>>}.
+
+@item &> @var{file}
+@itemx >& @var{file}
+Redirect both standard output and standard error to @var{dest},
+overwriting its contents with the new output.
+
+@item &>> @var{file}
+@itemx >>& @var{file}
+Redirect both standard output and standard error to @var{dest},
+appending it to the existing contents of @var{dest}.
+
+@item &>>> @var{file}
+@itemx >>>& @var{file}
+Redirect both standard output and standard error to @var{dest},
+inserting it like with @code{>>> @var{file}}.
+
+@item >&@var{other-fd}
+@itemx @var{fd}>&@var{other-fd}
+Duplicate the file descriptor @var{other-fd} to @var{fd} (or 1 if
+unspecified).  The order in which this is used is significant, so
+
+@example
+@var{command} > @var{file} 2>&1
+@end example
+
+redirects both standard output and standard error to @var{file},
+whereas
+
+@example
+@var{command} 2>&1 > @var{file}
+@end example
+
+only redirects standard output to @var{file} (and sends standard error
+to the display via standard output's original handle).
+
+@end table
+
+Eshell supports redirecting output to several different types of
+targets:
+
+@itemize @bullet
 
+@item
+files, including virtual targets (see below);
+
+@item
+buffers (@pxref{Buffers, , , elisp, GNU Emacs Lisp Reference Manual});
+
+@item
+markers (@pxref{Markers, , , elisp, GNU Emacs Lisp Reference Manual});
+
+@item
+processes (@pxref{Processes, , , elisp, GNU Emacs Lisp Reference
+Manual}); and
+
+@item
+symbols (@pxref{Symbols, , , elisp, GNU Emacs Lisp Reference Manual}).
+
+@end itemize
+
+@subsection Virtual Targets
+Virtual targets are mapping of device names to functions.  Eshell
+comes with four virtual devices:
+
+@table @file
+
+@item /dev/null
+Does nothing with the output passed to it.
+
+@item /dev/eshell
+Writes the text passed to it to the display.
+
+@item /dev/kill
+Adds the text passed to it to the kill ring.
+
+@item /dev/clip
+Adds the text passed to it to the clipboard.
+
+@end table
+
+@vindex eshell-virtual-targets
 You can, of course, define your own virtual targets.  They are defined
-by adding a list of the form @samp{("/dev/name" @var{function} @var{mode})} to
-@code{eshell-virtual-targets}.  The first element is the device name;
-@var{function} may be either a lambda or a function name.  If
-@var{mode} is @code{nil}, then the function is the output function; if it is
-non-@code{nil}, then the function is passed the redirection mode as a
-symbol--@code{overwrite} for @code{>}, @code{append} for @code{>>}, or
-@code{insert} for @code{>>>}--and the function is expected to return
-the output function.
+by adding a list of the form @samp{("/dev/name" @var{function}
+@var{mode})} to @code{eshell-virtual-targets}.  The first element is
+the device name; @var{function} may be either a lambda or a function
+name.  If @var{mode} is @code{nil}, then the function is the output
+function; if it is non-@code{nil}, then the function is passed the
+redirection mode as a symbol--@code{overwrite} for @code{>},
+@code{append} for @code{>>}, or @code{insert} for @code{>>>}--and the
+function is expected to return the output function.
 
 The output function is called once on each line of output until
 @code{nil} is passed, indicating end of output.
 
-@section Running Shell Pipelines Natively
+@node Pipelines
+@section Pipelines
+As with most other shells, Eshell supports pipelines to pass the
+output of one command the input of the next command.  You can send the
+standard output of one command to the standard input of another using
+the @code{|} operator.  For example,
+
+@example
+~ $ echo hello | rev
+olleh
+@end example
+
+To send both the standard output and standard error of a command to
+another command's input, you can use the @code{|&} operator.
+
+@subsection Running Shell Pipelines Natively
 When constructing shell pipelines that will move a lot of data, it is
 a good idea to bypass Eshell's own pipelining support and use the
 operating system shell's instead.  This is especially relevant when
@@ -2113,10 +2255,9 @@ current being used.
 
 @item How can Eshell learn if a background process has requested input?
 
-@item Support @samp{2>&1} and @samp{>&} and @samp{2>} and @samp{|&}
+@item Make a customizable syntax table for redirects
 
-The syntax table for parsing these should be customizable, such that the
-user could change it to use rc syntax: @samp{>[2=1]}.
+This way, the user could change it to use rc syntax: @samp{>[2=1]}.
 
 @item Allow @samp{$_[-1]}, which would indicate the last element of the array
 
diff --git a/doc/misc/flymake.texi b/doc/misc/flymake.texi
index 953e4605e9..7406557623 100644
--- a/doc/misc/flymake.texi
+++ b/doc/misc/flymake.texi
@@ -1,8 +1,8 @@
 \input texinfo   @c -*- mode: texinfo; coding: utf-8 -*-
 @comment %**start of header
 @setfilename ../../info/flymake.info
-@set VERSION 1.2
-@set UPDATED September 2021
+@set VERSION 1.2.2
+@set UPDATED November 2021
 @settitle GNU Flymake @value{VERSION}
 @include docstyle.texi
 @syncodeindex pg cp
@@ -801,6 +801,7 @@ Binding,,, elisp, The Emacs Lisp Reference Manual}) to be 
active.
                        for type = (if (string-match "^warning" msg)
                                       :warning
                                     :error)
+                       when (and beg end)
                        collect (flymake-make-diagnostic source
                                                         beg
                                                         end
diff --git a/doc/misc/gnus-coding.texi b/doc/misc/gnus-coding.texi
deleted file mode 100644
index 0858432acf..0000000000
--- a/doc/misc/gnus-coding.texi
+++ /dev/null
@@ -1,227 +0,0 @@
-\input texinfo
-
-@setfilename gnus-coding.info
-@settitle Gnus Coding Style and Maintenance Guide
-@include docstyle.texi
-@syncodeindex fn cp
-@syncodeindex vr cp
-@syncodeindex pg cp
-
-@copying
-Copyright @copyright{} 2004--2005, 2007--2022 Free Software Foundation,
-Inc.
-
-@quotation
-Permission is granted to copy, distribute and/or modify this document
-under the terms of the GNU Free Documentation License, Version 1.3 or
-any later version published by the Free Software Foundation; with no
-Invariant Sections, with the Front-Cover Texts being ``A GNU Manual'',
-and with the Back-Cover Texts as in (a) below.  A copy of the license
-is included in the section entitled ``GNU Free Documentation License''.
-
-(a) The FSF's Back-Cover Text is: ``You have the freedom to copy and
-modify this GNU manual.''
-@end quotation
-@end copying
-
-
-@titlepage
-@title Gnus Coding Style and Maintenance Guide
-
-@author by Reiner Steib  <Reiner.Steib@@gmx.de>
-
-@insertcopying
-@end titlepage
-
-@c Obviously this is only a very rudimentary draft.  We put it in the
-@c repository anyway hoping that it might annoy someone enough to fix
-@c it.  ;-) Fixing only a paragraph also is appreciated.
-
-@ifnottex
-@node Top
-@top Gnus Coding Style and Maintenance Guide
-This manual describes @dots{}
-
-@insertcopying
-@end ifnottex
-
-@menu
-* Gnus Coding Style:: Gnus Coding Style
-* Gnus Maintenance Guide:: Gnus Maintenance Guide
-* GNU Free Documentation License::  The license for this documentation.
-@end menu
-
-@c @ref{Gnus Reference Guide, ,Gnus Reference Guide, gnus, The Gnus Newsreader}
-
-@node Gnus Coding Style
-@chapter Gnus Coding Style
-@section Dependencies
-
-The Gnus distribution contains a lot of libraries that have been written
-for Gnus and used intensively for Gnus.  But many of those libraries are
-useful on their own.  E.g., other Emacs Lisp packages might use the
-@acronym{MIME} library @xref{Top, ,Top, emacs-mime, The Emacs MIME
-Manual}.
-
-@subsection General purpose libraries
-
-@table @file
-
-@item netrc.el
-@file{.netrc} parsing functionality.
-@c As of 2005-10-21...
-There are no Gnus dependencies in this file.
-
-@item format-spec.el
-Functions for formatting arbitrary formatting strings.
-@c As of 2005-10-21...
-There are no Gnus dependencies in this file.
-
-@item hex-util.el
-Functions to encode/decode hexadecimal string.
-@c As of 2007-08-25...
-There are no Gnus dependencies in these files.
-@end table
-
-@subsection Encryption and security
-
-@table @file
-@item encrypt.el
-File encryption routines
-@c As of 2005-10-25...
-There are no Gnus dependencies in this file.
-
-@item password.el
-Read passwords from user, possibly using a password cache.
-@c As of 2005-10-21...
-There are no Gnus dependencies in this file.
-
-@item sha1.el
-SHA1 Secure Hash Algorithm.
-@c As of 2007-08-25...
-There are no Gnus dependencies in these files.
-@end table
-
-@subsection Networking
-
-@table @file
-@item dig.el
-Domain Name System dig interface.
-@c As of 2005-10-21...
-There are no serious Gnus dependencies in this file.  Uses
-@code{gnus-run-mode-hooks} (a wrapper function).
-
-@item dns.el, dns-mode.el
-Domain Name Service lookups.
-@c As of 2005-10-21...
-There are no Gnus dependencies in these files.
-@end table
-
-@subsection Mail and News related RFCs
-
-@table @file
-@item pop3.el
-Post Office Protocol (RFC 1460) interface.
-@c As of 2005-10-21...
-There are no Gnus dependencies in this file.
-
-@item imap.el
-@acronym{IMAP} library.
-@c As of 2005-10-21...
-There are no Gnus dependencies in this file.
-
-@item ietf-drums.el
-Functions for parsing RFC 2822 headers.
-@c As of 2005-10-21...
-There are no Gnus dependencies in this file.
-
-@item rfc1843.el
-HZ (rfc1843) decoding.  HZ is a data format for exchanging files of
-arbitrarily mixed Chinese and @acronym{ASCII} characters.
-@c As of 2005-10-21...
-@code{rfc1843-gnus-setup} seem to be useful only for Gnus.  Maybe this
-function should be relocated to remove dependencies on Gnus.  Other
-minor dependencies: @code{gnus-newsgroup-name} could be eliminated by
-using an optional argument to @code{rfc1843-decode-article-body}.
-
-@item rfc2045.el
-Functions for decoding rfc2045 headers
-@c As of 2007-08-25...
-There are no Gnus dependencies in these files.
-
-@item rfc2047.el
-Functions for encoding and decoding rfc2047 messages
-@c As of 2007-08-25...
-There are no Gnus dependencies in these files.
-@c
-Only a couple of tests for gnusy symbols.
-
-@item rfc2104.el
-RFC2104 Hashed Message Authentication Codes
-@c As of 2007-08-25...
-There are no Gnus dependencies in these files.
-
-@item rfc2231.el
-Functions for decoding rfc2231 headers
-@c As of 2007-08-25...
-There are no Gnus dependencies in these files.
-
-@item flow-fill.el
-Interpret RFC2646 "flowed" text.
-@c As of 2005-10-27...
-There are no Gnus dependencies in this file.
-
-@item uudecode.el
-Elisp native uudecode.
-@c As of 2005-12-06...
-There are no Gnus dependencies in this file.
-@c ... but the custom group is gnus-extract.
-
-@item canlock.el
-Functions for Cancel-Lock feature
-@c Cf. draft-ietf-usefor-cancel-lock-01.txt
-@c Although this draft has expired, Canlock-Lock revived in 2007 when
-@c major news providers (e.g., news.individual.org) started to use it.
-@c As of 2007-08-25...
-There are no Gnus dependencies in these files.
-
-@end table
-
-@subsection message
-
-All message composition from Gnus (both mail and news) takes place in
-Message mode buffers.  Message mode is intended to be a replacement for
-Emacs mail mode.  There should be no Gnus dependencies in
-@file{message.el}.  Alas it is not anymore.  Patches and suggestions to
-remove the dependencies are welcome.
-
-@c message.el requires nnheader which requires gnus-util.
-
-@subsection Emacs @acronym{MIME}
-
-The files @file{mml*.el} and @file{mm-*.el} provide @acronym{MIME}
-functionality for Emacs.
-
-@acronym{MML} (@acronym{MIME} Meta Language) is supposed to be
-independent from Gnus.  Alas it is not anymore.  Patches and suggestions
-to remove the dependencies are welcome.
-
-@subsection Gnus backends
-
-The files @file{nn*.el} provide functionality for accessing NNTP
-(@file{nntp.el}), IMAP (@file{nnimap.el}) and several other Mail back
-ends (probably @file{nnml.el}, @file{nnfolder.el} and
-@file{nnmaildir.el} are the most widely used mail back ends).
-
-@c mm-uu requires nnheader which requires gnus-util.  message.el also
-@c requires nnheader.
-
-
-@node GNU Free Documentation License
-@appendix GNU Free Documentation License
-@include doclicense.texi
-
-@c Local Variables:
-@c mode: texinfo
-@c coding: utf-8
-@c End:
diff --git a/doc/misc/gnus-faq.texi b/doc/misc/gnus-faq.texi
index 6d09fd4ec9..3aad985c5a 100644
--- a/doc/misc/gnus-faq.texi
+++ b/doc/misc/gnus-faq.texi
@@ -49,23 +49,23 @@ This is the Gnus Frequently Asked Questions list.
 Gnus is a Usenet Newsreader and Electronic Mail User Agent implemented
 as a part of Emacs.  It's been around in some form since the early
 1990s, and has been distributed as a standard part of Emacs for much
-of that time. Gnus 5 is the latest (and greatest) incarnation.  The
+of that time.  Gnus 5 is the latest (and greatest) incarnation.  The
 original version was called GNUS, and was written by Masanobu UMEDA@.
 When autumn crept up in 1994, Lars Magne Ingebrigtsen grew bored and
 decided to rewrite Gnus.
 
 Its biggest strength is the fact that it is extremely
-customizable. It is somewhat intimidating at first glance, but
+customizable.  It is somewhat intimidating at first glance, but
 most of the complexity can be ignored until you're ready to take
-advantage of it. If you receive a reasonable volume of e-mail
+advantage of it.  If you receive a reasonable volume of e-mail
 (you're on various mailing lists), or you would like to read
 high-volume mailing lists but cannot keep up with them, or read
 high volume newsgroups or are just bored, then Gnus is what you
 want.
 
-This FAQ was maintained by Justin Sheehy until March 2002. He
+This FAQ was maintained by Justin Sheehy until March 2002.  He
 would like to thank Steve Baur and Per Abrahamsen for doing a wonderful
-job with this FAQ before him. We would like to do the same: thanks,
+job with this FAQ before him.  We would like to do the same: thanks,
 Justin!
 
 The information contained here was compiled with the assistance
@@ -117,7 +117,7 @@ development version that became Gnus 5.12.
 
 @menu
 * FAQ 2-1::    Every time I start Gnus I get a message "Gnus auto-save
-               file exists. Do you want to read it?", what does this mean and
+               file exists.  Do you want to read it?", what does this mean and
                how to prevent it?
 * FAQ 2-2::    Gnus doesn't remember which groups I'm subscribed to,
                what's this?
@@ -133,7 +133,7 @@ development version that became Gnus 5.12.
 @subsubheading Question 2.1
 
 Every time I start Gnus I get a message "Gnus auto-save
-file exists. Do you want to read it?", what does this mean
+file exists.  Do you want to read it?", what does this mean
 and how to prevent it?
 
 @subsubheading Answer
@@ -168,8 +168,8 @@ How to change the format of the lines in Group buffer?
 @subsubheading Answer
 
 You've got to tweak the value of the variable
-gnus-group-line-format. See the manual node "Group Line
-Specification" for information on how to do this. An
+gnus-group-line-format.  See the manual node "Group Line
+Specification" for information on how to do this.  An
 example for this (guess from whose .gnus :-)):
 
 @example
@@ -192,11 +192,11 @@ Linux under the topic linux, all dealing with music under
 the topic music and all dealing with scottish music under
 the topic scottish which is a subtopic of music.
 
-To enter topic mode, just hit t while in Group buffer. Now
+To enter topic mode, just hit t while in Group buffer.  Now
 you can use @samp{T n} to create a topic
 at point and @samp{T m} to move a group to
-a specific topic. For more commands see the manual or the
-menu. You might want to include the %P specifier at the
+a specific topic.  For more commands see the manual or the
+menu.  You might want to include the %P specifier at the
 beginning of your gnus-group-line-format variable to have
 the groups nicely indented.
 
@@ -231,7 +231,7 @@ hit @samp{C-y}.
                 possible?
 * FAQ 3-7::     And how about local spool files?
 * FAQ 3-8::     OK, reading news works now, but I want to be able to
-                read my mail with Gnus, too. How to do it?
+                read my mail with Gnus, too.  How to do it?
 * FAQ 3-9::     And what about IMAP?
 * FAQ 3-10::    At the office we use one of those MS Exchange servers,
                 can I use Gnus to read my mail from it?
@@ -248,8 +248,8 @@ but it only says "nntp (news) open error", what to do?
 
 @subsubheading Answer
 
-You've got to tell Gnus where to fetch the news from. Read
-the documentation for information on how to do this. As a
+You've got to tell Gnus where to fetch the news from.  Read
+the documentation for information on how to do this.  As a
 first start, put those lines in @file{~/.gnus.el}:
 
 @example
@@ -279,7 +279,7 @@ directory Emacs chooses will most certainly not be what
 you want, so let's do it the correct way.
 The first thing you've got to do is to
 create a suitable directory (no blanks in names
-please), e.g., c:\myhome. Then you must set the environment
+please), e.g., c:\myhome.  Then you must set the environment
 variable HOME to this directory.  To do this under Windows 9x
 or Me include the line
 
@@ -290,7 +290,7 @@ SET HOME=C:\myhome
 
 in your autoexec.bat and reboot.  Under NT, 2000 and XP, hit
 Winkey+Pause/Break to enter system options (if it doesn't work, go
-to Control Panel -> System -> Advanced). There you'll find the
+to Control Panel -> System -> Advanced).  There you'll find the
 possibility to set environment variables.  Create a new one with
 name HOME and value C:\myhome.  Rebooting is not necessary.
 
@@ -333,8 +333,8 @@ subscribe to a group.
 
 If you know the name of the group say @samp{U
 name.of.group @key{RET}} in group buffer (use the
-tab-completion Luke). Otherwise hit ^ in group buffer,
-this brings you to the server buffer. Now place point (the
+tab-completion Luke).  Otherwise hit ^ in group buffer,
+this brings you to the server buffer.  Now place point (the
 cursor) over the server which carries the group you want,
 hit @samp{@key{RET}}, move point to the group
 you want to subscribe to and say @samp{u}
@@ -349,7 +349,7 @@ post on this server as well as I am, what's that?
 @subsubheading Answer
 
 Some providers allow restricted anonymous access and full
-access only after authorization. To make Gnus send authinfo
+access only after authorization.  To make Gnus send authinfo
 to those servers append
 
 @example
@@ -366,8 +366,8 @@ I want Gnus to fetch news from several servers, is this 
possible?
 
 @subsubheading Answer
 
-Of course. You can specify more sources for articles in the
-variable gnus-secondary-select-methods. Add something like
+Of course.  You can specify more sources for articles in the
+variable gnus-secondary-select-methods.  Add something like
 this in @file{~/.gnus.el}:
 
 @example
@@ -418,25 +418,25 @@ to post articles, see the Gnus manual on how to do this.
 @subsubheading Question 3.8
 
 OK, reading news works now, but I want to be able to read my mail
-with Gnus, too. How to do it?
+with Gnus, too.  How to do it?
 
 @subsubheading Answer
 
 That's a bit harder since there are many possible sources
 for mail, many possible ways for storing mail and many
-different ways for sending mail. The most common cases are
+different ways for sending mail.  The most common cases are
 these: 1: You want to read your mail from a pop3 server and
 send them directly to a SMTP Server 2: Some program like
 fetchmail retrieves your mail and stores it on disk from
-where Gnus shall read it. Outgoing mail is sent by
-Sendmail, Postfix or some other MTA@. Sometimes, you even
+where Gnus shall read it.  Outgoing mail is sent by
+Sendmail, Postfix or some other MTA@.  Sometimes, you even
 need a combination of the above cases.
 
 However, the first thing to do is to tell Gnus in which way
 it should store the mail, in Gnus terminology which back end
-to use. Gnus supports many different back ends, the most
-commonly used one is nnml. It stores every mail in one file
-and is therefore quite fast. However you might prefer a one
+to use.  Gnus supports many different back ends, the most
+commonly used one is nnml.  It stores every mail in one file
+and is therefore quite fast.  However you might prefer a one
 file per group approach if your file system has problems with
 many small files, the nnfolder back end is then probably the
 choice for you.  To use nnml add the following to @file{~/.gnus.el}:
@@ -453,7 +453,7 @@ As you might have guessed, if you want nnfolder, it's
 @end example
 @noindent
 
-Now we need to tell Gnus, where to get its mail from. If
+Now we need to tell Gnus, where to get its mail from.  If
 it's a POP3 server, then you need something like this:
 
 @example
@@ -465,7 +465,7 @@ it's a POP3 server, then you need something like this:
 @noindent
 
 Make sure @file{~/.gnus.el} isn't readable to others if you store
-your password there. If you want to read your mail from a
+your password there.  If you want to read your mail from a
 traditional spool file on your local machine, it's
 
 @example
@@ -499,10 +499,10 @@ mail, it's
 Where :suffix ".prcml" tells Gnus only to use files with the
 suffix .prcml.
 
-OK, now you only need to tell Gnus how to send mail. If you
+OK, now you only need to tell Gnus how to send mail.  If you
 want to send mail via sendmail (or whichever MTA is playing
 the role of sendmail on your system), you don't need to do
-anything. However, if you want to send your mail to an
+anything.  However, if you want to send your mail to an
 SMTP Server you need the following in your @file{~/.gnus.el}
 
 @example
@@ -519,9 +519,9 @@ And what about IMAP?
 
 @subsubheading Answer
 
-There are two ways of using IMAP with Gnus. The first one is
+There are two ways of using IMAP with Gnus.  The first one is
 to use IMAP like POP3, that means Gnus fetches the mail from
-the IMAP server and stores it on disk. If you want to do
+the IMAP server and stores it on disk.  If you want to do
 this (you don't really want to do this) add the following to
 @file{~/.gnus.el}
 
@@ -586,7 +586,7 @@ each POP3 mail source.  @xref{Mail Source Specifiers}, for 
details on
 @subsection Reading messages
 
 @menu
-* FAQ 4-1::     When I enter a group, all read messages are gone. How to
+* FAQ 4-1::     When I enter a group, all read messages are gone.  How to
                 view them again?
 * FAQ 4-2::     How to tell Gnus to show an important message every time
                 I enter a group, even when it's read?
@@ -595,7 +595,7 @@ each POP3 mail source.  @xref{Mail Source Specifiers}, for 
details on
 * FAQ 4-5::     How can I change the headers Gnus displays by default at
                 the top of the article buffer?
 * FAQ 4-6::     I'd like Gnus NOT to render HTML-mails but show me the
-                text part if it's available. How to do it?
+                text part if it's available.  How to do it?
 * FAQ 4-7::     Can I use some other browser than shr to render my
                 HTML-mails?
 * FAQ 4-8::     Is there anything I can do to make poorly formatted
@@ -609,7 +609,7 @@ each POP3 mail source.  @xref{Mail Source Specifiers}, for 
details on
                 those?
 * FAQ 4-12::    The number of total messages in a group which Gnus
                 displays in group buffer is by far to high, especially in mail
-                groups. Is this a bug?
+                groups.  Is this a bug?
 * FAQ 4-13::    I don't like the layout of summary and article buffer,
                 how to change it? Perhaps even a three pane display?
 * FAQ 4-14::    I don't like the way the Summary buffer looks, how to
@@ -621,15 +621,15 @@ each POP3 mail source.  @xref{Mail Source Specifiers}, 
for details on
 @node FAQ 4-1
 @subsubheading Question 4.1
 
-When I enter a group, all read messages are gone. How to view them again?
+When I enter a group, all read messages are gone.  How to view them again?
 
 @subsubheading Answer
 
 If you enter the group by saying
 @samp{@key{RET}}
-in group buffer with point over the group, only unread and ticked messages are 
loaded. Say
+in group buffer with point over the group, only unread and ticked messages are 
loaded.  Say
 @samp{C-u @key{RET}}
-instead to load all available messages. If you want only the 300 newest say
+instead to load all available messages.  If you want only the 300 newest say
 @samp{C-u 300 @key{RET}}
 
 Loading only unread messages can be annoying if you have threaded view 
enabled, say
@@ -658,9 +658,9 @@ enter a group, even when it's read?
 
 @subsubheading Answer
 
-You can tick important messages. To do this hit
+You can tick important messages.  To do this hit
 @samp{u} while point is in summary buffer
-over the message. When you want to remove the mark, hit
+over the message.  When you want to remove the mark, hit
 either @samp{d} (this deletes the tick
 mark and set's unread mark) or @samp{M c}
 (which deletes all marks for the message).
@@ -700,7 +700,7 @@ the top of the article buffer?
 
 The variable gnus-visible-headers controls which headers
 are shown, its value is a regular expression, header lines
-which match it are shown. So if you want author, subject,
+which match it are shown.  So if you want author, subject,
 date, and if the header exists, Followup-To and MUA / NUA
 say this in @file{~/.gnus.el}:
 
@@ -715,7 +715,7 @@ say this in @file{~/.gnus.el}:
 @subsubheading Question 4.6
 
 I'd like Gnus NOT to render HTML-mails but show me the
-text part if it's available. How to do it?
+text part if it's available.  How to do it?
 
 @subsubheading Answer
 
@@ -728,7 +728,7 @@ Say
 @end example
 @noindent
 
-in @file{~/.gnus.el}. If you don't want HTML rendered, even if there's no text 
alternative add
+in @file{~/.gnus.el}.  If you don't want HTML rendered, even if there's no 
text alternative add
 
 @example
 (setq mm-automatic-display (remove "text/html" mm-automatic-display))
@@ -764,7 +764,7 @@ more readable?
 
 Gnus offers you several functions to ``wash'' incoming mail, you can
 find them if you browse through the menu, item
-Article->Washing. The most interesting ones are probably ``Wrap
+Article->Washing.  The most interesting ones are probably ``Wrap
 long lines'' (@samp{W w}), ``Decode ROT13''
 (@samp{W r}) and ``Outlook Deuglify'' which repairs
 the dumb quoting used by many users of Microsoft products
@@ -781,40 +781,40 @@ highlight more interesting ones in some way?
 
 @subsubheading Answer
 
-You want Scoring. Scoring means, that you define rules
-which assign each message an integer value. Depending on
+You want Scoring.  Scoring means, that you define rules
+which assign each message an integer value.  Depending on
 the value the message is highlighted in summary buffer (if
 it's high, say +2000) or automatically marked read (if the
 value is low, say @minus{}800) or some other action happens.
 
 There are basically three ways of setting up rules which assign
-the scoring-value to messages. The first and easiest way is to set
-up rules based on the article you are just reading. Say you're
+the scoring-value to messages.  The first and easiest way is to set
+up rules based on the article you are just reading.  Say you're
 reading a message by a guy who always writes nonsense and you want
-to ignore his messages in the future. Hit
+to ignore his messages in the future.  Hit
 @samp{L}, to set up a rule which lowers the score.
 Now Gnus asks you which the criteria for lowering the Score shall
-be. Hit @samp{?} twice to see all possibilities,
+be.  Hit @samp{?} twice to see all possibilities,
 we want @samp{a} which means the author (the from
-header). Now Gnus wants to know which kind of matching we want.
+header).  Now Gnus wants to know which kind of matching we want.
 Hit either @samp{e} for an exact match or
 @samp{s} for substring-match and delete afterwards
 everything but the name to score down all authors with the given
-name no matter which email address is used. Now you need to tell
+name no matter which email address is used.  Now you need to tell
 Gnus when to apply the rule and how long it should last, hit
 @samp{p} to apply the rule now and let it last
-forever. If you want to raise the score instead of lowering it say
+forever.  If you want to raise the score instead of lowering it say
 @samp{I} instead of @samp{L}.
 
-You can also set up rules by hand. To do this say @samp{V
-f} in summary buffer. Then you are asked for the name
+You can also set up rules by hand.  To do this say @samp{V
+f} in summary buffer.  Then you are asked for the name
 of the score file, it's name.of.group.SCORE for rules valid in
-only one group or all.Score for rules valid in all groups. See the
+only one group or all.Score for rules valid in all groups.  See the
 Gnus manual for the exact syntax, basically it's one big list
 whose elements are lists again. the first element of those lists
 is the header to score on, then one more list with what to match,
 which score to assign, when to expire the rule and how to do the
-matching. If you find me very interesting, you could add the
+matching.  If you find me very interesting, you could add the
 following to your all.Score:
 
 @example
@@ -825,14 +825,14 @@ following to your all.Score:
 
 This would add 999 to the score of messages written by me
 and 500 to the score of messages which are a (possibly
-indirect) answer to a message written by me. Of course
+indirect) answer to a message written by me.  Of course
 nobody with a sane mind would do this :-)
 
-The third alternative is adaptive scoring. This means Gnus
+The third alternative is adaptive scoring.  This means Gnus
 watches you and tries to find out what you find
 interesting and what annoying and sets up rules
-which reflect this. Adaptive scoring can be a huge help
-when reading high traffic groups. If you want to activate
+which reflect this.  Adaptive scoring can be a huge help
+when reading high traffic groups.  If you want to activate
 adaptive scoring say
 
 @example
@@ -852,11 +852,11 @@ set other variables specific for some groups?
 
 While in group buffer move point over the group and hit
 @samp{G c}, this opens a buffer where you
-can set options for the group. At the bottom of the buffer
+can set options for the group.  At the bottom of the buffer
 you'll find an item that allows you to set variables
-locally for the group. To disable threading enter
+locally for the group.  To disable threading enter
 gnus-show-threads as name of variable and @code{nil} as
-value. Hit button done at the top of the buffer when
+value.  Hit button done at the top of the buffer when
 you're ready.
 
 @node FAQ 4-11
@@ -868,7 +868,7 @@ those?
 @subsubheading Answer
 
 Stop those "Can I ..." questions, the answer is always yes
-in Gnus Country :-). It's a three step process: First we
+in Gnus Country :-).  It's a three step process: First we
 make faces (specifications of how summary-line shall look
 like) for those postings, then we'll give them some
 special score and finally we'll tell Gnus to use the new
@@ -879,16 +879,16 @@ faces.
 
 The number of total messages in a group which Gnus
 displays in group buffer is by far to high, especially in
-mail groups. Is this a bug?
+mail groups.  Is this a bug?
 
 @subsubheading Answer
 
 No, that's a matter of design of Gnus, fixing this would
 mean reimplementation of major parts of Gnus'
-back ends. Gnus thinks ``highest-article-number @minus{}
-lowest-article-number = total-number-of-articles''. This
+back ends.  Gnus thinks ``highest-article-number @minus{}
+lowest-article-number = total-number-of-articles''.  This
 works OK for Usenet groups, but if you delete and move
-many messages in mail groups, this fails. To cure the
+many messages in mail groups, this fails.  To cure the
 symptom, enter the group via @samp{C-u @key{RET}}
 (this makes Gnus get all messages), then
 hit @samp{M P b} to mark all messages and
@@ -907,9 +907,9 @@ to change it? Perhaps even a three pane display?
 @subsubheading Answer
 
 You can control the windows configuration by calling the
-function gnus-add-configuration. The syntax is a bit
+function gnus-add-configuration.  The syntax is a bit
 complicated but explained very well in the manual node
-"Window Layout". Some popular examples:
+"Window Layout".  Some popular examples:
 
 Instead 25% summary 75% article buffer 35% summary and 65%
 article (the 1.0 for article means "take the remaining
@@ -951,11 +951,11 @@ I don't like the way the Summary buffer looks, how to 
tweak it?
 @subsubheading Answer
 
 You've got to play around with the variable
-gnus-summary-line-format. Its value is a string of
+gnus-summary-line-format.  Its value is a string of
 symbols which stand for things like author, date, subject
-etc. A list of the available specifiers can be found in the
+etc.  A list of the available specifiers can be found in the
 manual node ``Summary Buffer Lines'' and the often forgotten
-node ``Formatting Variables'' and its sub-nodes. There
+node ``Formatting Variables'' and its sub-nodes.  There
 you'll find useful things like positioning the cursor and
 tabulators which allow you a summary in table form, but
 sadly hard tabulators are broken in 5.8.8.
@@ -963,7 +963,7 @@ sadly hard tabulators are broken in 5.8.8.
 Gnus offers you some very nice new specifiers,
 e.g., %B which draws a thread-tree and %&user-date which
 gives you a date where the details are dependent of the
-articles age. Here's an example which uses both:
+articles age.  Here's an example which uses both:
 
 @example
 (setq gnus-summary-line-format ":%U%R %B %s %-60=|%4L |%-20,20f |%&user-date; 
\n")
@@ -997,19 +997,19 @@ How to split incoming mails in several groups?
 
 Gnus offers two possibilities for splitting mail, the easy
 nnmail-split-methods and the more powerful Fancy Mail
-Splitting. I'll only talk about the first one, refer to
+Splitting.  I'll only talk about the first one, refer to
 the manual, node "Fancy Mail Splitting" for the latter.
 
 The value of nnmail-split-methods is a list, each element
-is a list which stands for a splitting rule. Each rule has
+is a list which stands for a splitting rule.  Each rule has
 the form "group where matching articles should go to",
 "regular expression which has to be matched", the first
-rule which matches wins. The last rule must always be a
+rule which matches wins.  The last rule must always be a
 general rule (regular expression .*) which denotes where
-articles should go which don't match any other rule. If
+articles should go which don't match any other rule.  If
 the folder doesn't exist yet, it will be created as soon
 as an article lands there.  By default the mail will be
-send to all groups whose rules match. If you
+send to all groups whose rules match.  If you
 don't want that (you probably don't want), say
 
 @example
@@ -1020,11 +1020,11 @@ don't want that (you probably don't want), say
 in @file{~/.gnus.el}.
 
 An example might be better than thousand words, so here's
-my nnmail-split-methods. Note that I send duplicates in a
+my nnmail-split-methods.  Note that I send duplicates in a
 special group and that the default group is spam, since I
 filter all mails out which are from some list I'm
 subscribed to or which are addressed directly to me
-before. Those rules kill about 80% of the Spam which
+before.  Those rules kill about 80% of the Spam which
 reaches me (Email addresses are changed to prevent spammers
 from using them):
 
@@ -1089,10 +1089,10 @@ of the variables @code{shr-color-visible-distance-min} 
and
 * FAQ 5-7::     Is there some kind of address-book, so I needn't
                 remember all those email addresses?
 * FAQ 5-8::     Sometimes I see little images at the top of article
-                buffer. What's that and how can I send one with my postings,
+                buffer.  What's that and how can I send one with my postings,
                 too?
 * FAQ 5-9::     Sometimes I accidentally hit r instead of f in
-                newsgroups. Can Gnus warn me, when I'm replying by mail in
+                newsgroups.  Can Gnus warn me, when I'm replying by mail in
                 newsgroups?
 * FAQ 5-10::    How to tell Gnus not to generate a sender header?
 * FAQ 5-11::    I want Gnus to locally store copies of my send mail and
@@ -1115,18 +1115,18 @@ either in Group or Summary buffer, for a posting, it's
 either @samp{a} in Group buffer and
 filling the Newsgroups header manually
 or @samp{a} in the Summary buffer of the
-group where the posting shall be send to. Replying by mail
+group where the posting shall be send to.  Replying by mail
 is
 @samp{r} if you don't want to cite the
 author, or import the cited text manually and
 @samp{R} to cite the text of the original
-message. For a follow up to a newsgroup, it's
+message.  For a follow up to a newsgroup, it's
 @samp{f} and @samp{F}
 (analogously to @samp{r} and
 @samp{R}).
 
 Enter new headers above the line saying "--text follows
-this line--", enter the text below the line. When ready
+this line--", enter the text below the line.  When ready
 hit @samp{C-c C-c}, to send the message,
 if you want to finish it later hit @samp{C-c
 C-d} to save it in the drafts group, where you
@@ -1189,7 +1189,7 @@ organization, address, name or body.  The attribute name
 can also be a string.  In that case, this will be used as
 a header name, and the value will be inserted in the
 headers of the article; if the value is @code{nil}, the header
-name will be removed. You can also say (eval (foo bar)),
+name will be removed.  You can also say (eval (foo bar)),
 then the function foo will be evaluated with argument bar
 and the result will be thrown away.
 
@@ -1200,8 +1200,8 @@ Can I set things like From, Signature etc group based on 
the group I post too?
 
 @subsubheading Answer
 
-That's the strength of posting styles. Before, we used ".*"
-to set the default for all groups. You can use a regexp
+That's the strength of posting styles.  Before, we used ".*"
+to set the default for all groups.  You can use a regexp
 like "^gmane" and the following settings are only applied
 to postings you send to the gmane hierarchy, use
 ".*binaries" instead and they will be applied to postings
@@ -1210,7 +1210,7 @@ name etc.
 
 You can instead of specifying a regexp specify a function
 which is evaluated, only if it returns true, the
-corresponding settings take effect. Two interesting
+corresponding settings take effect.  Two interesting
 candidates for this are message-news-p which returns t if
 the current Group is a newsgroup and the corresponding
 message-mail-p.
@@ -1220,7 +1220,7 @@ the example below, when I post to
 gmane.mail.spam.spamassassin.general, the settings under
 ".*" are applied and the settings under message-news-p and
 those under "^gmane" and those under
-"^gmane\\.mail\\.spam\\.spamassassin\\.general$". Because
+"^gmane\\.mail\\.spam\\.spamassassin\\.general$".  Because
 of this put general settings at the top and specific ones
 at the bottom.
 
@@ -1302,7 +1302,7 @@ Yes, say something like
 @end example
 @noindent
 
-in @file{~/.gnus.el}. Change "^de\\." and "deutsch8" to something
+in @file{~/.gnus.el}.  Change "^de\\." and "deutsch8" to something
 that suits your needs.
 
 @node FAQ 5-7
@@ -1324,12 +1324,12 @@ alias al        "Al <al@@english-heritage.invalid>"
 
 Then typing your alias (followed by a space or punctuation
 character) on a To: or Cc: line in the message buffer will
-cause Gnus to insert the full address for you. See the
+cause Gnus to insert the full address for you.  See the
 node "Mail Aliases" in Message (not Gnus) manual for
 details.
 
 However, what you really want is the Insidious Big Brother
-Database bbdb. Get it from
+Database bbdb.  Get it from
 @uref{http://bbdb.sourceforge.net/, bbdb's website}.
 Now place the following in @file{~/.gnus.el}, to activate bbdb for Gnus:
 
@@ -1358,14 +1358,14 @@ place them in ~/.emacs:
 @end example
 @noindent
 
-Now you should be ready to go. Say @samp{M-x bbdb @key{RET}
+Now you should be ready to go.  Say @samp{M-x bbdb @key{RET}
 @key{RET}} to open a bbdb buffer showing all
-entries. Say @samp{c} to create a new
+entries.  Say @samp{c} to create a new
 entry, @samp{b} to search your BBDB and
 @samp{C-o} to add a new field to an
-entry. If you want to add a sender to the BBDB you can
+entry.  If you want to add a sender to the BBDB you can
 also just hit @kbd{:} on the posting in the summary buffer and
-you are done. When you now compose a new mail,
+you are done.  When you now compose a new mail,
 hit @samp{TAB} to cycle through know
 recipients.
 
@@ -1373,18 +1373,18 @@ recipients.
 @subsubheading Question 5.8
 
 Sometimes I see little images at the top of article
-buffer. What's that and how can I send one with my
+buffer.  What's that and how can I send one with my
 postings, too?
 
 @subsubheading Answer
 
-Those images are called X-Faces. They are 48*48 pixel b/w
-pictures, encoded in a header line. If you want to include
+Those images are called X-Faces.  They are 48*48 pixel b/w
+pictures, encoded in a header line.  If you want to include
 one in your posts, you've got to convert some image to a
-X-Face. So fire up some image manipulation program (say
+X-Face.  So fire up some image manipulation program (say
 Gimp), open the image you want to include, cut out the
 relevant part, reduce color depth to 1 bit, resize to
-48*48 and save as bitmap. Now you should get the compface
+48*48 and save as bitmap.  Now you should get the compface
 package from
 @uref{ftp://ftp.cs.indiana.edu/pub/faces/, this site}.
 and create the actual X-face by saying
@@ -1424,7 +1424,7 @@ to @code{gnus-posting-styles}.
 @subsubheading Question 5.9
 
 Sometimes I accidentally hit r instead of f in
-newsgroups. Can Gnus warn me, when I'm replying by mail in
+newsgroups.  Can Gnus warn me, when I'm replying by mail in
 newsgroups?
 
 @subsubheading Answer
@@ -1454,7 +1454,7 @@ news, how to do it?
 @subsubheading Answer
 
 You must set the variable gnus-message-archive-group to do
-this. You can set it to a string giving the name of the
+this.  You can set it to a string giving the name of the
 group where the copies shall go or like in the example
 below use a function which is evaluated and which returns
 the group to use.
@@ -1491,8 +1491,8 @@ aren't they and how to fix it?
 @subsubheading Answer
 
 The message-ID is a unique identifier for messages you
-send. To make it unique, Gnus need to know which machine
-name to put after the "@@". If the name of the machine
+send.  To make it unique, Gnus need to know which machine
+name to put after the "@@".  If the name of the machine
 where Gnus is running isn't suitable (it probably isn't
 at most private machines) you can tell Gnus what to use
 by saying:
@@ -1519,7 +1519,7 @@ instead (works for newer versions as well):
 
 If you have no idea what to insert for
 "yourmachine.yourdomain.tld", you've got several
-choices. You can either ask your provider if he allows
+choices.  You can either ask your provider if he allows
 you to use something like
 yourUserName.userfqdn.provider.net, or you can use
 somethingUnique.yourdomain.tld if you own the domain
@@ -1556,7 +1556,7 @@ correctly by sending yourself a Mail and looking at the 
Message-ID.
 * FAQ 6-3::    How to search for a specific message?
 * FAQ 6-4::    How to get rid of old unwanted mail?
 * FAQ 6-5::    I want that all read messages are expired (at least in
-               some groups). How to do it?
+               some groups).  How to do it?
 * FAQ 6-6::    I don't want expiration to delete my mails but to move
                them to another group.
 @end menu
@@ -1569,16 +1569,16 @@ How to import my old mail into Gnus?
 @subsubheading Answer
 
 The easiest way is to tell your old mail program to
-export the messages in mbox format. Most Unix mailers
+export the messages in mbox format.  Most Unix mailers
 are able to do this, if you come from the MS Windows
 world, you may find tools at
 @uref{https://sourceforge.net/projects/mbx2mbox/}.
 
-Now you've got to import this mbox file into Gnus. To do
+Now you've got to import this mbox file into Gnus.  To do
 this, create a nndoc group based on the mbox file by
 saying @samp{G f /path/file.mbox @key{RET}} in
-Group buffer. You now have read-only access to your
-mail. If you want to import the messages to your normal
+Group buffer.  You now have read-only access to your
+mail.  If you want to import the messages to your normal
 Gnus mail groups hierarchy, enter the nndoc group you've
 just created by saying @samp{C-u @key{RET}}
 (thus making sure all messages are retrieved), mark all
@@ -1597,8 +1597,8 @@ How to archive interesting messages?
 
 If you stumble across an interesting message, say in
 gnu.emacs.gnus and want to archive it there are several
-solutions. The first and easiest is to save it to a file
-by saying @samp{O f}. However, wouldn't
+solutions.  The first and easiest is to save it to a file
+by saying @samp{O f}.  However, wouldn't
 it be much more convenient to have more direct access to
 the archived message from Gnus? If you say yes, put this
 snippet by Frank Haun <pille3003@@fhaun.de> in
@@ -1607,7 +1607,7 @@ snippet by Frank Haun <pille3003@@fhaun.de> in
 @example
 (defun my-archive-article (&optional n)
   "Copies one or more article(s) to a corresponding `nnml:' group, e.g.,
-`gnus.ding' goes to `nnml:1.gnus.ding'. And `nnml:List-gnus.ding' goes
+`gnus.ding' goes to `nnml:1.gnus.ding'.  And `nnml:List-gnus.ding' goes
 to `nnml:1.List-gnus-ding'.
 
 Use process marks or mark a region in the summary buffer to archive
@@ -1644,7 +1644,7 @@ How to search for a specific message?
 
 @subsubheading Answer
 
-There are several ways for this, too. For a posting from
+There are several ways for this, too.  For a posting from
 a Usenet group the easiest solution is probably to ask
 @uref{https://groups.google.com, groups.google.com},
 if you found the posting there, tell Google to display
@@ -1659,9 +1659,9 @@ Another idea which works for both mail and news groups
 is to enter the group where the message you are
 searching is and use the standard Emacs search
 @samp{C-s}, it's smart enough to look at
-articles in collapsed threads, too. If you want to
+articles in collapsed threads, too.  If you want to
 search bodies, too try @samp{M-s}
-instead. Further on there are the
+instead.  Further on there are the
 gnus-summary-limit-to-foo functions, which can help you,
 too.
 
@@ -1675,17 +1675,17 @@ How to get rid of old unwanted mail?
 You can of course just mark the mail you don't need
 anymore by saying @samp{#} with point
 over the mail and then say @samp{B @key{DEL}}
-to get rid of them forever. You could also instead of
+to get rid of them forever.  You could also instead of
 actually deleting them, send them to a junk-group by
 saying @samp{B m nnml:trash-bin} which
 you clear from time to time, but both are not the intended
 way in Gnus.
 
 In Gnus, we let mail expire like news expires on a news
-server. That means you tell Gnus the message is
+server.  That means you tell Gnus the message is
 expirable (you tell Gnus "I don't need this mail
 anymore") by saying @samp{E} with point
-over the mail in summary buffer. Now when you leave the
+over the mail in summary buffer.  Now when you leave the
 group, Gnus looks at all messages which you marked as
 expirable before and if they are old enough (default is
 older than a week) they are deleted.
@@ -1694,24 +1694,24 @@ older than a week) they are deleted.
 @subsubheading Question 6.5
 
 I want that all read messages are expired (at least in
-some groups). How to do it?
+some groups).  How to do it?
 
 @subsubheading Answer
 
 If you want all read messages to be expired (e.g., in
 mailing lists where there's an online archive), you've
 got two choices: auto-expire and
-total-expire. Auto-expire means, that every article
+total-expire.  Auto-expire means, that every article
 which has no marks set and is selected for reading is
 marked as expirable, Gnus hits @samp{E}
-for you every time you read a message. Total-expire
+for you every time you read a message.  Total-expire
 follows a slightly different approach, here all article
 where the read mark is set are expirable.
 
 To activate auto-expire, include auto-expire in the
 Group parameters for the group. (Hit @samp{G
 c} in summary buffer with point over the
-group to change group parameters). For total-expire add
+group to change group parameters).  For total-expire add
 total-expire to the group-parameters.
 
 Which method you choose is merely a matter of taste:
@@ -1753,7 +1753,7 @@ variables specific for some groups?")
 * FAQ 7-1::    I don't have a permanent connection to the net, how can I
                minimize the time I've got to be connected?
 * FAQ 7-2::    So what was this thing about the Agent?
-* FAQ 7-3::    I want to store article bodies on disk, too. How to do
+* FAQ 7-3::    I want to store article bodies on disk, too.  How to do
                it?
 * FAQ 7-4::    How to tell Gnus not to try to send mails / postings
                while I'm offline?
@@ -1790,7 +1790,7 @@ Then you want to fetch your Mail, popular choices
 are @uref{https://www.fetchmail.info/, fetchmail}
 and @uref{http://pyropus.ca/software/getmail/, getmail}.
 You should tell those to write the mail to your disk and
-Gnus to read it from there. Last but not least the mail
+Gnus to read it from there.  Last but not least the mail
 sending part: This can be done with every MTA like
 @uref{https://www.proofpoint.com/us/open-source-email-solution, sendmail} or
 @uref{https://www.exim.org/, exim}.
@@ -1800,7 +1800,7 @@ On windows boxes I'd vote for
 it's a small freeware, open-source program which fetches
 your mail and news from remote servers and offers them
 to Gnus (or any other mail and/or news reader) via nntp
-respectively POP3 or IMAP@. It also includes a smtp
+respectively POP3 or IMAP@.  It also includes a smtp
 server for receiving mails from Gnus.
 
 @node FAQ 7-2
@@ -1812,7 +1812,7 @@ So what was this thing about the Agent?
 
 The Gnus agent is part of Gnus, it allows you to fetch
 mail and news and store them on disk for reading them
-later when you're offline. It kind of mimics offline
+later when you're offline.  It kind of mimics offline
 newsreaders like Forte Agent.  It is enabled by default.
 
 You've got to select the servers whose groups can be
@@ -1831,7 +1831,7 @@ there the next time you enter the group.
 @node FAQ 7-3
 @subsubheading Question 7.3
 
-I want to store article bodies on disk, too. How to do it?
+I want to store article bodies on disk, too.  How to do it?
 
 @subsubheading Answer
 
@@ -1839,16 +1839,16 @@ You can tell the agent to automatically fetch the bodies
 of articles which fulfill certain predicates, this is
 done in a special buffer which can be reached by
 saying @samp{J c} in group
-buffer. Please refer to the documentation for
+buffer.  Please refer to the documentation for
 information which predicates are possible and how
 exactly to do it.
 
 Further on you can tell the agent manually which
-articles to store on disk. There are two ways to do
+articles to store on disk.  There are two ways to do
 this: Number one: In the summary buffer, process mark a
 set of articles that shall be stored in the agent by
 saying @samp{#} with point over the
-article and then type @samp{J s}. The
+article and then type @samp{J s}.  The
 other possibility is to set, again in the summary
 buffer, downloadable (%) marks for the articles you
 want by typing @samp{@@} with point over
@@ -1873,11 +1873,11 @@ while I'm offline?
 
 All you've got to do is to tell Gnus when you are online
 (plugged) and when you are offline (unplugged), the rest
-works automatically. You can toggle plugged/unplugged
+works automatically.  You can toggle plugged/unplugged
 state by saying @samp{J j} in group
-buffer. To start Gnus unplugged say @samp{M-x
+buffer.  To start Gnus unplugged say @samp{M-x
 gnus-unplugged} instead of
-@samp{M-x gnus}. Note that for this to
+@samp{M-x gnus}.  Note that for this to
 work, the agent must be active.
 
 @node FAQ 8 - Getting help
@@ -1903,10 +1903,10 @@ How to find information and help inside Emacs?
 The first stop should be the Gnus manual (Say
 @samp{C-h i d m Gnus @key{RET}} to start the
 Gnus manual, then walk through the menus or do a
-full-text search with @samp{s}). Then
+full-text search with @samp{s}).  Then
 there are the general Emacs help commands starting with
 C-h, type @samp{C-h ? ?} to get a list
-of all available help commands and their meaning. Finally
+of all available help commands and their meaning.  Finally
 @samp{M-x apropos-command} lets you
 search through all available functions and @samp{M-x
 apropos} searches the bound variables.
@@ -1999,10 +1999,10 @@ active file, see the node "The Active File" in the Gnus
 manual for things you might try to speed the process up.
 An other idea would be to byte compile your @file{~/.gnus.el} (say
 @samp{M-x byte-compile-file @key{RET} ~/.gnus.el
-@key{RET}} to do it). Finally, if you have require
+@key{RET}} to do it).  Finally, if you have require
 statements in your .gnus, you could replace them with
 @code{with-eval-after-load}, which loads the stuff not at startup
-time, but when it's needed. Say you've got this in your
+time, but when it's needed.  Say you've got this in your
 @file{~/.gnus.el}:
 
 @example
@@ -2011,7 +2011,7 @@ time, but when it's needed. Say you've got this in your
 @end example
 @noindent
 
-then as soon as you start Gnus, message.el is loaded. If
+then as soon as you start Gnus, message.el is loaded.  If
 you replace it with
 
 @example
@@ -2052,7 +2052,7 @@ Sending mail becomes slower and slower, what's up?
 
 The reason could be that you told Gnus to archive the
 messages you wrote by setting
-gnus-message-archive-group. Try to use a nnml group
+gnus-message-archive-group.  Try to use a nnml group
 instead of an archive group, this should bring you back
 to normal speed.
 
@@ -2063,7 +2063,7 @@ to normal speed.
 
 @item ~/.gnus.el
 When the term @file{~/.gnus.el} is used it just means your Gnus
-configuration file. You might as well call it @file{~/.gnus} or
+configuration file.  You might as well call it @file{~/.gnus} or
 specify another name.
 
 @item Back End
diff --git a/doc/misc/gnus.texi b/doc/misc/gnus.texi
index 738ff94b9f..b1331e79bf 100644
--- a/doc/misc/gnus.texi
+++ b/doc/misc/gnus.texi
@@ -30580,7 +30580,6 @@ Below is a slightly shortened version of the 
@code{nndir} back end.
 (defvoo nndir-get-new-mail nil nil nnml-get-new-mail nnmh-get-new-mail)
 
 (defvoo nndir-status-string "" nil nnmh-status-string)
-(defconst nndir-version "nndir 1.0")
 
 ;;; @r{Interface functions.}
 
diff --git a/doc/misc/idlwave.texi b/doc/misc/idlwave.texi
index 0ba87b2e58..4bdbd5c219 100644
--- a/doc/misc/idlwave.texi
+++ b/doc/misc/idlwave.texi
@@ -4162,8 +4162,8 @@ tried to install the optional modules 
@file{idlw-roprompt.el} or
 load file}}.
 
 The problem is that your Emacs is not finding the version of IDLWAVE you
-installed.  Many Emacsen come with an older bundled copy of IDLWAVE
-(e.g., v4.7 for Emacs 21.x), which is likely what's being used instead.
+installed.  Emacs might come with an older bundled copy of IDLWAVE
+which is likely what's being used instead.
 You need to make sure your Emacs @emph{load-path} contains the directory
 where IDLWAVE is installed (@file{/usr/local/share/emacs/site-lisp}, by
 default), @emph{before} Emacs's default search directories.  You can
diff --git a/doc/misc/mh-e.texi b/doc/misc/mh-e.texi
index 2106c674f3..6aa2cf290d 100644
--- a/doc/misc/mh-e.texi
+++ b/doc/misc/mh-e.texi
@@ -213,13 +213,12 @@ more niceties about GNU Emacs and MH@. Now I'm fully 
hooked on both of
 them.
 
 The MH-E package is distributed with Emacs@footnote{Version
-@value{VERSION} of MH-E appeared in Emacs 24.4.
-It is compatible with MH versions 6.8.4 and
-higher, all versions of nmh, and GNU mailutils 1.0 and higher}, so you
-shouldn't have to do anything special to use it. Gnus is also
-required; version 5.10 or higher is recommended. This manual covers
-MH-E version @value{VERSION}. To help you decide which version you
-have, see @ref{Getting Started}.
+@value{VERSION} of MH-E appeared in Emacs 24.4. It is compatible with
+MH versions 6.8.4 and higher, all versions of nmh, and GNU mailutils
+1.0 and higher}, so you shouldn't have to do anything special to use
+it. Gnus is also required; it is bundled with Emacs. This manual
+covers MH-E version @value{VERSION}. To help you decide which version
+you have, see @ref{Getting Started}.
 
 @findex help-with-tutorial
 @kindex C-h t
@@ -331,8 +330,7 @@ Press the @key{TAB} key.
 Press the @key{DELETE} key.
 @c -------------------------
 @item @key{BS}
-Press the @key{BACKSPACE} key@footnote{If you are using Version 20 or
-earlier of Emacs, you will need to use the @key{DEL} key.}.
+Press the @key{BACKSPACE} key.
 @end table
 
 @cindex Emacs, prefix argument
@@ -1480,11 +1478,9 @@ and click on the @samp{INS} button. Enter a @samp{Spool 
File} of
 Binding} of @samp{m}.
 
 @cindex @command{emacsclient}
-@cindex @command{gnuclient}
 @cindex @command{xbuffy}
 @cindex @samp{gnuserv}
 @cindex Unix commands, @command{emacsclient}
-@cindex Unix commands, @command{gnuclient}
 @cindex Unix commands, @command{xbuffy}
 
 You can use @command{xbuffy} to automate the incorporation of this
@@ -2712,8 +2708,7 @@ Drafts}).
 @cindex signed messages
 
 You can read encrypted or signed PGP or GPG messages with
-MH-E@footnote{This feature depends on post-5.10 versions of Gnus.
-@cite{MIME Security with OpenPGP} is documented in
+MH-E@footnote{@cite{MIME Security with OpenPGP} is documented in
 @uref{https://www.rfc-editor.org/rfc/rfc3156.txt, RFC 3156}. However,
 MH-E can also decrypt old-style PGP messages that are not in MIME
 format.}. This section assumes that you already have a good
@@ -8538,9 +8533,7 @@ If you're on a mailing list that is so voluminous that it 
is
 impossible to read every message, it usually better to read the
 mailing list like a newsgroup in a news reader. Emacs has a built-in
 newsreader called Gnus. The remainder of this appendix talks about how
-to use Gnus with an MH message store. The version of Gnus that was
-used to prepare this manual was 5.10. Versions 5.8 through 5.10 should
-work but versions prior to 5.8 use different options.
+to use Gnus with an MH message store.
 
 This table contains a list of Gnus options that you will have to
 modify. Note that for them to become accessible, you'll have to load
@@ -8660,28 +8653,11 @@ question, file a ticket and your question will become a 
new FAQ!
 @cindex getting MH-E
 @cindex obtaining MH-E
 
-Because MH-E is undergoing a phase of sustained growth, the version of
-MH-E in your Emacs is likely to be out of date although it is most
-likely to be more up to date than the copy that comes with the MH
-distribution in @file{miscellany/mh-e}.
-
-@cindex change log
-@cindex release notes
-
-New MH-E releases are always available for downloading at
-@uref{https://sourceforge.net/projects/mh-e/files/, SourceForge}
-before they appear in an Emacs release. You can read the release notes
-on that page to determine if the given release of MH-E is already
-installed in your version of Emacs. You can also read the change log
-to see if you are interested in what the given release of MH-E has to
-offer (although we have no doubt that you will be extremely interested
-in all new releases).
-
-@cindex Debian
-
-If you use Debian, you can install the Debian
-@uref{https://packages.debian.org/unstable/mail/mh-e, mh-e package}
-instead.
+Since MH-E 8.6 was released in 2016, its development migrated to the
+Emacs repository. MH-E is now only supported in the version of Emacs
+in which it appears. Old releases of MH-E are still available for
+download at @uref{https://sourceforge.net/projects/mh-e/files/,
+SourceForge}.
 
 @cindex files, @samp{MH-E-NEWS}
 @cindex files, @samp{README}
diff --git a/doc/misc/modus-themes.org b/doc/misc/modus-themes.org
index 1b4bf88a0c..2680fe9eb5 100644
--- a/doc/misc/modus-themes.org
+++ b/doc/misc/modus-themes.org
@@ -4,9 +4,9 @@
 #+language:              en
 #+options:               ':t toc:nil author:t email:t num:t
 #+startup:               content
-#+macro:                 stable-version 2.6.0
-#+macro:                 release-date 2022-08-19
-#+macro:                 development-version 2.7.0-dev
+#+macro:                 stable-version 2.7.0
+#+macro:                 release-date 2022-10-01
+#+macro:                 development-version 2.8.0-dev
 #+macro:                 file @@texinfo:@file{@@$1@@texinfo:}@@
 #+macro:                 space @@texinfo:@: @@
 #+macro:                 kbd @@texinfo:@kbd{@@$1@@texinfo:}@@
@@ -3902,6 +3902,7 @@ package:
 (use-package circadian                  ; you need to install this
   :ensure
   :after solar
+  :config
   (setq circadian-themes '((:sunrise . modus-operandi)
                            (:sunset  . modus-vivendi)))
   (circadian-setup))
@@ -4514,6 +4515,7 @@ have lots of extensions, so the "full support" may not be 
100% true…
 + calendar and diary
 + calfw
 + calibredb
++ centaur-tabs
 + cfrs
 + change-log and log-view (such as ~vc-print-log~, ~vc-print-root-log~)
 + chart
@@ -4533,6 +4535,7 @@ have lots of extensions, so the "full support" may not be 
100% true…
 + counsel-css
 + cov
 + cperl-mode
++ crontab-mode
 + css-mode
 + csv-mode
 + ctrlf
@@ -4705,6 +4708,7 @@ have lots of extensions, so the "full support" may not be 
100% true…
 + powerline
 + powerline-evil
 + prism ([[#h:a94272e0-99da-4149-9e80-11a7e67a2cf2][Note for prism.el]])
++ prescient
 + proced
 + prodigy
 + pulse
@@ -4738,6 +4742,7 @@ have lots of extensions, so the "full support" may not be 
100% true…
 + smerge
 + spaceline
 + speedbar
++ spell-fu
 + stripes
 + suggest
 + switch-window
@@ -5596,6 +5601,32 @@ those buttons.  Disabling the logo fixes the problem:
 (setq notmuch-show-logo nil)
 #+end_src
 
+** Note on goto-address-mode faces
+:PROPERTIES:
+:CUSTOM_ID: h:2d74236a-e41c-4616-8735-75f949a67334
+:END:
+
+The built-in ~goto-address-mode~ uses heuristics to identify URLs and
+email addresses in the current buffer.  It then applies a face to them
+to change their style.  Some packages, such as =notmuch=, use this
+minor-mode automatically.
+
+The faces are not declared with ~defface~, meaning that it is better
+that the theme does not modify them.  The user is thus encouraged to
+consider including (or equivalent) this in their setup:
+
+#+begin_src emacs-lisp
+(setq goto-address-url-face 'link
+      goto-address-url-mouse-face 'highlight
+      goto-address-mail-face 'link
+      goto-address-mail-mouse-face 'highlight)
+#+end_src
+
+My personal preference is to set ~goto-address-mail-face~ to nil, as
+it otherwise adds too much visual noise to the buffer (email addresses
+stand out more, due to the use of the uncommon =@= character but also
+because they are often enclosed in angled brackets).
+
 * Frequently Asked Questions
 :properties:
 :custom_id: h:b3384767-30d3-4484-ba7f-081729f03a47
@@ -5868,11 +5899,11 @@ usability beyond matters of color---they would be 
making a
 not-so-obvious error of treating different cases as if they were the
 same.
 
-The Modus themes prioritise "thematic consistency" over abstract harmony
+The Modus themes prioritize "thematic consistency" over abstract harmony
 or regularity among their applicable colors.  In concrete terms, we do
 not claim that, say, our yellows are the best complements for our blues
 because we generally avoid using complementary colors side-by-side, so
-it is wrong to optimise for a decontextualised blue+yellow combination.
+it is wrong to optimize for a decontextualised blue+yellow combination.
 Not to imply that our colors do not work well together because they do,
 just to clarify that consistency of context is what themes must strive
 for, and that requires widening the scope of the design beyond the
@@ -6072,42 +6103,44 @@ The Modus themes are a collective effort.  Every bit of 
work matters.
 + Author/maintainer :: Protesilaos Stavrou.
 
 + Contributions to code or documentation :: Alex Griffin, Anders
-  Johansson, Basil L.{{{space()}}} Contovounesios, Björn Lindström,
-  Carlo Zancanaro, Christian Tietze, Daniel Mendler, Eli Zaretskii,
-  Fritz Grabo, Illia Ostapyshyn, Kévin Le Gouguec, Kostadin Ninev,
-  Madhavan Krishnan, Manuel Giraud, Markus Beppler, Matthew Stevenson,
-  Mauro Aranda, Nicolas De Jaeghere, Paul David, Philip Kaludercic,
-  Pierre Téchoueyres, Rudolf Adamkovič, Stephen Gildea, Shreyas Ragavan,
-  Stefan Kangas, Utkarsh Singh, Vincent Murphy, Xinglu Chen, Yuanchen
-  Xie.
+  Johansson, Antonio Ruiz, Basil L.{{{space()}}} Contovounesios, Björn
+  Lindström, Carlo Zancanaro, Christian Tietze, Daniel Mendler, Eli
+  Zaretskii, Fritz Grabo, Illia Ostapyshyn, Kévin Le Gouguec, Koen van
+  Greevenbroek, Kostadin Ninev, Madhavan Krishnan, Manuel Giraud,
+  Markus Beppler, Matthew Stevenson, Mauro Aranda, Nicolas De
+  Jaeghere, Paul David, Philip Kaludercic, Pierre Téchoueyres, Rudolf
+  Adamkovič, Stephen Gildea, Shreyas Ragavan, Stefan Kangas, Utkarsh
+  Singh, Vincent Murphy, Xinglu Chen, Yuanchen Xie, okamsn.
 
 + Ideas and user feedback :: Aaron Jensen, Adam Porter, Adam Spiers,
-  Adrian Manea, Alex Griffin, Alex Koen, Alex Peitsinis, Alexey Shmalko,
-  Alok Singh, Anders Johansson, André Alexandre Gomes, Andrew Tropin,
-  Antonio Hernández Blas, Arif Rezai, Augusto Stoffel, Basil
+  Adrian Manea, Alex Griffin, Alex Koen, Alex Peitsinis, Alexey
+  Shmalko, Alok Singh, Anders Johansson, André Alexandre Gomes, Andrew
+  Tropin, Antonio Hernández Blas, Arif Rezai, Augusto Stoffel, Basil
   L.{{{space()}}} Contovounesios, Burgess Chang, Christian Tietze,
-  Christopher Dimech, Christopher League, Damien Cassou, Daniel Mendler,
-  Dario Gjorgjevski, David Edmondson, Davor Rotim, Divan Santana, Eliraz
-  Kedmi, Emanuele Michele Alberto Monterosso, Farasha Euker, Feng Shu,
-  Gautier Ponsinet, Gerry Agbobada, Gianluca Recchia, Gonçalo Marrafa,
-  Guilherme Semente, Gustavo Barros, Hörmetjan Yiltiz, Ilja Kocken, Iris
-  Garcia, Ivan Popovych, Jeremy Friesen, Jerry Zhang, Johannes Grødem,
-  John Haman, Jonas Collberg, Jorge Morais, Joshua O'Connor, Julio
-  C. Villasante, Kenta Usami, Kevin Fleming, Kévin Le Gouguec, Kostadin
-  Ninev, Len Trigg, Lennart C. Karssen, Magne Hov, Manuel Uberti, Mark
-  Bestley, Mark Burton, Markus Beppler, Matt Armstrong, Mauro Aranda,
-  Maxime Tréca, Michael Goldenberg, Morgan Smith, Morgan Willcock,
-  Murilo Pereira, Nicky van Foreest, Nicolas De Jaeghere, Paul Poloskov,
-  Pengji Zhang, Pete Kazmier, Peter Wu, Philip Kaludercic, Pierre
-  Téchoueyres, Przemysław Kryger, Robert Hepple, Roman Rudakov, Ryan
-  Phillips, Rytis Paškauskas, Rudolf Adamkovič, Sam Kleinman, Samuel
-  Culpepper, Saša Janiška, Shreyas Ragavan, Simon Pugnet, Tassilo Horn,
-  Thibaut Verron, Thomas Heartman, Togan Muftuoglu, Tony Zorman, Trey
-  Merkley, Tomasz Hołubowicz, Toon Claes, Uri Sharf, Utkarsh Singh,
-  Vincent Foley.  As well as users: Ben, CsBigDataHub1, Emacs Contrib,
-  Eugene, Fourchaux, Fredrik, Moesasji, Nick, Summer Emacs, TheBlob42,
-  Trey, bepolymathe, bit9tream, derek-upham, doolio, fleimgruber,
-  gitrj95, iSeeU, jixiuf, okamsn, pRot0ta1p.
+  Christopher Dimech, Christopher League, Damien Cassou, Daniel
+  Mendler, Dario Gjorgjevski, David Edmondson, Davor Rotim, Divan
+  Santana, Eliraz Kedmi, Emanuele Michele Alberto Monterosso, Farasha
+  Euker, Feng Shu, Gautier Ponsinet, Gerry Agbobada, Gianluca Recchia,
+  Gonçalo Marrafa, Guilherme Semente, Gustavo Barros, Hörmetjan
+  Yiltiz, Ilja Kocken, Iris Garcia, Ivan Popovych, Jeremy Friesen,
+  Jerry Zhang, Johannes Grødem, John Haman, Jonas Collberg, Jorge
+  Morais, Joshua O'Connor, Julio C. Villasante, Kenta Usami, Kevin
+  Fleming, Kévin Le Gouguec, Kostadin Ninev, Len Trigg, Lennart
+  C. Karssen, Luis Miguel Castañeda, Magne Hov, Manuel Uberti, Mark
+  Bestley, Mark Burton, Mark Simpson, Markus Beppler, Matt Armstrong,
+  Mauro Aranda, Maxime Tréca, Michael Goldenberg, Morgan Smith, Morgan
+  Willcock, Murilo Pereira, Nicky van Foreest, Nicolas De Jaeghere,
+  Paul Poloskov, Pengji Zhang, Pete Kazmier, Peter Wu, Philip
+  Kaludercic, Pierre Téchoueyres, Przemysław Kryger, Robert Hepple,
+  Roman Rudakov, Ryan Phillips, Rytis Paškauskas, Rudolf Adamkovič,
+  Sam Kleinman, Samuel Culpepper, Saša Janiška, Shreyas Ragavan, Simon
+  Pugnet, Tassilo Horn, Thibaut Verron, Thomas Heartman, Togan
+  Muftuoglu, Tony Zorman, Trey Merkley, Tomasz Hołubowicz, Toon Claes,
+  Uri Sharf, Utkarsh Singh, Vincent Foley.  As well as users: Ben,
+  CsBigDataHub1, Emacs Contrib, Eugene, Fourchaux, Fredrik, Moesasji,
+  Nick, Summer Emacs, TheBlob42, Trey, bepolymathe, bit9tream,
+  derek-upham, doolio, fleimgruber, gitrj95, iSeeU, jixiuf, okamsn,
+  pRot0ta1p.
 
 + Packaging :: Basil L.{{{space()}}} Contovounesios, Eli Zaretskii,
   Glenn Morris, Mauro Aranda, Richard Stallman, Stefan Kangas (core
diff --git a/doc/misc/org.org b/doc/misc/org.org
index 7971c417a5..1ce99728c6 100644
--- a/doc/misc/org.org
+++ b/doc/misc/org.org
@@ -11042,7 +11042,7 @@ a major LaTeX mode like AUCTeX in order to speed-up 
insertion of
 environments and math templates.  Inside Org mode, you can make use of
 some of the features of CDLaTeX mode.  You need to install
 =cdlatex.el= and =texmathp.el= (the latter comes also with AUCTeX)
-using [[https://melpa.org/][MELPA]] with the 
[[https://www.gnu.org/software/emacs/manual/html_node/emacs/Package-Installation.html][Emacs
 packaging system]] or alternatively from
+from [[https://elpa.nongnu.org/][NonGNU ELPA]] with the 
[[https://www.gnu.org/software/emacs/manual/html_node/emacs/Package-Installation.html][Emacs
 packaging system]] or alternatively from
 [[https://staff.fnwi.uva.nl/c.dominik/Tools/cdlatex/]].  Do not use
 CDLaTeX mode itself under Org mode, but use the special version Org
 CDLaTeX minor mode that comes as part of Org.  Turn it on for the
@@ -22024,7 +22024,7 @@ a deadline string.  See ~org-agenda-entry-types~ on how 
to set what
 planning information is taken into account.
 
 [fn:104] For HTML you need to install Hrvoje Nikšić's =htmlize.el=
-as an Emacs package from MELPA or from 
[[https://github.com/hniksic/emacs-htmlize][Hrvoje Nikšić's repository]].
+as an Emacs package from [[https://elpa.nongnu.org/][NonGNU ELPA]] or from 
[[https://github.com/hniksic/emacs-htmlize][Hrvoje Nikšić's repository]].
 
 [fn:105] To create PDF output, the Ghostscript ps2pdf utility must be
 installed on the system.  Selecting a PDF file also creates the
diff --git a/doc/misc/rcirc.texi b/doc/misc/rcirc.texi
index 8c798d6c33..307fe55a63 100644
--- a/doc/misc/rcirc.texi
+++ b/doc/misc/rcirc.texi
@@ -177,7 +177,7 @@ using a different nick.  This will prompt you for four 
things:
 @cindex server, connecting
 @cindex Libera.Chat network
 @item IRC Server
-What server do you want to connect to? All the servers in a particular
+What server do you want to connect to?  All the servers in a particular
 network are equivalent.  Some networks use a round-robin system where
 a single server redirects new connections to a random server in the
 network.  @code{irc.libera.chat} is such a server for the Libera.Chat
@@ -531,7 +531,7 @@ This variable is used for the default nick.  It defaults to 
the login
 name returned by @code{user-login-name}.
 
 @example
-(setq rcirc-default-nick "kensanata")
+(setopt rcirc-default-nick "kensanata")
 @end example
 
 @item rcirc-default-port
@@ -557,7 +557,7 @@ to the name returned by @code{user-full-name}.  If you want 
to hide
 your full name, you might want to set it to some pseudonym.
 
 @example
-(setq rcirc-default-full-name "Curious Minds Want To Know")
+(setopt rcirc-default-full-name "Curious Minds Want To Know")
 @end example
 
 @item rcirc-authinfo
@@ -575,10 +575,10 @@ followed by the arguments this method requires.
 Here is an example to illustrate how you would set it:
 
 @example
-(setq rcirc-authinfo
-      '(("Libera.Chat" nickserv "bob" "p455w0rd")
-        ("Libera.Chat" chanserv "bob" "#bobland" "passwd99")
-        ("bitlbee" bitlbee "robert" "sekrit")))
+(setopt rcirc-authinfo
+        '(("Libera.Chat" nickserv "bob" "p455w0rd")
+          ("Libera.Chat" chanserv "bob" "#bobland" "passwd99")
+          ("bitlbee" bitlbee "robert" "sekrit")))
 @end example
 
 And here are the valid method symbols and the arguments they require:
@@ -821,7 +821,7 @@ You can control which notices get omitted via the
 omit away messages:
 
 @example
-(setq rcirc-omit-responses '("JOIN" "PART" "QUIT" "NICK" "AWAY"))
+(setopt rcirc-omit-responses '("JOIN" "PART" "QUIT" "NICK" "AWAY"))
 @end example
 
 @vindex rcirc-omit-threshold
@@ -840,7 +840,7 @@ and @code{NAMES} messages, after reconnecting, you can 
configure
 @code{rcirc-omit-unless-requested} to hide:
 
 @example
-(setq rcirc-omit-unless-requested '("TOPIC" "NAMES"))
+(setopt rcirc-omit-unless-requested '("TOPIC" "NAMES"))
 @end example
 
 Now NAMES will only be displayed, after it has been requested via the
@@ -859,6 +859,7 @@ Here are some examples of stuff you can do to configure 
@code{rcirc}.
 * Changing the time stamp format::
 * Defining a new command::
 * Using rcirc with bouncers::
+* Dealing with Bridge Bots::
 @end menu
 
 @node Skipping /away messages using handlers
@@ -933,7 +934,7 @@ Manual}, for details.
 how to include the date in the time stamp:
 
 @example
-(setq rcirc-time-format "%Y-%m-%d %H:%M ")
+(setopt rcirc-time-format "%Y-%m-%d %H:%M ")
 @end example
 
 @findex rcirc-when
@@ -969,16 +970,16 @@ because @code{rcirc-define-command} is not yet available, 
and without
 @cindex bouncer
 
 Some bouncers multiplex connections to various servers, but have to
-modify nicks and channel names to make this work. The channel
+modify nicks and channel names to make this work.  The channel
 @code{#emacs} on @code{irc.libera.chat} becomes
 @code{#emacs/irc.libera.chat}.
 
 @vindex rcirc-nick-filter
 @vindex rcirc-channel-filter
 The options @code{rcirc-nick-filter} and @code{rcirc-channel-filter}
-can be used to make this feel more natural. When set to functions,
+can be used to make this feel more natural.  When set to functions,
 these will be used to change how nicks and channel names are
-displayed. A simple configuration to fix the above example might be:
+displayed.  A simple configuration to fix the above example might be:
 
 @smallexample
 (defun my/rcirc-remove-suffix (STR)
@@ -988,10 +989,50 @@ displayed. A simple configuration to fix the above 
example might be:
         (substring str 0 (match-beginning 0))
       str)))
 
-(setq rcirc-nick-filter #'my/rcirc-remove-suffix
-      rcirc-channel-filter #'local/rcirc-soju-suffix)
+(setopt rcirc-nick-filter #'my/rcirc-remove-suffix
+        rcirc-channel-filter #'local/rcirc-soju-suffix)
 @end smallexample
 
+@node Dealing with Bridge Bots
+@section Dealing with Bridge Bots
+@cindex bridge
+
+It is increasingly common for IRC channels to be ``bridged'' onto
+other networks such as XMPP, Matrix, etc.  Sometimes the software does
+a good job at mapping each non-IRC user into an IRC user, but more
+often than not it doesn't.  In that case you might receive a message
+like:
+
+@example
+@verbatim
+09:47 <bridge> <john> I am not on IRC
+@end verbatim
+@end example
+
+where @samp{bridge} is a bot responsible for sending messages back and
+forth between networks, and @samp{john} is the user name of someone on
+a different network.  Note that the bot indicates this within the
+message (@verb{|<john> I am not on IRC|}) that appears in your chat
+buffer.
+
+@vindex rcirc-bridge-bot-alist
+If this annoys you, the user option @code{rcirc-bridge-bot-alist} may
+be of use.  It consists of descriptions of what users are these kinds
+of ``bridge bots'' and how they format their messages.  To handle the
+above example, we might set the user option to:
+
+@example
+(setopt rcirc-bridge-bot-alist
+        '(("bridge" . "<\\(.+?\\)>[[:space:]]+")))
+@end example
+
+If there is an entry for the current user, @code{rcirc} will take the
+associated regular expression and try to find a match in the message
+string.  If it manages to find anything, the matching expression is
+deleted from the message.  The regular expression must contain at
+least one group that will match the user name of the bridged message.
+This will then be used to replace the username of the bridge bot.
+
 @node GNU Free Documentation License
 @appendix GNU Free Documentation License
 @include doclicense.texi
diff --git a/doc/misc/reftex.texi b/doc/misc/reftex.texi
index 0c95b388cb..b30e5aeaa4 100644
--- a/doc/misc/reftex.texi
+++ b/doc/misc/reftex.texi
@@ -3539,18 +3539,6 @@ as a label of type @code{?p}.  Argument count for this 
macro starts only
 after the @samp{@{step+@}}, also when specifying how to get
 context.
 
-@item
-@b{Viper mode}@*
-@cindex Viper mode
-@cindex Key bindings, problems with Viper mode
-@findex viper-harness-minor-mode
-With @i{Viper} mode prior to Vipers version 3.01, you need to protect
-@RefTeX{}'s keymaps with
-
-@lisp
-(viper-harness-minor-mode "reftex")
-@end lisp
-
 @end itemize
 
 @page
diff --git a/doc/misc/semantic.texi b/doc/misc/semantic.texi
index eb5c7e0e67..25ba30d13c 100644
--- a/doc/misc/semantic.texi
+++ b/doc/misc/semantic.texi
@@ -25,8 +25,7 @@
 @copying
 This manual documents the Semantic library and utilities.
 
-Copyright @copyright{} 1999--2005, 2007, 2009--2022 Free Software
-Foundation, Inc.
+Copyright @copyright{} 1999--2022 Free Software Foundation, Inc.
 
 @quotation
 Permission is granted to copy, distribute and/or modify this document
@@ -65,13 +64,6 @@ modify this GNU manual.''
 @b{\kw\}
 @end macro
 
-@macro obsolete{old,new}
-@sp 1
-@strong{Compatibility}:
-@code{\new\} introduced in @semantic{} version 2.0 supersedes
-@code{\old\} which is now obsolete.
-@end macro
-
 @c *************************************************************************
 @c @ Document
 @c *************************************************************************
diff --git a/doc/misc/texinfo.tex b/doc/misc/texinfo.tex
index f86af0db3e..09f2d28c2f 100644
--- a/doc/misc/texinfo.tex
+++ b/doc/misc/texinfo.tex
@@ -3,7 +3,7 @@
 % Load plain if necessary, i.e., if running under initex.
 \expandafter\ifx\csname fmtname\endcsname\relax\input plain\fi
 %
-\def\texinfoversion{2022-08-20.19}
+\def\texinfoversion{2022-09-21.15}
 %
 % Copyright 1985, 1986, 1988, 1990-2022 Free Software Foundation, Inc.
 %
@@ -241,9 +241,6 @@
 %
 \def\finalout{\overfullrule=0pt }
 
-\newdimen\outerhsize \newdimen\outervsize % set by the paper size routines
-\newdimen\topandbottommargin \topandbottommargin=.75in
-
 % Output a mark which sets \thischapter, \thissection and \thiscolor.
 % We dump everything together because we only have one kind of mark.
 % This works because we only use \botmark / \topmark, not \firstmark.
@@ -317,16 +314,8 @@
 \newbox\footlinebox
 
 % When outputting the double column layout for indices, an output routine
-% is run several times, which hides the original value of \topmark.  This
-% can lead to a page heading being output and duplicating the chapter heading
-% of the index.  Hence, save the contents of \topmark at the beginning of
-% the output routine.  The saved contents are valid until we actually
-% \shipout a page.
-%
-% (We used to run a short output routine to actually set \topmark and
-% \firstmark to the right values, but if this was called with an empty page
-% containing whatsits for writing index entries, the whatsits would be thrown
-% away and the index auxiliary file would remain empty.)
+% is run several times, hiding the original value of \topmark.  Hence, save
+% \topmark at the beginning.
 %
 \newtoks\savedtopmark
 \newif\iftopmarksaved
@@ -351,15 +340,9 @@
   %
   \checkchapterpage
   %
-  % Retrieve the information for the headings from the marks in the page,
-  % and call Plain TeX's \makeheadline and \makefootline, which use the
-  % values in \headline and \footline.
-  %
-  % Common context changes for both heading and footing.
-  % Do this outside of the \shipout so @code etc. will be expanded in
-  % the headline as they should be, not taken literally (outputting ''code).
+  % Make the heading and footing.  \makeheadline and \makefootline
+  % use the contents of \headline and \footline.
   \def\commonheadfootline{\let\hsize=\txipagewidth \texinfochars}
-  %
   \ifodd\pageno \getoddheadingmarks \else \getevenheadingmarks \fi
   \global\setbox\headlinebox = \vbox{\commonheadfootline \makeheadline}%
   \ifodd\pageno \getoddfootingmarks \else \getevenfootingmarks \fi
@@ -617,21 +600,6 @@
 % @? is an end-of-sentence query.
 \def\?{?\spacefactor=\endofsentencespacefactor\space}
 
-% @frenchspacing on|off  says whether to put extra space after punctuation.
-%
-\def\onword{on}
-\def\offword{off}
-%
-\parseargdef\frenchspacing{%
-  \def\temp{#1}%
-  \ifx\temp\onword \plainfrenchspacing
-  \else\ifx\temp\offword \plainnonfrenchspacing
-  \else
-    \errhelp = \EMsimple
-    \errmessage{Unknown @frenchspacing option `\temp', must be on|off}%
-  \fi\fi
-}
-
 % @w prevents a word break.  Without the \leavevmode, @w at the
 % beginning of a paragraph, when TeX is still in vertical mode, would
 % produce a whole line of output instead of starting the paragraph.
@@ -2803,14 +2771,22 @@ end
 % @var unconditionally uses \sl.  This gives consistency for
 % parameter names whether they are in @def, @table @code or a
 % regular paragraph.
+%  To get ttsl font for @var when used in code context, @set txicodevaristt.
 % The \null is to reset \spacefactor.
 \def\aftersmartic{}
 \def\var#1{%
   \let\saveaftersmartic = \aftersmartic
   \def\aftersmartic{\null\let\aftersmartic=\saveaftersmartic}%
-  {\sl #1}\smartitaliccorrection
+  %
+  \ifflagclear{txicodevaristt}%
+    {\def\varnext{{{\sl #1}}\smartitaliccorrection}}%
+    {\def\varnext{\smartslanted{#1}}}%
+  \varnext
 }
 
+% To be removed after next release
+\def\SETtxicodevaristt{}% @set txicodevaristt
+
 \let\i=\smartitalic
 \let\slanted=\smartslanted
 \let\dfn=\smartslanted
@@ -2859,6 +2835,24 @@ end
 \catcode`@=\other
 \def\endofsentencespacefactor{3000}% default
 
+% @frenchspacing on|off  says whether to put extra space after punctuation.
+%
+\def\onword{on}
+\def\offword{off}
+%
+\let\frenchspacingsetting\plainnonfrenchspacing % used in output routine
+\parseargdef\frenchspacing{%
+  \def\temp{#1}%
+  \ifx\temp\onword \let\frenchspacingsetting\plainfrenchspacing
+  \else\ifx\temp\offword \let\frenchspacingsetting\plainnonfrenchspacing
+  \else
+    \errhelp = \EMsimple
+    \errmessage{Unknown @frenchspacing option `\temp', must be on|off}%
+  \fi\fi
+  \frenchspacingsetting
+}
+
+
 % @t, explicit typewriter.
 \def\t#1{%
   {\tt \defcharsdefault \plainfrenchspacing #1}%
@@ -3450,8 +3444,13 @@ $$%
        % Revert to plain's \scriptsize, which is 7pt.
        \count255=\the\fam $\fam\count255 \scriptstyle A$%
      \else
-       % For 11pt, we can use our lllsize.
-       \switchtolllsize A%
+       \ifx\curfontsize\smallword
+         % For footnotes and indices
+         \count255=\the\fam $\fam\count255 \scriptstyle A$%
+       \else
+         % For 11pt, we can use our lllsize.
+         \switchtolllsize A%
+       \fi
      \fi
      }%
      \vss
@@ -3459,6 +3458,7 @@ $$%
   \kern-.15em
   \TeX
 }
+\def\smallword{small}
 
 % Some math mode symbols.  Define \ensuremath to switch into math mode
 % unless we are already there.  Expansion tricks may not be needed here,
@@ -3829,15 +3829,16 @@ $$%
 \newtoks\oddfootline     % footline on odd pages
 
 % Now make \makeheadline and \makefootline in Plain TeX use those variables
-\headline={{\textfonts\rm
+\headline={{\textfonts\rm\frenchspacingsetting
             \ifchapterpage
               \ifodd\pageno\the\oddchapheadline\else\the\evenchapheadline\fi
             \else
               \ifodd\pageno\the\oddheadline\else\the\evenheadline\fi
             \fi}}
 
-\footline={{\textfonts\rm \ifodd\pageno \the\oddfootline
-                            \else \the\evenfootline \fi}\HEADINGShook}
+\footline={{\textfonts\rm\frenchspacingsetting
+            \ifodd\pageno \the\oddfootline \else \the\evenfootline \fi}%
+           \HEADINGShook}
 \let\HEADINGShook=\relax
 
 % Commands to set those variables.
@@ -3963,7 +3964,7 @@ $$%
 \global\oddfootline={\hfil}
 \global\evenheadline={\line{\folio\hfil\thistitle}}
 \global\oddheadline={\line{\thischapter\hfil\folio}}
-\global\evenchapheadline={\line{\folio\hfil}}
+\global\evenchapheadline={\line{\folio\hfil\thistitle}}
 \global\oddchapheadline={\line{\hfil\folio}}
 \global\let\contentsalignmacro = \chapoddpage
 }
@@ -4351,8 +4352,7 @@ $$%
 % undo it ourselves.
 \def\headitemfont{\b}% for people to use in the template row; not changeable
 \def\headitem{%
-  \checkenv\multitable
-  \crcr
+  \crcr % must appear first
   \gdef\headitemcrhook{\nobreak}% attempt to avoid page break after headings
   \global\everytab={\bf}% can't use \headitemfont since the parsing differs
   \the\everytab % for the first item
@@ -6755,6 +6755,11 @@ might help (with 'rm \jobname.?? \jobname.??s')%
     \ifnum\romancount=0 \global\romancount=\pagecount \fi
 }
 
+% \raggedbottom in plain.tex hardcodes \topskip so override it
+\catcode`\@=11
+\def\raggedbottom{\advance\topskip by 0pt plus60pt \r@ggedbottomtrue}
+\catcode`\@=\other
+
 % redefined for the two-volume lispref.  We always output on
 % \jobname.toc even if this is redefined.
 %
@@ -7115,7 +7120,7 @@ might help (with 'rm \jobname.?? \jobname.??s')%
   % collide with the section heading.
   \ifnum\lastpenalty>10000 \vskip\parskip \penalty\lastpenalty \fi
   %
-  \setbox\groupbox=\vbox\bgroup
+  \setbox\groupbox=\vtop\bgroup
       \baselineskip=0pt\parskip=0pt\lineskip=0pt
       \carttop
       \hbox\bgroup
@@ -7801,6 +7806,8 @@ might help (with 'rm \jobname.?? \jobname.??s')%
 % Print arguments.  Use slanted for @def*, typewriter for @deftype*.
 \def\defunargs#1{%
   \df \ifdoingtypefn \tt \else \sl \fi
+  \ifflagclear{txicodevaristt}{}%
+    {\def\var##1{{\setregularquotes \ttsl ##1}}}%
   #1%
 }
 
@@ -9311,6 +9318,12 @@ might help (with 'rm \jobname.?? \jobname.??s')%
     \imagexxx #1,,,,,\finish
   \fi
 }
+
+% Approximate height of a line in the standard text font.
+\newdimen\capheight
+\setbox0=\vbox{\tenrm H}
+\capheight=\ht0
+
 %
 % Arguments to @image:
 % #1 is (mandatory) image filename; we tack on .eps extension.
@@ -9340,7 +9353,7 @@ might help (with 'rm \jobname.?? \jobname.??s')%
     %
     % Place image in a \vtop for a top page margin that is (close to) correct,
     % as \topskip glue is relative to the first baseline.
-    \vtop\bgroup\hrule height 0pt\vskip-\parskip
+    \vtop\bgroup \kern -\capheight \vskip-\parskip
   \fi
   %
   % Enter horizontal mode so that indentation from an enclosing
@@ -11217,13 +11230,9 @@ directory should work if nowhere else does.}
   %
   \vsize = #1\relax
   \advance\vsize by \topskip
-  \outervsize = \vsize
-  \advance\outervsize by 2\topandbottommargin
   \txipageheight = \vsize
   %
   \hsize = #2\relax
-  \outerhsize = \hsize
-  \advance\outerhsize by 0.5in
   \txipagewidth = \hsize
   %
   \normaloffset = #4\relax
diff --git a/doc/misc/tramp.texi b/doc/misc/tramp.texi
index 0e55b6c1d2..7de64829c0 100644
--- a/doc/misc/tramp.texi
+++ b/doc/misc/tramp.texi
@@ -903,6 +903,30 @@ supports changing the remote login shell @command{/bin/sh}.
 Check the @samp{Share SSH connections if possible} control for that
 session.
 
+@item @option{docker}
+@cindex method @option{docker}
+@cindex @option{docker} method
+
+Integration for Docker containers.  The host name may be either a
+running container's name or ID, as returned by @samp{docker ps}.
+
+@item @option{podman}
+@cindex method @option{podman}
+@cindex @option{podman} method
+
+Podman is an alternative to @option{docker} which may be run rootless,
+if desired.
+
+@item @option{kubernetes}
+@cindex method @option{kubernetes}
+@cindex @option{kubernetes} method
+
+Integration for containers in Kubernetes pods.  The host name is a pod
+name returned by @samp{kubectl get pods}.  The first container in a
+pod is used.
+
+This method does not support user names.
+
 @end table
 
 
@@ -1763,36 +1787,21 @@ They can be installed with Emacs's Package Manager.  
This includes
 @c @item ibuffer-tramp.el
 @c Contact Svend Sorensen <svend@@ciffer.net>
 
-@item docker-tramp
-@cindex method @option{docker}
-@cindex @option{docker} method
-Integration for Docker containers.  A container is accessed via
-@file{@trampfn{docker,user@@container,/path/to/file}}, where
-@samp{user} is the (optional) user that you want to use, and
-@samp{container} is the id or name of the container.
-
-@item kubernetes-tramp
-@cindex method @option{kubectl}
-@cindex @option{kubectl} method
-Integration for Docker containers deployed in a Kubernetes cluster.
-It is derived from @samp{docker-tramp}.  A container is accessed via
-@file{@trampfn{kubectl,user@@container,/path/to/file}}, @samp{user}
-and @samp{container} have the same meaning as in @samp{docker-tramp}.
-
 @item lxc-tramp
 @cindex method @option{lxc}
 @cindex @option{lxc} method
 Integration for LXC containers.  A container is accessed via
 @file{@trampfn{lxc,container,/path/to/file}}, @samp{container} has the
-same meaning as in @samp{docker-tramp}.  A @samp{user} specification
-is ignored.
+same meaning as with the @option{docker} method.  A @samp{user}
+specification is ignored.
 
 @item lxd-tramp
 @cindex method @option{lxd}
 @cindex @option{lxd} method
 Integration for LXD containers.  A container is accessed via
 @file{@trampfn{lxd,user@@container,/path/to/file}}, @samp{user} and
-@samp{container} have the same meaning as in @samp{docker-tramp}.
+@samp{container} have the same meaning as with the @option{docker}
+method.
 
 @item magit-tramp
 @cindex method @option{git}
@@ -1997,10 +2006,10 @@ password of the target user.  If these connections 
happen on the local
 host, an entry with the local user and local host is used:
 
 @example
-machine @var{HOST} port sudo login @var{USER} password secret
+machine @var{host} port sudo login @var{user} password secret
 @end example
 
-@var{USER} and @var{HOST} are the strings returned by
+@var{user} and @var{host} are the strings returned by
 @code{(user-login-name)} and @code{(system-name)}.  If one of these
 methods is connected via a multi hop (@pxref{Multi-hops}), the
 credentials of the previous hop are used.
diff --git a/doc/misc/url.texi b/doc/misc/url.texi
index 5644027f95..546639b017 100644
--- a/doc/misc/url.texi
+++ b/doc/misc/url.texi
@@ -380,7 +380,7 @@ for specific schemes.
 * info::                        Emacs "Info" pages.
 * mailto::                      Sending email.
 * news/nntp/snews::             Usenet news.
-* rlogin/telnet/tn3270::        Remote host connectivity.
+* telnet/tn3270::               Remote host connectivity.
 * irc::                         Internet Relay Chat.
 * data::                        Embedded data URLs.
 * nfs::                         Networked File System.
@@ -675,9 +675,8 @@ environment variable @samp{NNTPSERVER}, or @samp{news} if 
that
 environment variable is unset.
 @end defopt
 
-@node rlogin/telnet/tn3270
-@section rlogin, telnet and tn3270
-@cindex rlogin
+@node telnet/tn3270
+@section telnet and tn3270
 @cindex telnet
 @cindex tn3270
 @cindex terminal emulation
@@ -694,10 +693,10 @@ telnet://@var{user}:@var{password}@@@var{host}:@var{port}
 but the @var{password} component is ignored.  By default, the
 @code{telnet} scheme is handled via Tramp (@pxref{Tramp}).
 
-To handle rlogin, telnet and tn3270 URLs, a @code{rlogin},
-@code{telnet} or @code{tn3270} (the program names and arguments are
-hardcoded) session is run in a @code{terminal-emulator} buffer.
-Well-known ports are used if the URL does not specify a port.
+To handle telnet and tn3270 URLs, a @code{telnet} or @code{tn3270}
+(the program names and arguments are hardcoded) session is run in a
+@code{terminal-emulator} buffer.  Well-known ports are used if the URL
+does not specify a port.
 
 @node irc
 @section irc
@@ -1039,12 +1038,6 @@ a list of symbols.  Possible values are:
 Use this method if you must first telnet and log into a gateway host,
 and then run telnet from that host to connect to outside machines.
 
-@item rlogin
-@cindex @command{rlogin}
-This method is identical to @code{telnet}, but uses @command{rlogin}
-to log into the remote machine without having to send the username and
-password over the wire every time.
-
 @item socks
 @cindex @sc{socks}
 Use if the firewall has a @sc{socks} gateway running on it.  The
@@ -1087,19 +1080,6 @@ The password to send when logging in.
 This is a regular expression that matches the shell prompt.
 @end defopt
 
-@defopt url-gateway-rlogin-host
-Host to @samp{rlogin} to before telnetting out.
-@end defopt
-@defopt url-gateway-rlogin-parameters
-Parameters to pass to @samp{rsh}.
-@end defopt
-@defopt url-gateway-rlogin-user-name
-User name to use when logging in to the gateway.
-@end defopt
-@defopt url-gateway-prompt-pattern
-This is a regular expression that matches the shell prompt.
-@end defopt
-
 @defopt socks-server
 This specifies the default server, it takes the form
 @w{@code{("Default server" @var{server} @var{port} @var{version})}}
@@ -1327,8 +1307,6 @@ from the local machine.  The supported methods are:
 @table @code
 @item telnet
 Run telnet in a subprocess to connect;
-@item rlogin
-Rlogin to another machine to connect;
 @item socks
 Connect through a socks server;
 @item ssl
diff --git a/doc/misc/viper.texi b/doc/misc/viper.texi
index 0703667ecc..d36019f06a 100644
--- a/doc/misc/viper.texi
+++ b/doc/misc/viper.texi
@@ -34,7 +34,7 @@ modify this GNU manual.''
 @titlepage
 @title Viper Is a Package for Emacs Rebels
 @subtitle a Vi emulator for Emacs
-@subtitle November 2008, Viper Version 3.11.2
+@subtitle July 2013, Viper Version 3.14.2
 
 @author Michael Kifer (Viper)
 @author Aamod Sane (VIP 4.4)
@@ -325,9 +325,9 @@ lines (in the given order!):
 @noindent
 in your @file{~/.emacs} file.  The @file{.emacs} file is placed in your
 home directory and it is be executed every time you invoke Emacs.  This is
-the place where all general Emacs customization takes place.  Beginning with
-version 20.0, Emacsen have an interactive interface, which simplifies the
-job of customization significantly.
+the place where all general Emacs customization takes place.  Emacs
+has an interactive interface (@kbd{M-x customize}), which simplifies
+the job of customization significantly.
 
 Viper also uses the file @file{~/.emacs.d/viper} for Viper-specific 
customization.
 The location of Viper customization file can be changed by setting the
diff --git a/etc/AUTHORS b/etc/AUTHORS
index f6349df5bc..2659395898 100644
--- a/etc/AUTHORS
+++ b/etc/AUTHORS
@@ -103,8 +103,8 @@ Alakazam Petrofsky: changed hanoi.el
 Alan Mackenzie: wrote cc-awk.el
 and co-wrote cc-align.el cc-cmds.el cc-defs.el cc-engine.el cc-fonts.el
   cc-langs.el cc-mode.el cc-styles.el cc-vars.el
-and changed cc-mode.texi minibuf.c bytecomp.el edebug.el follow.el
-  window.c display.texi subr.el syntax.texi progmodes/compile.el
+and changed cc-mode.texi minibuf.c bytecomp.el window.c edebug.el
+  follow.el display.texi subr.el syntax.texi progmodes/compile.el
   programs.texi eval.c keyboard.c lisp.h modes.texi window.el
   windows.texi cus-start.el font-lock.el frame.c isearch.el
   and 167 other files
@@ -994,7 +994,8 @@ and changed calc.el replace.el update-game-score.c 
calc-ext.el
 
 Colin Williams: changed calc.texi
 
-Colin Woodbury: changed files.el files.texi macros.texi shortdoc.el
+Colin Woodbury: changed files.el cl-seq.el files.texi macros.texi
+  shortdoc.el
 
 Constantin Kulikov: changed server.el startup.el
 
@@ -2056,10 +2057,10 @@ Gregor Schmid: changed intervals.c intervals.h 
tcl-mode.el textprop.c
 
 Gregory Chernov: changed nnslashdot.el
 
-Gregory Heytings: changed isearch.el minibuffer.el mini.texi quail.el
-  search.texi simple.el HELLO buffers.texi diff-mode.el emake facemenu.el
-  files.el fringe.c help-macro.el icomplete.el keyboard.c misc-lang.el
-  modula2.el pcmpl-gnu.el print.c pulse.el and 4 other files
+Gregory Heytings: changed isearch.el minibuffer.el efaq.texi mini.texi
+  quail.el search.texi simple.el HELLO buffers.texi diff-mode.el emake
+  facemenu.el fbterm.el files.el fringe.c help-macro.el icomplete.el
+  keyboard.c misc-lang.el modula2.el pcmpl-gnu.el and 6 other files
 
 Grégory Mounié: changed display.texi hi-lock.el man.el xfns.c
 
@@ -4518,7 +4519,7 @@ Philipp Stephani: wrote callint-tests.el checkdoc-tests.el
 and changed emacs-module.c emacs-module-tests.el configure.ac json.c
   process.c eval.c internals.texi json-tests.el process-tests.el alloc.c
   emacs-module.h.in emacs.c lread.c nsterm.m lisp.h pdumper.c bytecomp.el
-  callproc.c seccomp-filter.c gtkutil.c files.el and 184 other files
+  callproc.c seccomp-filter.c gtkutil.c files.el and 185 other files
 
 Phillip Lord: wrote ps-print-tests.el w32-feature.el
 and changed build-zips.sh build-dep-zips.py lisp/Makefile.in undo.c
@@ -4787,7 +4788,7 @@ Robert Pluim: wrote nsm-tests.el
 and changed configure.ac process.c blocks.awk network-stream-tests.el
   font.c processes.texi ftfont.c gtkutil.c vc-git.el process-tests.el
   emoji-zwj.awk gnutls.el network-stream.el nsm.el tramp.texi mml-sec.el
-  nsterm.m unicode xfns.c auth.texi composite.c and 138 other files
+  nsterm.m unicode xfns.c auth.texi composite.c and 139 other files
 
 Robert Thorpe: changed cus-start.el indent.el rmail.texi
 
@@ -5180,8 +5181,8 @@ Stefan Kangas: wrote bookmark-tests.el cal-julian-tests.el
 and co-wrote help-tests.el keymap-tests.el
 and changed efaq.texi checkdoc.el package.el cperl-mode.el bookmark.el
   help.el keymap.c subr.el simple.el erc.el ediff-util.el idlwave.el
-  time.el bytecomp-tests.el comp.el speedbar.el bytecomp.el edebug.el
-  emacs-lisp-intro.texi flyspell.el ibuffer.el and 1344 other files
+  time.el bytecomp-tests.el comp.el emacs-lisp-intro.texi speedbar.el
+  bytecomp.el edebug.el flyspell.el ibuffer.el and 1348 other files
 
 Stefan Merten: co-wrote rst.el
 
diff --git a/etc/ERC-NEWS b/etc/ERC-NEWS
index 7f95cdd39a..988eb1e09c 100644
--- a/etc/ERC-NEWS
+++ b/etc/ERC-NEWS
@@ -59,9 +59,17 @@ which, when present, becomes the first argument passed to 
the "USER"
 IRC command.  The traditional way of setting this globally, via
 'erc-email-userid', is still honored.
 
-** Additional display options for updated buffers.
-Additional flexibility is now available for controlling the behavior
-of newly created target buffers, especially during reconnection.
+** Changes to display options for new ERC buffers.
+The default value for the option 'erc-join-buffer', which determines
+how new buffers are displayed, has been changed to 'bury' for security
+reasons.  Although the old value of 'buffer' is still accessible,
+along with its original behavior, users wanting a safer alternative
+can now opt for an improved 'window-noselect' instead.  It still
+offers the same pronounced visual cue when connecting and joining but
+now avoids any hijacking of the active window as well.
+
+Beyond this, additional flexibility is now available for controlling
+the behavior of newly created target buffers during reconnection.
 
 ** Improved handling of multiline prompt input.
 This means better detection and handling of intervening and trailing
@@ -77,6 +85,12 @@ now collapse into an alternate form designated by the option
 but can be fine-tuned via the repurposed, formerly abandoned option
 'erc-hide-prompt'.
 
+Certain commands provided by the 'erc-match' module, such as
+'erc-add-keyword', 'erc-add-pal', and others, now optionally ask
+whether to 'regexp-quote' the current input.  A new option,
+'erc-match-quote-when-adding', has been added to allow for retaining
+the old behavior, if desired.
+
 A bug has been fixed affecting users of the Soju bouncer: outgoing
 messages during periods of heavy traffic no longer disappear.
 
diff --git a/etc/HELLO b/etc/HELLO
index d73465318c..b05c09da3c 100644
--- a/etc/HELLO
+++ b/etc/HELLO
@@ -24,6 +24,7 @@ Non-ASCII examples:
 
 LANGUAGE (NATIVE NAME) HELLO
 ---------------------- -----
+Adlam (𞤀𞤣𞤤𞤢𞤥)  𞤅𞤢𞤤𞤢𞥄𞤥
 Amharic (አማርኛ) ሠላም
 Arabic (العربيّة)      السّلام عليكم
 Armenian (հայերեն)     Բարև ձեզ
@@ -56,6 +57,7 @@ Finnish (suomi)       Hei / Hyvää päivää
 French (français)      Bonjour / Salut
 Georgian (ქართული)     გამარჯობა
 German (Deutsch)       Guten Tag / Grüß Gott
+Gothic (𐌲𐌿𐍄𐌹𐍃𐌺𐌰)       𐌷𐌰𐌹𐌻𐍃 / 𐌷𐌰𐌹𐌻𐌰
 Grantha (𑌗𑍍𑌰𑌨𑍍𑌥)       𑌨𑌮𑌸𑍍𑌤𑍇 / 𑌨𑌮𑌸𑍍𑌕𑌾𑌰𑌃
 Greek (ελληνικά)       Γειά σας
 Greek, ancient (ἑλληνική)      Οὖλέ τε καὶ μέγα χαῖρε
@@ -84,6 +86,7 @@ Maldivian (ދިވެހި)    އައްސަލާމު ޢަލައިކުމް / ކިހިނ
 Maltese (il-Malti)     Bonġu / Saħħa
 Mathematics    ∀ p ∈ world • hello p  □
 Meetei Mayek (ꯃꯤꯇꯩ ꯃꯌꯦꯛ)       ꯈꯨꯔꯨꯝꯖꯔꯤ
+Mende Kikakui (𞠀𞠁𞠂)    𞠛𞠉
 Modi (𑘦𑘻𑘚𑘲)    𑘡𑘦𑘭𑘿𑘎𑘰𑘨
 Mongolian (монгол хэл) Сайн байна уу?
 Northern Thai (ᨣᩣᩴᨾᩮᩬᩥᨦ / ᨽᩣᩈᩣᩃ᩶ᩣ᩠ᨶᨶᩣ) ᩈ᩠ᩅᩢᩔ᩠ᨯᩦᨣᩕᩢ᩠ᨸ
@@ -111,9 +114,9 @@ Tibetan (བོད་སྐད་)  བཀྲ་ཤིས་བདེ་ལེག
 Tigrigna (ትግርኛ)        ሰላማት
 Tirhuta (𑒞𑒱𑒩𑒯𑒳𑒞𑒰)      𑒣𑓂𑒩𑒢𑒰𑒧 / 𑒮𑒲𑒞𑒰𑒩𑒰𑒧
 Turkish (Türkçe)       Merhaba
-Ukrainian (українська) Вітаю
+Ukrainian (українська) Вітаю / Добрий день! / Привіт
 Vietnamese (tiếng Việt)        Chào bạn
-
+Wancho (𞋒𞋀𞋉𞋃𞋕)         𞋂𞋈𞋛
 
 
 <x-charset><param>japanese-jisx0208</param>Japanese (日本語)      
こんにちは</x-charset> <x-charset><param>katakana-jisx0201</param>/ コンニチハ
diff --git a/etc/HISTORY b/etc/HISTORY
index bb4e3e38e1..9e4becc946 100644
--- a/etc/HISTORY
+++ b/etc/HISTORY
@@ -226,6 +226,8 @@ GNU Emacs 27.2 (2021-03-25) emacs-27.2
 
 GNU Emacs 28.1 (2022-04-04) emacs-28.1
 
+GNU Emacs 28.2 (2022-09-12) emacs-28.2
+
 
 ----------------------------------------------------------------------
 This file is part of GNU Emacs.
diff --git a/etc/NEWS b/etc/NEWS
index 1317cd0128..b616c5382c 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -24,11 +24,24 @@ applies, and please also update docstrings as needed.
 
 * Installation Changes in Emacs 29.1
 
+---
+** Ahead-of-time native compilation can now be specified via configure.
+Use '--with-native-compilation=aot' to specify that all the Lisp files
+in the Emacs tree should be natively compiled ahead of time.  (This is
+slow on most machines.)
+
 +++
 ** Emacs can be built with built-in support for accessing SQLite databases.
 This uses the popular sqlite3 library, and can be disabled by using
 the '--without-sqlite3' option to the 'configure' script.
 
++++
+** Support for the WebP image format.
+This support is built by default when the libwebp library is
+available, and includes support for animated WebP images.  To disable
+WebP support, use the '--without-webp' configure flag.  Image
+specifiers can now use ':type webp'.
+
 +++
 ** Emacs has been ported to the Haiku operating system.
 The configuration process should automatically detect and build for
@@ -100,7 +113,7 @@ as was already the case for all the non-preloaded files.
 ** Emacs Sessions (Desktop)
 
 +++
-*** New option to load a locked desktop if locking Emacs is not running.
+*** New user option to load a locked desktop if locking Emacs is not running.
 The option 'desktop-load-locked-desktop' can now be set to the value
 'check-pid', which means to allow loading a locked ".emacs.desktop"
 file if the Emacs process which locked it is no longer running on the
@@ -109,6 +122,12 @@ files when the Emacs session which locked it crashes, or 
was otherwise
 interrupted, and didn't exit gracefully.  See the "(emacs) Saving
 Emacs Sessions" node in the Emacs manual for more details.
 
+** Miscellaneous
+
++++
+*** User option 'minibuffer-eldef-shorten-default' is now obsolete.
+Customize the user option 'minibuffer-default-prompt-format' instead.
+
 
 * Startup Changes in Emacs 29.1
 
@@ -131,6 +150,11 @@ and then execute the rest of the script file as Emacs 
Lisp.  When it
 reaches the end of the script, Emacs will exit with an exit code from
 the value of the final form.
 
++++
+** New function 'substitute-quotes'.
+This function works like 'substitute-command-keys' but only
+substitutes quote characters.
+
 +++
 ** Emacs now supports setting 'user-emacs-directory' via '--init-directory'.
 
@@ -146,9 +170,16 @@ time.
 
 ** Native Compilation
 
++++
+*** New variable 'inhibit-automatic-native-compilation'.
+If set, Emacs will inhibit native compilation (and won't write
+anything to the eln cache automatically).  The variable is initialised
+from the 'EMACS_INHIBIT_AUTOMATIC_NATIVE_COMPILATION' environment
+variable on Emacs startup.
+
 ---
 *** New command 'native-compile-prune-cache'.
-This command deletes older ".eln" cache entries (but not the ones for
+This command deletes older eln cache entries (but not the ones for
 the current Emacs version).
 
 ---
@@ -156,14 +187,56 @@ the current Emacs version).
 This function can be called in your init files to change the
 user-specific directory where Emacs stores the "*.eln" files produced
 by native compilation of Lisp packages Emacs loads.  The default
-eln-cache directory is unchanged: it is the "eln-cache" subdirectory
+eln cache directory is unchanged: it is the "eln-cache" subdirectory
 of 'user-emacs-directory'.
 
 
 * Incompatible changes in Emacs 29.1
 
 +++
-*** Explicitly-set read-only state is preserved when reverting a buffer.
+** The image commands have changed key bindings.
+In previous Emacs versions, images have had the '+', '-' and 'r' keys
+bound when point is over an image.  In Emacs 29.1, additional commands
+were added, and this made it more likely that users would trigger the
+image commands by mistake.  To avoid this, all image commands have
+moved to the 'i' keymap, so '+' is now 'i +', '-' is now 'i -', and
+'r' is now 'i r'.  In addition, these commands are now repeating, so
+you can rotate an image twice by saying 'i r r', for instance.
+
++++
+** Emacs now picks the correct coding system for X input methods.
+Previously, Emacs would use the locale coding system for input
+methods, which could in some circumstances be incorrect, especially
+when the input method chose to fall back to some other coding system.
+
+Now, Emacs automatically detects the coding system used by input
+methods, and uses that to decode input in preference to the value of
+'locale-coding-system'.  This unfortunately means that users who have
+changed the coding system used to decode X keyboard input must adjust
+their customizations to 'locale-coding-system' to the variable
+'x-input-coding-system' instead.
+
++++
+** Bookmarks no longer include context for encrypted files.
+If you're visiting an encrypted file, setting a bookmark no longer
+includes excerpts from that buffer in the bookmarks file.  This is
+implemented by the new hook 'bookmark-inhibit-context-functions',
+where packages can register a function which returns non-nil for file
+names to be excluded from adding such excerpts.
+
+---
+** 'show-paren-mode' is now disabled in 'special-mode' buffers.
+In Emacs versions previous to Emacs 28.1, 'show-paren-mode' defaulted
+off.  In Emacs 28.1, the mode was switched on in all buffers.  In
+Emacs 29.1, this was changed to be switched on in all editing-related
+buffers, but not in buffers that inherit from 'special-mode'.  To get
+back to how things worked in Emacs 28.1, put the following in your
+init file:
+
+    (setopt show-paren-predicate t)
+
++++
+** Explicitly-set read-only state is preserved when reverting a buffer.
 If you use the 'C-x C-q' command to change the read-only state of the
 buffer and then revert it, Emacs would previously use the file
 permission bits to determine whether the buffer should be read-only
@@ -171,7 +244,7 @@ after reverting the buffer.  Emacs now remembers the 
decision made in
 'C-x C-q'.
 
 ---
-*** The Gtk selection face is no longer used for the region.
+** The Gtk selection face is no longer used for the region.
 The combination of a Gtk-controlled background and a foreground color
 controlled by the internal Emacs machinery led to low-contrast faces
 in common default setups.  Emacs now uses the same 'region' face on
@@ -297,6 +370,10 @@ been restricted to "...", '...', /.../, |...|, (...), 
[...], <...>,
 and {...}.  See the "(eshell) Argument Predication and Modification"
 node in the Eshell manual for more details.
 
++++
+*** Eshell pipelines now only pipe stdout by default.
+To pipe both stdout and stderr, use the '|&' operator instead of '|'.
+
 ---
 ** The 'delete-forward-char' command now deletes by grapheme clusters.
 This command is by default bound to the <Delete> function key
@@ -339,6 +416,37 @@ the major mode according to 'initial-major-mode', like at 
Emacs
 startup.  Previously, these functions ignored
 'initial-scratch-message' and left "*scratch*" in 'fundamental-mode'.
 
+---
+** Naming of Image-Dired thumbnail files has changed.
+Names of thumbnail files generated when 'image-dired-thumbnail-storage'
+is 'image-dired' now always end in ".jpg".  This fixes various issues
+on different platforms, but means that thumbnails generated in Emacs 28
+will not be used in Emacs 29, and vice-versa.  If disk space is an
+issue, consider deleting the 'image-dired-dir' directory after
+upgrading (usually "~/.emacs.d/image-dired/").
+
+---
+** The 'rlogin' method in the URL library is now obsolete.
+Emacs will now display a warning if you request a URL like
+"rlogin://foo@example.org".
+
+---
+** Setting 'url-gateway-method' to 'rlogin' is now obsolete.
+Emacs will now display a warning when setting it to that value.
+The user options 'url-gateway-rlogin-host',
+'url-gateway-rlogin-parameters', and 'url-gateway-rlogin-user-name'
+are also obsolete.
+
+---
+** The linum.el library is now obsolete.
+We recommend using either the built-in 'display-line-numbers-mode', or
+the 'nlinum' package from GNU ELPA instead.  The former has better
+performance, but the latter is closer to a drop-in replacement.
+
+---
+** The thumbs.el library is now obsolete.
+We recommend using 'M-x image-dired' instead.
+
 ---
 ** The autoarg.el library is now marked obsolete.
 This library provides the 'autoarg-mode' and 'autoarg-kp-mode' minor
@@ -379,6 +487,18 @@ option) and can be set to nil to disable Just-in-time Lock 
mode.
 
 * Changes in Emacs 29.1
 
++++
+** New user option 'major-mode-remap-alist' to specify favorite major modes.
+This user option lets you remap the default modes (e.g. 'perl-mode' or
+'latex-mode') to your favorite ones (e.g. 'cperl-mode' or
+'LaTeX-mode') without having to use 'defalias', which can have
+undesirable side effects.
+This applies to all modes specified via 'auto-mode-alist', file-local
+variables, etc.
+
+---
+** Emacs now supports Unicode Standard version 15.0.
+
 ---
 ** New user option 'electric-quote-replace-consecutive'.
 
@@ -419,10 +539,6 @@ increase and decrease the font size globally.  
Additionally, the
 user option 'global-text-scale-adjust-resizes-frames' controls whether
 the frames are resized when the font size is changed.
 
-+++
-** New function 'file-parent-directory'.
-Get the parent directory of a file.
-
 ** New config variable 'syntax-wholeline-max' to reduce the cost of long lines.
 This variable is used by some operations (mostly syntax-propertization
 and font-locking) to treat lines longer than this variable as if they
@@ -533,8 +649,9 @@ If non-nil, this option allows scrolling a window while 
dragging text
 around without a scroll wheel.
 
 +++
-*** 'mouse-drag-copy-region' can now be 'non-empty'.
-This inhibits putting empty strings onto the kill ring.
+*** The value of 'mouse-drag-copy-region' can now be the symbol 'non-empty'.
+This prevents mouse drag gestures from putting empty strings onto the
+kill ring.
 
 +++
 ** New user options 'dnd-indicate-insertion-point' and 'dnd-scroll-margin'.
@@ -567,7 +684,9 @@ yes/no question before executing.  The new function 
'command-query' is
 a convenient method of making commands disabled in this way.
 
 ---
-** 'count-lines' will now report buffer totals if given a prefix.
+** 'count-words' will now report buffer totals if given a prefix.
+Without a prefix, it will only report the word count for the narrowed
+part of the buffer.
 
 +++
 ** 'count-words' will now report sentence count when used interactively.
@@ -612,11 +731,6 @@ This value stands for the value of the corresponding 
attribute of the
 'default' face.  It can be used to reset attribute values produced by
 inheriting from other faces.
 
-+++
-** New function 'buffer-text-pixel-size'.
-This is similar to 'window-text-pixel-size', but can be used when the
-buffer isn't displayed.
-
 +++
 ** New X resource: "borderThickness".
 This controls the thickness of the external borders of the menu bars
@@ -720,6 +834,25 @@ saved to the X primary selection, following the
 'select-active-regions' variable.  This support is enabled when
 'tty-select-active-regions' is non-nil.
 
+---
+*** New command to set up display of unsupported characters.
+The new command 'standard-display-by-replacement-char' produces Lisp
+code that sets up the 'standard-display-table' to use a replacement
+character for display of characters that the text-mode terminal
+doesn't support.  It is most useful with the Linux console and similar
+terminals, where Emacs has a reliable way of determining which
+characters have glyphs in the font loaded into the terminal's memory.
+
+---
+*** New functions to set terminal output buffer size.
+The new functions 'tty--set-output-buffer-size' and
+'tty--output-buffer-size' allow setting and retrieving the output
+buffer size of a terminal device.  The default buffer size is and has
+always been BUFSIZ, which is defined in your system's stdio.h.  When
+you set a buffer size with 'tty--set-output-buffer-size', this also
+prevents Emacs from explicitly flushing the tty output stream, except
+at the end of display update.
+
 ** ERT
 
 +++
@@ -890,17 +1023,23 @@ or is itself too long.
 *** New user option 'outline-minor-mode-use-buttons'.
 If non-nil, Outline Minor Mode will use buttons to hide/show outlines
 in addition to the ellipsis.  The default is nil in editing modes, but
-non-nil in 'special-mode' and its derivatives.
+non-nil in 'help-mode' and its derivatives.
 
 +++
-** Support for the WebP image format.
-This support is built by default when the libwebp library is
-available, and includes support for animated WebP images.  To disable
-WebP support, use the '--without-webp' configure flag.  Image
-specifiers can now use ':type webp'.
+*** New user option 'outline-minor-mode-use-margins'.
+If non-nil, Outline Minor Mode will use the window margins to
+hide/show outlines in addition to the ellipsis.  The default is
+non-nil in 'special-mode' and its derivatives, and it can be used in
+editing modes.
 
 ** Windows
 
++++
+*** New commands 'split-root-window-below' and 'split-root-window-right'.
+These commands split the root window in two, and are bound to 'C-x w 2'
+and 'C-x w 3', respectively.  A number of other useful window-related
+commands are now available on the 'C-x w' prefix.
+
 +++
 *** New user option 'display-buffer-avoid-small-windows'.
 If non-nil, this should be a window height, a number.  Windows smaller
@@ -956,10 +1095,15 @@ suspicious and could be malicious.
 ** Emacs server and client changes
 
 +++
-*** New command-line option '-r' for emacsclient.
+*** New command-line option '-r'/'--reuse-frame' for emacsclient.
 With this command-line option, Emacs reuses an existing graphical client
 frame if one exists; otherwise it creates a new frame.
 
++++
+*** New command-line option '-w N'/'--timeout=N' for emacsclient.
+With this command-line option, emacsclient will exit if Emacs does not
+respond within N seconds.  The default is to wait forever.
+
 +++
 *** 'server-stop-automatically' can be used to automatically stop the server.
 The Emacs server will be automatically stopped when certain conditions
@@ -977,6 +1121,30 @@ Rcirc will use the default 'completion-at-point' 
mechanism.  The
 conventional IRC behavior of completing by cycling through the
 available options can be restored by enabling this option.
 
++++
+*** New user option 'rcirc-bridge-bot-alist'.
+If you are in a channel where a bot is responsible for bridging
+between networks, you can use this variable to make these messages
+appear more native.  For example you might set the option to:
+
+    (setq rcirc-bridge-bot-alist '(("bridge" . "{\\(.+?\\)}[[:space:]]+")))
+
+for messages like
+
+    09:47 <bridge> {john} I am not on IRC
+
+to be reformatted into
+
+    09:47 <john> I am not on IRC
+
+---
+*** New formatting commands.
+Most IRC clients (including rcirc) support basic formatting using
+control codes.  Under the 'C-c C-f' prefix a few commands have been
+added to insert these automatically.  For example, if a region is
+active and 'C-c C-f C-b' is invoked, markup is inserted for the region
+to be highlighted bold.
+
 ** Imenu
 
 +++
@@ -1084,6 +1252,14 @@ When nil, this prevents comint from deleting the current 
input when
 inserting previous input using '<mouse-2>'.  The default is t, to
 preserve past behavior.
 
+---
+*** New minor mode 'comint-fontify-input-mode'.
+This minor mode is enabled by default in "*shell*" and "*ielm*"
+buffers.  It fontifies input text according to 'shell-mode' or
+'emacs-lisp-mode' font-lock rules.  Customize the user options
+'shell-fontify-input-enable' and 'ielm-fontify-input-enable' to nil if
+you don't want to enable input fontification by default.
+
 ** Mwheel
 
 ---
@@ -1108,10 +1284,10 @@ to edit such sequences by allowing point to "enter" the 
sequence.
 *** Support for many old scripts and writing systems.
 Emacs now supports and has language-environments and input methods for
 several dozens of old scripts that were used in the past for various
-languages in South and South-East Asia.  For each such script Emacs
-now has font-selection and character composition rules, a language
-environment, and an input method.  The newly-added scripts and the
-corresponding language environments are:
+languages.  For each such script Emacs now has font-selection and
+character composition rules, a language environment, and an input
+method.  The newly-added scripts and the corresponding language
+environments are:
 
 Tai Tham script and the Northern Thai language environment
 Brahmi script and language environment
@@ -1138,6 +1314,11 @@ Grantha script and language environment
 Kharoshthi script and language environment
 Lepcha script and language environment
 Meetei Mayek script and language environment
+Adlam script and language environment
+Mende Kikakui script and language environment
+Wancho script and language environment
+Toto script and language environment
+Gothic script and language environment
 
 ---
 *** The "Oriya" language environment was renamed to "Odia".
@@ -1151,6 +1332,9 @@ supported.
 Type 'C-u C-h t' to select it in case your language setup does not do
 so automatically.
 
+---
+*** New Ukrainian translation of the Emacs Tutorial.
+
 ---
 *** New default phonetic input method for the Tamil language environment.
 The default input method for the Tamil language environment is now
@@ -1161,6 +1345,17 @@ change the input method's translation rules, customize 
the user option
 
 * Changes in Specialized Modes and Packages in Emacs 29.1
 
+** ecomplete
+
+---
+*** New commands 'ecomplete-edit' and 'ecomplete-remove'.
+These allow you to (respectively) edit and bulk-remove entries from
+the ecomplete database.
+
+---
+*** New user option 'ecomplete-auto-select'.
+If non-nil and there's only one matching option, auto-select that.
+
 ** Dired
 
 +++
@@ -1253,6 +1448,10 @@ This controls how statements like the following are 
indented:
     foo &&
         bar
 
+*** New Flymake backend using the ShellCheck program.
+It is enabled by default, but requires that the external "shellcheck"
+command is installed.
+
 ** Cperl Mode
 
 ---
@@ -1288,6 +1487,11 @@ Sets the value of the buffer-local variable 
'whitespace-style' in
 'diff-mode' buffers.  By default, this variable is '(face trailing)',
 which preserves behavior from previous Emacs versions.
 
++++
+*** New user option 'diff-add-log-use-relative-names'.
+If non-nil insert file names in ChangeLog skeletons relative to the
+VC root directory.
+
 ** Ispell
 
 ---
@@ -1402,8 +1606,8 @@ characters instead of just 'SPC' and 'TAB'.
 This mode adds some highlighting, fixes the 'M-q' command, and has
 commands for doing maintenance of the Emacs NEWS files.  In addition,
 this mode turns on 'outline-minor-mode', and thus displays
-customizable icons (see 'icon-preference') on heading lines.  To
-disable these icons, customize 'outline-minor-mode-use-buttons' to a
+customizable icons (see 'icon-preference') in the margins.  To
+disable these icons, customize 'outline-minor-mode-use-margins' to a
 nil value.
 
 ---
@@ -1413,7 +1617,7 @@ uses the 'key-parse' syntax.  It replaces the old 
'kmacro-lambda-form'
 (which is now declared obsolete).
 
 ---
-** 'savehist.el' can now truncate variables that are too long.
+** savehist.el can now truncate variables that are too long.
 An element of 'savehist-additional-variables' can now be of the form
 '(VARIABLE . MAX-ELTS)', which means to truncate the VARIABLE's value to
 at most MAX-ELTS elements (if the value is a list) before saving the
@@ -1518,6 +1722,9 @@ with 'C-s C-s', but also after typing a character.
 Non-nil means that the default definitions of equivalent characters
 are overridden.
 
+*** New command 'describe-char-fold-equivalences'.
+It displays character equivalences used by 'char-fold-to-regexp'.
+
 +++
 *** New command 'isearch-emoji-by-name'.
 It is bound to 'C-x 8 e RET' during an incremental search.  The
@@ -1532,11 +1739,13 @@ completion, and adds the Emoji into the search string.
 This allows an easy way to toggle seeing all glyphless characters in
 the current buffer.
 
+---
 *** The extra slot of 'glyphless-char-display' can now have cons values.
 The extra slot of the 'glyphless-char-display' char-table can now have
 values that are cons cells, specifying separate values for text-mode
 and GUI terminals.
 
++++
 *** "Replacement character" feature for undisplayable characters on TTYs.
 The 'acronym' method of displaying glyphless characters on text-mode
 frames treats single-character acronyms specially: they are displayed
@@ -1571,13 +1780,53 @@ This fills the region to be no wider than a specified 
pixel width.
 This will take you to the gnu.org web server's version of the current
 info node.  This command only works for the Emacs and Emacs Lisp manuals.
 
+** Shortdoc
+
+---
+*** New command 'shortdoc-copy-function-as-kill' bound to 'w'.
+It copies the name of the function near point into the kill ring.
+
+---
+*** 'N' and 'P' are now bound to 'shortdoc-{next,previous}-section'.
+This is in addition to the old keybindings 'C-c C-n' and 'C-c C-p'.
+
 ** VC
 
+---
+*** New command 'vc-pull-and-push'.
+This commands first does a "pull" command, and if that is successful,
+do a "push" command afterwards.
+
++++
+*** 'C-x v b' prefix key is used now for branch commands.
+'vc-print-branch-log' is bound to 'C-x v b l', and new commands are
+'vc-create-branch' ('C-x v b c') and 'vc-switch-branch' ('C-x v b s').
+The VC Directory buffer now uses the prefix 'b' for these branch-related
+commands.
+
 +++
 *** New command '%' ('vc-dir-mark-by-regexp').
 This command marks files based on a regexp.  If given a prefix
 argument, unmark instead.
 
++++
+*** New command 'C-x v !' ('vc-edit-next-command').
+This prefix command requests editing of the next VC shell command
+before execution.  For example, in a Git repository, you can produce a
+log of more than one branch by typing 'C-x v ! C-x v b l' and then
+appending additional branch names to the 'git log' command.
+
+---
+*** 'C-x v v' in a diffs buffer allows to commit only some of the changes.
+This command is intended to allow you to commit only some of the
+changes you have in your working tree.  Begin by creating a buffer
+with the changes against the last commit, e.g. with 'C-x v D'
+('vc-root-diff').  Then edit the diffs to remove the hunks you don't
+want to commit.  Finally, type 'C-x v v' in that diff buffer to commit
+only part of your changes, those whose hunks were left in the buffer.
+
+Currently this feature works only with the Git as 'vc-backend'.
+
 ---
 *** 'C-x v v' on an unregistered file will now use the most specific backend.
 Previously, if you had an SVN-covered "~/" directory, and a Git-covered
@@ -1586,6 +1835,17 @@ directory in "~/foo/bar", using 'C-x v v' on a new, 
unregistered file
 in the Git repository in "~/foo/bar".  This makes this command
 consistent with 'vc-responsible-backend'.
 
+---
+*** Log Edit now font locks long Git commit summary lines.
+Writing shorter summary lines avoids truncation in contexts in which
+Git commands display summary lines.  See the two new user options
+'vc-git-log-edit-summary-target-len' and 'vc-git-log-edit-summary-max-len'.
+
+---
+*** New 'log-edit-headers-separator' face.
+It is used to style the line that separates the 'log-edit' headers
+from the 'log-edit' summary.
+
 ** Message
 
 ---
@@ -1853,11 +2113,16 @@ as opposed to via the command-line.
 *** New command 'image-transform-fit-to-window'.
 This command fits the image to the current window by scaling down or
 up as necessary.  Unlike 'image-transform-fit-both', this does not
-only scale the image down, but up as well.  It is bound to "s w" in
+only scale the image down, but up as well.  It is bound to 's w' in
 Image Mode by default.
 
+---
+*** New command 'image-mode-wallpaper-set'.
+This command sets the desktop background to the current image.  It is
+bound to 'W' by default.
+
 +++
-*** 'image-transform-fit-to-(height|width)' are now obsolete.
+*** 'image-transform-fit-to-{height,width}' are now obsolete.
 Use the new command 'image-transform-fit-to-window' instead.
 The keybinding for 'image-transform-fit-to-width' is now 's i'.
 
@@ -1878,12 +2143,21 @@ this message for SVG and XPM.
 
 +++
 *** New commands: 'image-flip-horizontally' and 'image-flip-vertically'.
-These commands horizontally and vertically flip the image under point.
+These commands horizontally and vertically flip the image under point,
+and are bound to 'i h' and 'i v', respectively.
 
 +++
 *** New command 'image-transform-set-percent'.
 It allows setting the image size to a percentage of its original size,
-and is bound to "s p" in Image mode.
+and is bound to 's p' in Image mode.
+
++++
+*** 'image-transform-original' renamed to 'image-transform-reset-to-original'.
+The old name was confusing, and is now an obsolete function alias.
+
++++
+*** 'image-transform-reset' renamed to 'image-transform-reset-to-initial'.
+The old name was confusing, and is now an obsolete function alias.
 
 ** Images
 
@@ -1894,53 +2168,54 @@ This is done via 'image-converter-add-handler'.
 ** Image-Dired
 
 +++
-*** 'image-dired-display-image-mode' is now based on 'image-mode'.
+*** 'image-dired-image-mode' is now based on 'image-mode'.
 This avoids converting images in the background, and makes Image-Dired
 noticeably faster.  New keybindings from 'image-mode' are now
 available in the "*image-dired-display-image*" buffer; press '?' or
-'h' in that buffer to see the full list.  Finally, some commands and
-user options that are no longer needed are now obsolete:
-'image-dired-cmd-create-temp-image-options',
-'image-dired-cmd-create-temp-image-program',
-'image-dired-display-current-image-full',
-'image-dired-display-current-image-sized',
-'image-dired-display-window-height-correction',
-'image-dired-display-window-width-correction',
-'image-dired-temp-image-file'.
+'h' in that buffer to see the full list.
 
 ---
 *** Navigation and marking commands now work in image display buffer.
 The following new bindings have been added:
+- 'n', 'SPC' => 'image-dired-display-next'
+- 'p', 'DEL' => 'image-dired-display-previous'
+- 'm'        => 'image-dired-mark-thumb-original-file'
+- 'd'        => 'image-dired-flag-thumb-original-file'
+- 'u'        => 'image-dired-unmark-thumb-original-file'
 
-    n or SPC  image-dired-display-next-thumbnail-original
-    p or DEL  image-dired-display-previous-thumbnail-original
-    m         image-dired-mark-thumb-original-file
-    d         image-dired-flag-thumb-original-file
-    u         image-dired-unmark-thumb-original-file
+---
+*** New command 'image-dired-unmark-all-marks'.
+It removes all marks from all files in the thumbnail and the
+associated Dired buffer, and is bound to 'U' in the thumbnail and
+display buffer.
 
 ---
-*** Reduce dependency on external "exiftool" command.
-The 'image-dired-copy-with-exif-file-name' no longer requires an
-external "exiftool" command to be available.  The user options
-'image-dired-cmd-read-exif-data-program' and
-'image-dired-cmd-read-exif-data-options' are now obsolete.
+*** New command 'image-dired-do-flagged-delete'.
+It deletes all flagged files, and is bound to 'x' in the thumbnail
+buffer.  It replaces the command 'image-dired-delete-marked', which is
+now an obsolete alias.
 
 ---
-*** New command for the thumbnail buffer.
-The new command 'image-dired-unmark-all-marks' has been added.  It is
-bound to 'U' in the thumbnail and display buffer.
+*** New command 'image-dired-copy-filename-as-kill'.
+It copies the name of the marked or current image to the kill ring,
+and is bound to 'w' in the thumbnail buffer.
 
 ---
-*** Support Thumbnail Managing Standard v0.9.0 (Dec 2020).
-This standard allows sharing generated thumbnails across different
-programs.  Version 0.9.0 adds two larger thumbnail sizes: 512x512 and
-1024x1024 pixels.  See the user option 'image-dired-thumbnail-storage'
-to use it; it is not enabled by default.
+*** New command 'image-dired-wallpaper-set'.
+This command sets the desktop background to the image at point in the
+thumbnail buffer.  It is bound to 'W' by default.
 
 ---
-*** Support GraphicsMagick command line tools.
-Support for the GraphicsMagick command line tool ("gm") has been
-added, and is used instead of ImageMagick when it is available.
+*** 'image-dired-slideshow-start' is now bound to 'S'.
+It is bound in both the thumbnail and display buffer, and no longer
+prompts for a timeout; use a numerical prefix (e.g. 'C-u 8 S') to set
+the timeout.
+
+---
+*** New user option 'image-dired-marking-shows-next'.
+If this option is non-nil (the default), marking, unmarking or
+flagging an image in either the thumbnail or display buffer shows the
+next image.
 
 ---
 *** New face 'image-dired-thumb-flagged'.
@@ -1949,8 +2224,59 @@ used for images that are flagged for deletion in the 
Dired buffer
 associated with Image-Dired.
 
 ---
-*** 'image-dired-slideshow-start' is now bound to 'S'.
-It is bound in both the thumbnail and display buffer.
+*** Image information is now shown in the header line of the thumbnail buffer.
+This replaces the message that most navigation commands in the
+thumbnail buffer used to show at the bottom of the screen.
+
+---
+*** New specifiers for 'image-dired-display-properties-format'.
+This is used to format the new header line.  The new specifiers are:
+"%d" for the name of the directory that the file is in, "%n" for
+file's number in the thumbnail buffer, and "%s" for the file size.
+
+The default format has been updated to use this.  If you prefer the
+old format, add this to your Init file:
+
+    (setopt image-dired-display-properties-format "%b: %f (%t): %c")
+
+---
+*** New faces for the header line of the thumbnail buffer.
+These faces correspond to different parts of the header line, as
+specified in 'image-dired-display-properties-format':
+- 'image-dired-thumb-header-directory-name'
+- 'image-dired-thumb-header-file-name'
+- 'image-dired-thumb-header-file-size'
+- 'image-dired-thumb-header-image-count'
+
+---
+*** PDF support.
+Image-Dired now displays thumbnails for PDF files.  Type 'RET' on a
+PDF file in the thumbnail buffer to visit the corresponding PDF.
+
+---
+*** Support GraphicsMagick command line tools.
+Support for the GraphicsMagick command line tool ("gm") has been
+added, and is used instead of ImageMagick when it is available.
+
+---
+*** Support Thumbnail Managing Standard v0.9.0 (Dec 2020).
+This standard allows sharing generated thumbnails across different
+programs.  Version 0.9.0 adds two larger thumbnail sizes: 512x512 and
+1024x1024 pixels.  See the user option 'image-dired-thumbnail-storage'
+to use it; it is not enabled by default.
+
+---
+*** Reduce dependency on external "exiftool" command.
+The 'image-dired-copy-with-exif-file-name' no longer requires an
+external "exiftool" command to be available.  The user options
+'image-dired-cmd-read-exif-data-program' and
+'image-dired-cmd-read-exif-data-options' are now obsolete.
+
+---
+*** Support for bookmark.el.
+The command 'bookmark-set' (bound to 'C-x r m') is now supported in
+the thumbnail view, and will create a bookmark that opens the current
+directory in Image-Dired.
 
 ---
 *** The 'image-dired-slideshow-start' command no longer prompts.
@@ -1960,38 +2286,64 @@ You can set the delay with a prefix argument, or a 
negative prefix
 argument to prompt for a delay.  Customize the user option
 'image-dired-slideshow-delay' to change the default from 5 seconds.
 
----
-*** Support for bookmark.el.
-The command 'bookmark-set' (bound to 'C-x r m') is now supported in
-the thumbnail view, and will create a bookmark that opens the current
-directory in Image-Dired.
++++
+*** 'image-dired-show-all-from-dir-max-files' increased to 1000.
+This user option controls asking for confirmation when starting
+Image-Dired in a directory with many files.  Since Image-Dired creates
+thumbnails in the background in recent versions, this is not as
+important as it used to be.  You can now also customize this option to
+nil to disable this confirmation completely.
 
 ---
-*** New user option 'image-dired-marking-shows-next'.
-If this option is non-nil (the default), marking, unmarking or
-flagging an image in either the thumbnail or display buffer shows the
-next image.
+*** 'image-dired-thumb-size' increased to 128.
+
++++
+*** 'image-dired-db-file' renamed to 'image-dired-tags-db-file'.
 
 ---
-*** Image information is now shown in the header line.
-This replaces the message most navigation commands in the thumbnail
-buffer used to show at the bottom of the screen.
+*** 'image-dired-display-image-mode' renamed to 'image-dired-image-mode'.
+The corresponding keymap is now named 'image-dired-image-mode-map'.
 
 +++
-*** 'image-dired-show-all-from-dir-max-files' has been increased to 500.
-This option controls asking for confirmation when starting Image-Dired
-in a directory with many files.  However, Image-Dired creates
-thumbnails in the background these days, so this is not as important
-as it used to be, back when entering a large directory could lock up
-Emacs for tens of seconds.  In addition, you can now customize this
-option to nil to disable this confirmation completely.
+*** Some commands have been renamed to be shorter.
+- 'image-dired-display-thumbnail-original-image' has been renamed to
+  'image-dired-display-this'.
+- 'image-dired-display-next-thumbnail-original' has been renamed to
+  'image-dired-display-next'.
+- 'image-dired-display-previous-thumbnail-original' has been renamed
+  to 'image-dired-display-previous'.
+The old names are now obsolete aliases.
 
 ---
-*** 'image-dired-rotate-thumbnail-(left|right)' is now obsolete.
+*** 'image-dired-thumb-{height,width}' are now obsolete.
+Customize 'image-dired-thumb-size' instead, which will set both the
+height and width.
+
+---
+*** HTML image gallery generation is now obsolete.
+The 'image-dired-gallery-generate' command and these user options are
+now obsolete: 'image-dired-gallery-thumb-image-root-url',
+'image-dired-gallery-hidden-tags', 'image-dired-gallery-dir',
+'image-dired-gallery-image-root-url'.
+
+---
+*** 'image-dired-rotate-thumbnail-{left,right}' are now obsolete.
 Instead, use commands 'image-dired-refresh-thumb' to generate a new
 thumbnail, or 'image-rotate' to rotate the thumbnail without updating
 the thumbnail file.
 
++++
+*** Some commands and user options are now obsolete.
+Since 'image-dired-display-image-mode' is now based on 'image-mode',
+some commands and user options are no longer needed and are now obsolete:
+'image-dired-cmd-create-temp-image-options',
+'image-dired-cmd-create-temp-image-program',
+'image-dired-display-current-image-full',
+'image-dired-display-current-image-sized',
+'image-dired-display-window-height-correction',
+'image-dired-display-window-width-correction',
+'image-dired-temp-image-file'.
+
 ** Dired
 
 ---
@@ -2045,9 +2397,9 @@ recently set.
 It is bound to the new command 'bookmark-edit-annotation-cancel'.
 
 ---
-*** New option 'bookmark-fringe-mark'.
+*** New user option 'bookmark-fringe-mark'.
 This option controls the bitmap used to indicate bookmarks in the
-fringe (or 'nil' to disable showing this marker).
+fringe (or nil to disable showing this marker).
 
 ** Exif
 
@@ -2097,6 +2449,11 @@ and friends.
 
 ** Tramp
 
++++
+*** New connection methods "docker", "podman" and "kubernetes".
+They allow accessing environments provided by Docker and similar
+programs.
+
 ---
 *** Tramp supports abbreviating remote home directories now.
 When calling 'abbreviate-file-name' on a Tramp file name, the result
@@ -2139,6 +2496,18 @@ the Galeon web browser was released in September, 2008.
 Note that this historical web browser is different from Mozilla
 Firefox; it is its predecessor.
 
+** Python Mode
+
++++
+*** Project shells and a new user option 'python-shell-dedicated'.
+When called with a prefix argument, 'run-python' now offers the choice
+of creating a shell dedicated to the current project.  This shell runs
+in the project root directory and is shared among all project buffers.
+
+Without a prefix argument, the kind of shell (buffer-dedicated,
+project-dedicated or global) is specified by the new
+'python-shell-dedicated' user option.
+
 ** Ruby Mode
 
 ---
@@ -2163,7 +2532,15 @@ automatically insert the Tramp prefix.  The automatic 
insertion
 applies only when 'default-directory' is remote and the command is a
 Lisp function.  This frees you from having to keep track of whether
 commands are Lisp function or external when supplying absolute file
-name arguments.  See "Electric forward slash" in the Eshell manual.
+name arguments.  See the "(eshell) Electric forward slash" node in the
+Eshell manual for details.
+
++++
+*** Improved support for redirection operators in Eshell.
+Eshell now supports a wider variety of redirection operators.  For
+example, you can now redirect both stdout and stderr via '&>' or
+duplicate one output handle to another via 'NEW-FD>&OLD-FD'.  For more
+information, see the "(eshell) Redirection" node in the Eshell manual.
 
 +++
 *** Double-quoting an Eshell expansion now treats the result as a single 
string.
@@ -2213,6 +2590,12 @@ behavior, customize the new 
'eshell-lisp-form-nil-is-failure' option.
 Enabling this will automatically kill a "*shell*" buffer as soon as
 the shell session terminates.
 
+---
+*** New minor mode 'shell-highlight-undef-mode'.
+Customize 'shell-highlight-undef-enable' to t if you want to enable
+this minor mode in "*shell*" buffers.  It will highlight undefined
+commands with a warning face as you type.
+
 ** Calc
 
 +++
@@ -2256,6 +2639,19 @@ Enabling this minor mode turns on hiding header 
material, like
 'elide-head' does; disabling it shows the header.  The commands
 'elide-head' and 'elide-head-show' are now obsolete.
 
+*** New package ansi-osc.el.
+Support for OSC ("Operating System Command") escape sequences has been
+extracted from comint.el in order to provide interpretation of OSC
+sequences in compilation buffers.
+
+Adding the new function 'ansi-osc-compilation-filter' to
+'compilation-filter-hook' enables interpretation of OSC escape
+sequences in compilation buffers.  By default, all sequences are
+filtered out.
+
+The list of handlers (already covering OSC 7 and 8) has been extended
+with a handler for OSC 2, the command to set a window title.
+
 +++
 *** New user option 'project-vc-include-untracked'.
 If non-nil, files untracked by a VCS are considered to be part of
@@ -2282,7 +2678,7 @@ instead of also trying to ping it.  Customize the user 
option
 *** The 'run-dig' command is now obsolete; use 'dig' instead.
 
 ---
-*** Some `bib-mode' commands and variables have been renamed.
+*** Some 'bib-mode' commands and variables have been renamed.
 To respect Emacs naming conventions, the variable 'unread-bib-file'
 has been renamed to 'bib-unread-file'.  The following commands have
 also been renamed:
@@ -2303,9 +2699,36 @@ remote host are shown.  Alternatively, the user option
 *** 'outlineify-sticky' command is renamed to 'allout-outlinify-sticky'.
 The old name is still available as an obsolete function alias.
 
+---
+*** New command 'world-clock-copy-time-as-kill' for 'M-x world-clock'.
+It copies the current line into the kill ring.
+
+---
+*** 'edit-abbrevs' now uses font-locking.
+The new face 'abbrev-table-name' is used to display the abbrev table
+name.
+
 
 * New Modes and Packages in Emacs 29.1
 
++++
+** New commands 'image-crop' and 'image-cut.
+These commands allow interactively cropping/cutting the image at
+point.  The commands are bound to keys 'i c' and 'i x' (respectively)
+in the local keymap over images.  They rely on external programs, by
+default "convert" from ImageMagick, to do the actual cropping/eliding
+of the image file.
+
+---
+** New package 'wallpaper'.
+This package provides the command 'wallpaper-set', which sets the
+desktop background image.  Depending on the system and the desktop,
+this may require an external program (such as "swaybg", "gm",
+"display" or "xloadimage").  If so, a suitable command should be
+detected automatically in most cases.  It can also be customized
+manually if needed, using the new user options 'wallpaper-command' and
+'wallpaper-command-args'.
+
 +++
 ** New package 'oclosure'.
 Allows the creation of "functions with slots" or "function objects"
@@ -2335,6 +2758,11 @@ when visiting JSON files.
 
 * Incompatible Lisp Changes in Emacs 29.1
 
++++
+** 'format-prompt' now uses 'substitute-command-keys'.
+This means that both the prompt and 'minibuffer-default-prompt-format'
+will have key definitions and single quotes handled specially.
+
 ---
 ** 'find-image' now uses 'create-image'.
 This means that images found through 'find-image' also have
@@ -2357,15 +2785,15 @@ in-memory format is now by using ':data-width' and 
':data-height'.
 ** "loaddefs.el" generation has been reimplemented.
 The various "loaddefs.el" files in the Emacs tree (which contain
 information about autoloads, built-in packages and package prefixes)
-used to be generated by functions in "autoloads.el".  These are now
-generated by "loaddefs-gen.el" instead.  This leads to functionally
-equivalent loaddef files, but they do not use exactly the same syntax,
-so using 'M-x update-file-autoloads' no longer works.  (This didn't
-work well in most files in the past, either, but it will now signal an
-error in any file.)
+used to be generated by functions in autoloads.el.  These are now
+generated by loaddefs-gen.el instead.  This leads to functionally
+equivalent "loaddef.el" files, but they do not use exactly the same
+syntax, so using 'M-x update-file-autoloads' no longer works.  (This
+didn't work well in most files in the past, either, but it will now
+signal an error in any file.)
 
 In addition, files are scanned in a slightly different way.
-Previously ';;;###' specs inside a top-level form (i.e., something
+Previously, ';;;###' specs inside a top-level form (i.e., something
 like '(when ... ;;;### ...)' would be ignored.  They are now parsed as
 normal.
 
@@ -2507,59 +2935,106 @@ It's been obsolete since Emacs-22.1, actually.
 negative arguments, and is generally slower than 'ash', which should be
 used instead.  This warning can be suppressed by surrounding calls to
 'lsh' with the construct '(with-suppressed-warnings ((suspicious lsh)) ...)',
-but switching to `ash` is generally much preferable.
+but switching to 'ash' is generally much preferable.
 
 ---
 ** Some functions and variables obsolete since Emacs 24 have been removed:
+'Buffer-menu-buffer+size-width', 'Electric-buffer-menu-mode',
 'Info-edit-map', 'allout-abbreviate-flattened-numbering',
-'allout-mode-deactivate-hook', 'ansi-color-unfontify-region',
-'auth-source-forget-user-or-password', 'auth-source-hide-passwords',
-'auth-source-user-or-password', 'bibtex-complete',
-'bibtex-entry-field-alist', 'buffer-substring-filters',
-'byte-compile-disable-print-circle', 'cfengine-mode-abbrevs',
-'chart-map', 'comint-dynamic-complete',
-'comint-dynamic-complete-as-filename',
-'comint-dynamic-simple-complete', 'command-history-map',
-'compilation-parse-errors-function', 'completion-annotate-function',
-'condition-case-no-debug', 'count-lines-region', 'data-debug-map',
-'deferred-action-list', 'deferred-action-function',
-'dired-x-submit-report', 'eieio-defgeneric', 'eieio-defmethod',
-'emacs-lock-from-exiting', 'erc-complete-word',
-'eshell-cmpl-suffix-list', 'eshell-for', 'font-lock-maximum-size',
+'allout-exposure-change-hook', 'allout-mode-deactivate-hook',
+'allout-structure-added-hook', 'allout-structure-deleted-hook',
+'allout-structure-shifted-hook', 'ansi-color-unfontify-region',
+'archive-extract-hooks', 'auth-source-forget-user-or-password',
+'auth-source-hide-passwords', 'auth-source-user-or-password',
+'automatic-hscrolling', 'automount-dir-prefix', 'bibtex-complete',
+'bibtex-entry-field-alist', 'buffer-has-markers-at',
+'buffer-substring-filters', 'byte-compile-disable-print-circle',
+'c-prepare-bug-report-hooks', 'cfengine-mode-abbrevs',
+'change-log-acknowledgement', 'chart-map',
+'checkdoc-comment-style-hooks', 'comint--unquote&expand-filename',
+'comint-dynamic-complete', 'comint-dynamic-complete-as-filename',
+'comint-dynamic-simple-complete', 'comint-unquote-filename',
+'command-history-map', 'compilation-parse-errors-function',
+'completion-annotate-function', 'condition-case-no-debug',
+'count-lines-region', 'crisp-mode-modeline-string',
+'custom-print-functions', 'custom-print-functions',
+'cvs-string-prefix-p', 'data-debug-map', 'deferred-action-function',
+'deferred-action-list', 'dired-pop-to-buffer', 'dired-shrink-to-fit',
+'dired-sort-set-modeline', 'dired-x-submit-report',
+'display-buffer-function',
+'ediff-choose-window-setup-function-automatically',
+'eieio-defgeneric', 'eieio-defmethod', 'emacs-lock-from-exiting',
+'erc-complete-word', 'erc-dcc-chat-filter-hook',
+'eshell-add-to-window-buffer-names', 'eshell-cmpl-suffix-list',
+'eshell-for', 'eshell-remove-from-window-buffer-names',
+'eshell-status-in-modeline', 'filesets-cache-fill-content-hooks',
+'font-list-limit', 'font-lock-maximum-size',
 'font-lock-reference-face', 'gnus-carpal',
 'gnus-debug-exclude-variables', 'gnus-debug-files',
 'gnus-local-domain', 'gnus-outgoing-message-group',
-'gnus-secondary-servers', 'gnus-registry-user-format-function-M',
+'gnus-registry-user-format-function-M', 'gnus-secondary-servers',
+'gnus-subscribe-newsgroup-hooks', 'gud-inhibit-global-bindings',
+'hangul-input-method-inactivate', 'hfy-post-html-hooks',
 'image-extension-data', 'image-library-alist',
+'inactivate-current-input-method-function', 'inactivate-input-method',
 'inhibit-first-line-modes-regexps',
-'inhibit-first-line-modes-suffixes', 'intdos',
-'mail-complete-function', 'mail-completion-at-point-function',
+'inhibit-first-line-modes-suffixes', 'input-method-inactivate-hook',
+'intdos', 'javascript-generic-mode', 'javascript-generic-mode-hook',
+'latex-string-prefix-p', 'macro-declaration-function' (function),
+'macro-declaration-function' (variable), 'mail-complete-function',
+'mail-completion-at-point-function',
 'mail-mailer-swallows-blank-line', 'mail-sent-via', 'make-register',
 'makefile-complete', 'menu-bar-kill-ring-save',
 'meta-complete-symbol', 'meta-mode-map',
+'mh-kill-folder-suppress-prompt-hooks',
 'minibuffer-completing-symbol',
 'minibuffer-local-filename-must-match-map', 'mode25', 'mode4350',
-'msb-after-load-hooks', 'nnimap-split-rule', 'nntp-authinfo-file',
-'ns-alternatives-map', 'ns-store-cut-buffer-internal',
-'package-menu-view-commentary', 'pascal-last-completions',
-'pascal-show-completions', 'pascal-toggle-completions',
-'prolog-char-quote-workaround', 'read-filename-at-point',
+'mpc-string-prefix-p', 'msb-after-load-hooks',
+'nndiary-request-accept-article-hooks',
+'nndiary-request-create-group-hooks',
+'nndiary-request-update-info-hooks', 'nnimap-split-rule',
+'nntp-authinfo-file', 'ns-alternatives-map',
+'ns-store-cut-buffer-internal', 'package-menu-view-commentary',
+'pascal-last-completions', 'pascal-show-completions',
+'pascal-toggle-completions', 'pcomplete-arg-quote-list',
+'pcomplete-quote-argument', 'prolog-char-quote-workaround',
+'python-buffer', 'python-guess-indent', 'python-indent',
+'python-info-ppss-comment-or-string-p', 'python-info-ppss-context',
+'python-info-ppss-context-type', 'python-preoutput-result',
+'python-proc', 'python-send-receive', 'python-send-string',
+'python-use-skeletons', 'quail-inactivate', 'quail-inactivate-hook',
+'query-replace-interactive', 'rcirc-activity-hooks',
+'rcirc-print-hooks', 'rcirc-receive-message-hooks',
+'rcirc-sentinel-hooks', 'read-filename-at-point', 'redraw-modeline',
 'reftex-index-map', 'reftex-index-phrases-map',
 'reftex-select-bib-map', 'reftex-select-label-map', 'reftex-toc-map',
-'register-name-alist', 'register-value',
+'register-name-alist', 'register-value', 'report-emacs-bug-info',
 'report-emacs-bug-pretest-address',
 'rmail-default-dont-reply-to-names', 'rmail-dont-reply-to',
-'rmail-dont-reply-to-names', 'rst-block-face', 'rst-comment-face',
+'rmail-dont-reply-to-names', 'robin-inactivate',
+'robin-inactivate-hook', 'rst-block-face', 'rst-comment-face',
 'rst-definition-face', 'rst-directive-face', 'rst-emphasis1-face',
 'rst-emphasis2-face', 'rst-external-face', 'rst-literal-face',
-'rst-reference-face', 'semantic-grammar-map',
-'semantic-grammar-syntax-table', 'set-register-value',
-'speedbar-key-map', 'speedbar-syntax-table',
-'starttls-any-program-available', 'strokes-report-bug',
+'rst-reference-face', 'semantic-change-hooks',
+'semantic-edits-delete-change-hooks',
+'semantic-edits-new-change-hooks',
+'semantic-edits-reparse-change-hooks', 'semantic-grammar-map',
+'semantic-grammar-syntax-table', 'semantic-lex-reset-hooks',
+'semanticdb-elisp-sym-function-arglist',
+'semanticdb-save-database-hooks', 'set-face-underline-p',
+'set-register-value', 'sh-maybe-here-document', 'speedbar-key-map',
+'speedbar-syntax-table', 'starttls-any-program-available',
+'strokes-modeline-string', 'strokes-report-bug',
+'term-default-bg-color', 'term-default-fg-color',
+'tex-string-prefix-p', 'timeclock-modeline-display',
+'timeclock-modeline-display', 'timeclock-update-modeline',
 'toggle-emacs-lock', 'tooltip-use-echo-area', 'turn-on-cwarn-mode',
-'turn-on-iimage-mode', 'vc-toggle-read-only', 'view-return-to-alist',
+'turn-on-iimage-mode', 'ucs-input-inactivate', 'ucs-insert',
+'url-recreate-url-attributes', 'user-variable-p',
+'vc-string-prefix-p', 'vc-toggle-read-only', 'view-return-to-alist',
 'view-return-to-alist-update', 'w32-default-color-map' (function),
-'which-func-mode' (function), 'x-cut-buffer-or-selection-value'.
+'which-func-mode' (function), 'window-system-version',
+'winner-mode-leave-hook', 'x-cut-buffer-or-selection-value'.
 
 ---
 ** Some functions and variables obsolete since Emacs 23 have been removed:
@@ -2597,7 +3072,7 @@ functions.
 
 ---
 ** '?\' at the end of a line now signals an error.
-Previously it produced a nonsense value, -1, that was never intended.
+Previously, it produced a nonsense value, -1, that was never intended.
 
 ---
 ** Some libraries obsolete since Emacs 24.1 and 24.3 have been removed:
@@ -2610,19 +3085,19 @@ patcomp.el, pc-mode.el, pc-select.el, s-region.el, and 
sregex.el.
 Emacs has a number of rather obscure generalized variables defined,
 that, for instance, allowed you to say things like:
 
-   (setf (point-min) 4)
+    (setf (point-min) 4)
 
 These never caught on and have been made obsolete.  The form above,
 for instance, is the same as saying
 
-   (narrow-to-region 4 (point-max))
+    (narrow-to-region 4 (point-max))
 
 The following generalized variables have been made obsolete:
 'buffer-file-name', 'buffer-local-value', 'buffer-modified-p',
 'buffer-name', 'buffer-string', 'buffer-substring', 'current-buffer',
 'current-column', 'current-global-map', 'current-input-mode',
 'current-local-map', 'current-window-configuration',
-'default-file-modes', 'documentation-property', 'frame-height',
+'default-file-modes', 'documentation-property', 'eq', 'frame-height',
 'frame-width', 'frame-visible-p', 'global-key-binding',
 'local-key-binding', 'mark', 'mark-marker', 'marker-position',
 'mouse-position', 'point', 'point-marker', 'point-max', 'point-min',
@@ -2634,6 +3109,10 @@ The following generalized variables have been made 
obsolete:
 
 * Lisp Changes in Emacs 29.1
 
++++
+** New macro 'while-let'.
+This is like 'when-let', but repeats until a binding form is nil.
+
 +++
 ** New function 'make-obsolete-generalized-variable'.
 This can be used to mark setters used by 'setf' as obsolete, and the
@@ -2679,10 +3158,6 @@ TIMEOUT is the idle time after which to deactivate the 
transient map.
 The default timeout value can be defined by the new variable
 'set-transient-map-timeout'.
 
-+++
-** New function 'seq-split'.
-This returns a list of sub-sequences of the specified sequence.
-
 +++
 ** 'plist-get', 'plist-put' and 'plist-member' are no longer limited to 'eq'.
 These function now take an optional comparison predicate argument.
@@ -2738,6 +3213,33 @@ request the name of the ".eln" file which defined a 
given symbol.
 +++
 ** New macro 'with-memoization' provides a very primitive form of memoization.
 
++++
+** 'max-char' can now report the maximum codepoint according to Unicode.
+When called with a new optional argument UNICODE non-nil, 'max-char'
+will now report the maximum valid codepoint defined by the Unicode
+Standard.
+
+** seq
+
++++
+** New function 'seq-split'.
+This returns a list of sub-sequences of the specified sequence.
+
++++
+** New function 'seq-remove-at-position'.
+This function returns a copy of the specified sequence where the
+element at a given (zero-based) index got removed.
+
++++
+** New function 'seq-positions'.
+This returns a list of the (zero-based) indices of elements matching a
+given predicate in the specified sequence.
+
++++
+** New function 'seq-keep'.
+This is like 'seq-map', but removes all non-nil results from the
+returned list.
+
 ** Themes
 
 ---
@@ -3132,6 +3634,10 @@ a valid key sequence according to 'key-valid-p'.
 ** New function 'file-name-split'.
 This returns a list of all the components of a file name.
 
++++
+** New function 'file-name-parent-directory'.
+This returns the parent directory of a file name.
+
 +++
 ** New macro 'with-undo-amalgamate'.
 It records a particular sequence of operations as a single undo step.
@@ -3241,11 +3747,27 @@ This means the vscroll will not be reset when set on a 
window that is
 ** XDG support
 
 ---
-*** New function 'xdg-state-home' returns 'XDG_STATE_HOME' environment 
variable.
-This new location, introduced in the XDG Base Directory Specification
-version 0.8 (8th May 2021), "contains state data that should persist
+*** New function 'xdg-state-home'.
+It returns the new 'XDG_STATE_HOME' environment variable.  It should
+point to a file name that "contains state data that should persist
 between (application) restarts, but that is not important or portable
 enough to the user that it should be stored in $XDG_DATA_HOME".
+(This variable was introduced in the XDG Base Directory Specification
+version 0.8 released on May 8, 2021.)
+
+---
+*** New function 'xdg-current-desktop'.
+It returns a list of strings, corresponding to the colon-separated
+list of names in the 'XDG_CURRENT_DESKTOP' environment variable, which
+identify the current desktop environment.
+(This variable was introduced in XDG Desktop Entry Specification
+version 1.2.)
+
+---
+*** New function 'xdg-session-type'.
+It returns the 'XDG_SESSION_TYPE' environment variable.  (This is not
+part of any official standard; see the man page pam_systemd(8) for
+more information.)
 
 +++
 ** New macro 'with-delayed-message'.
@@ -3318,6 +3840,11 @@ It no longer depends on edmacro.el and cl-lib.el.
 This function returns t if point is on a valid image, and nil
 otherwise.
 
++++
+** New function 'buffer-text-pixel-size'.
+This is similar to 'window-text-pixel-size', but can be used when the
+buffer isn't displayed.
+
 +++
 ** New function 'string-pixel-width'.
 This returns the width of a string in pixels.  This can be useful when
@@ -3506,6 +4033,20 @@ to preserve the old behavior, apply
 '(take N LIST)' returns the first N elements of LIST; 'ntake' does
 the same but works by modifying LIST destructively.
 
+---
+** 'string-split' is now an alias for 'split-string'.
+
++++
+** 'format-spec' now accepts functions in the replacement.
+The function is called only when used in the format string.  This is
+useful to avoid side-effects such as prompting, when the value is not
+actually being used for anything.
+
++++
+** The variable 'max-specpdl-size' has been made obsolete.
+Now 'max-lisp-eval-depth' alone is used for limiting Lisp recursion
+and stack usage.  'max-specpdl-size' is still present as a plain
+variable for compatibility but its limiting powers have been taken away.
 
 
 * Changes in Emacs 29.1 on Non-Free Operating Systems
diff --git a/etc/NEWS.22 b/etc/NEWS.22
index 926b9f489e..d7b26dda51 100644
--- a/etc/NEWS.22
+++ b/etc/NEWS.22
@@ -1413,7 +1413,8 @@ with different file attributes in two dired buffers.
 *** New Dired command 'dired-do-touch' (bound to T) changes timestamps
 of marked files with the value entered in the minibuffer.
 
-*** In Dired, the w command now stores the current line's file name
+*** New Dired command 'dired-copy-filename-as-kill' copies file name.
+In Dired, the w command now stores the current line's file name
 into the kill ring.  With a zero prefix arg, it stores the absolute file name.
 
 *** In Dired-x, Omitting files is now a minor mode, dired-omit-mode.
diff --git a/etc/NEWS.23 b/etc/NEWS.23
index ef87db79d9..5f13845dcb 100644
--- a/etc/NEWS.23
+++ b/etc/NEWS.23
@@ -2543,6 +2543,8 @@ a list of buffers/files to search for a string/regexp.
 ** The new major mode 'special-mode' is intended as a parent for
 major modes such as those that set the "'mode-class 'special" property.
 
+** New package format-spec.el provides 'format-spec'.
+
 
 ----------------------------------------------------------------------
 This file is part of GNU Emacs.
diff --git a/etc/NEWS.28 b/etc/NEWS.28
index 01e8ac112f..1edf4e85b0 100644
--- a/etc/NEWS.28
+++ b/etc/NEWS.28
@@ -16,6 +16,41 @@ You can narrow news to a specific version by calling 
'view-emacs-news'
 with a prefix argument or by typing 'C-u C-h C-n'.
 
 
+* Installation Changes in Emacs 28.3
+
+
+* Startup Changes in Emacs 28.3
+
+
+* Changes in Emacs 28.3
+
+
+* Editing Changes in Emacs 28.3
+
+
+* Changes in Specialized Modes and Packages in Emacs 28.3
+
+** 'native-comp-driver-options' on macOS.
+The value of 'native-comp-driver-options' has been changed to contain
+"-Wl,-w" to suppress warnings of the form
+
+    ld: warning: -undefined dynamic_lookup may not work with chained fixups
+
+emitted during native compilation on macOS 12.6 with Xcode 14.
+
+
+* New Modes and Packages in Emacs 28.3
+
+
+* Incompatible Lisp Changes in Emacs 28.3
+
+
+* Lisp Changes in Emacs 28.3
+
+
+* Changes in Emacs 28.3 on Non-Free Operating Systems
+
+
 * Installation Changes in Emacs 28.2
 
 ** To install the Emacs binary in a non-standard directory, use '--bindir='.
@@ -37,17 +72,11 @@ preloaded Lisp packages, and the relative name of that 
directory needs
 therefore to be recorded in the executable as part of the build.
 
 
-* Startup Changes in Emacs 28.2
-
-
 * Changes in Emacs 28.2
 
 This is a bug-fix release with no new features.
 
 
-* Editing Changes in Emacs 28.2
-
-
 * Changes in Specialized Modes and Packages in Emacs 28.2
 
 ** The command 'kdb-macro-redisplay' was renamed to 'kmacro-redisplay'.
@@ -66,18 +95,6 @@ terminate Emacs with 'save-buffers-kill-emacs', not with 
'kill-emacs'.
 back then.)
 
 
-* New Modes and Packages in Emacs 28.2
-
-
-* Incompatible Lisp Changes in Emacs 28.2
-
-
-* Lisp Changes in Emacs 28.2
-
-
-* Changes in Emacs 28.2 on Non-Free Operating Systems
-
-
 * Installation Changes in Emacs 28.1
 
 ** Emacs now optionally supports native compilation of Lisp files.
@@ -377,7 +394,7 @@ the current buffer and the text "GNU Emacs" instead of the 
value of
 your init file:
 
     (setq frame-title-format '(multiple-frames "%b"
-                              ("" invocation-name "@" system-name)))
+                               ("" invocation-name "@" system-name)))
 
 *** New frame parameter 'drag-with-tab-line'.
 This parameter, similar to 'drag-with-header-line', allows moving frames
@@ -1310,18 +1327,18 @@ comma-separated list.
 *** New commands to filter the package list.
 The filter commands are bound to the following keys:
 
-key             binding
----             -------
-/ a             package-menu-filter-by-archive
-/ d             package-menu-filter-by-description
-/ k             package-menu-filter-by-keyword
-/ N             package-menu-filter-by-name-or-description
-/ n             package-menu-filter-by-name
-/ s             package-menu-filter-by-status
-/ v             package-menu-filter-by-version
-/ m             package-menu-filter-marked
-/ u             package-menu-filter-upgradable
-/ /             package-menu-clear-filter
+    key             binding
+    ---             -------
+    / a             package-menu-filter-by-archive
+    / d             package-menu-filter-by-description
+    / k             package-menu-filter-by-keyword
+    / N             package-menu-filter-by-name-or-description
+    / n             package-menu-filter-by-name
+    / s             package-menu-filter-by-status
+    / v             package-menu-filter-by-version
+    / m             package-menu-filter-marked
+    / u             package-menu-filter-upgradable
+    / /             package-menu-clear-filter
 
 *** Option to automatically native-compile packages upon installation.
 Customize the user option 'package-native-compile' to enable automatic
@@ -2614,7 +2631,7 @@ non-nil.
 
 ** ERC
 
-*** Starting with Emacs 28.1 and ERC 5.4, see the ERC-NEWS file for
+Starting with Emacs 28.1 and ERC 5.4, see the ERC-NEWS file for
 user-visible changes in ERC.
 
 ** Xwidget Webkit mode
@@ -3275,12 +3292,13 @@ completing on commands from buffers in major modes 
derived from
 MODE..., or, if it's a minor mode, when that minor mode is enabled in
 the current buffer.
 
-Note that these forms will only have their effect if the
+Note that these forms will only have their effect for 'M-x' if the
 'read-extended-command-predicate' user option is customized to call
 'command-completion-default-include-p' or a similar function.  The
 default value of 'read-extended-command-predicate' is nil, which means
 no commands that match what you have typed are excluded from being
-completion candidates.
+completion candidates.  The forms will, however, be used by 'M-S-x' by
+default.
 
 ** 'define-minor-mode' now takes an ':interactive' argument.
 This can be used for specifying which modes this minor mode is meant
@@ -3605,6 +3623,13 @@ pairs.
 ** New function 'mail-header-parse-address-lax'.
 Parse a string as a mail address-like string.
 
+** New function 'make-closure'.
+This function is used internally by the byte-compiler: calls to it are
+inserted into the generated bytecode to handle closures more
+efficiently than the old code which relied on
+'make-byte-code' instead.
+It also makes the disassembly more readable.
+
 ** New function 'make-separator-line'.
 Make a string appropriate for usage as a visual separator line.
 
@@ -3941,7 +3966,6 @@ and enable the MS-Windows native Input Method Editor 
(IME) at run
 time.  A companion function 'w32-get-ime-open-status' returns the
 current IME activation status.
 
---
 ** On macOS, 's-<left>' and 's-<right>' are now bound to
 'move-beginning-of-line' and 'move-end-of-line' respectively.  The commands
 to select previous/next frame are still bound to 's-~' and 's-`'.
diff --git a/etc/PROBLEMS b/etc/PROBLEMS
index 6624f747c8..ed2bc1ae05 100644
--- a/etc/PROBLEMS
+++ b/etc/PROBLEMS
@@ -2267,6 +2267,13 @@ term/xterm.el) for more details.
 
 *** Linux console problems with double-width characters
 
+If possible, we recommend running Emacs inside fbterm, when in a Linux
+console (see the node "Emacs in a Linux console" in the Emacs FAQ).
+Most Unicode characters should then be displayed correctly.
+
+If that is not possible, the following may be useful to alleviate the
+problem of displaying Unicode characters in a raw console.
+
 The Linux console declares UTF-8 encoding, but supports only a limited
 number of Unicode characters, and can cause Emacs produce corrupted or
 garbled display with some unusual characters and sequences.  Emacs 28
diff --git a/etc/TODO b/etc/TODO
index 772fbf7191..d884539037 100644
--- a/etc/TODO
+++ b/etc/TODO
@@ -359,6 +359,17 @@ should invoke the 'shape' method.  'hbfont_shape' should 
be extended
 to pass to 'hb_shape_full' the required array of features, as
 mentioned in the above HarfBuzz discussion.
 
+Alternatively, stylistic sets could be a font property, or a face
+property.  See this discussion:
+
+  https://lists.gnu.org/archive/html/emacs-devel/2022-09/msg01597.html
+
+For some relevant use cases, see
+
+  https://lists.gnu.org/archive/html/emacs-devel/2022-09/msg01652.html
+  https://lists.gnu.org/archive/html/emacs-devel/2022-09/msg01701.html
+  https://lists.gnu.org/archive/html/emacs-devel/2022-09/msg01772.html
+
 ** Concurrency
 Stefan Monnier writes: "Including it as an 'experimental' compile-time
 option sounds good.  Of course there might still be big questions
@@ -1481,8 +1492,8 @@ Markers are implemented as a non-sorted singly linked 
list of markers.
 This makes them scale badly when thousands of markers are created in a
 buffer for some purpose, because some low-level primitives in Emacs
 traverse the markers' list (e.g., when converting between character
-and byte positions), and also because searching for a marker (e.g.,
-with 'buffer-has-markers-at') becomes very slow.
+and byte positions), and also because searching for a marker becomes
+very slow.
 
 **** Explore whether overlay-recenter can cure overlays performance problems
 
@@ -1686,12 +1697,6 @@ and the one to use when terminating the selection.
 More specifically do what's needed to make ibuffer.el the default, or
 just an extension of buff-menu.el.
 
-** Replace linum.el with nlinum.el
-https://lists.gnu.org/r/emacs-devel/2013-08/msg00379.html
-
-(Since Emacs 26 introduced native line numbers, this item is
-probably obsolete.)
-
 ** Merge sendmail.el and messages.el
 Probably not a complete merge, but at least arrange for messages.el to
 be a derived mode of sendmail.el.  Or arrange for messages.el to be
@@ -1732,7 +1737,11 @@ https://lists.gnu.org/r/emacs-devel/2012-06/msg00354.html
 ** Maybe replace lib-src/rcs2log with a Lisp implementation
 It wouldn't have to be a complete replacement, just enough
 for vc-rcs-update-changelog.
-
+** Allow Emacs to use the bottom-right corner of a TTY
+Emacs doesn't use the bottom-right corner of a TTY when terminfo
+capability "am" (auto_right_margin) is defined.  It could use the
+bottom-right corner nonetheless when certain other capabilities are
+defined.  See bug#57607.
 * Other known bugs
 
 ** 'make-frame' forgets unhandled parameters, at least for X11 frames
@@ -1754,6 +1763,26 @@ enough environment under which the fix can be tested.
 The MPX code has not been tested under X toolkit or GTK+ 2.x builds
 and is not expected to work there.
 
+** Framework for doing animations
+Emacs does animations all over the place, usually "pluse" animations.
+These currently animate by waiting for a small but fixed amount of
+time between each redisplay, which causes screen tearing by not
+synchronizing with the vertical refresh.  Frame synchronization works
+by causing redisplay to delay until the next time the monitor can
+refresh; this works, but can cause substandard frame rate when
+redisplay happens less often than the monitor refreshing, as redisplay
+will have to continually wait for missed monitor refresh.
+
+The right remedy for this problem is to define a function that returns
+the amount of time remaining before the next vertical blanking period,
+and to schedule animation redisplay within that period, using the
+information provided by the _NET_WM_FRAME_DRAWN and
+_NET_WM_FRAME_TIMINGS compositor messages on X and the
+BScreen::GetMonitorInfo function on Haiku.  Ideally, all features
+performing animations should be modified to use that method of
+scheduling redisplay.  Examples include xref-pulse-momentarily and
+pixel-scroll-precision-start-momentum.
+
 
 This file is part of GNU Emacs.
 
diff --git a/etc/images/checked.xpm b/etc/images/checked.xpm
index aefa9dd5da..4dbcb9e6fd 100644
--- a/etc/images/checked.xpm
+++ b/etc/images/checked.xpm
@@ -1,23 +1,4 @@
 /* XPM */
-/* Copyright (C) 2010-2022 Free Software Foundation, Inc.
- *
- * Author: Chong Yidong <cyd@stupidchicken.com>
- *
- * This file is part of GNU Emacs.
- *
- * GNU Emacs is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * GNU Emacs is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.
- */
 static char * checked_xpm[] = {
 "12 12 5 1",
 "      c None",
diff --git a/etc/images/gnus/gnus-pointer.xpm b/etc/images/gnus/gnus-pointer.xpm
index c47443dbb7..5328b15bed 100644
--- a/etc/images/gnus/gnus-pointer.xpm
+++ b/etc/images/gnus/gnus-pointer.xpm
@@ -1,7 +1,7 @@
 /* XPM */
-static char *gnus-pointer[] = {
+static char *gnus_pointer_xpm[] = {
 /* width height num_colors chars_per_pixel */
-"    18    13        2            1",
+"18 13 2 1",
 /* colors */
 ". c #0000ff",
 "# c None s None",
@@ -19,4 +19,4 @@ static char *gnus-pointer[] = {
 "###....####.######",
 "###..######.######",
 "###########.######"
-};
\ No newline at end of file
+};
diff --git a/etc/images/gnus/gnus.xpm b/etc/images/gnus/gnus.xpm
index b6ee4d0d73..4f46eca4fa 100644
--- a/etc/images/gnus/gnus.xpm
+++ b/etc/images/gnus/gnus.xpm
@@ -1,7 +1,7 @@
 /* XPM */
-static char *gnus[] = {
+static char *gnus_xpm[] = {
 /* width height num_colors chars_per_pixel */
-"   271   273        3            1",
+"271 273 3 1",
 /* colors */
 ". s thing c #bf9900",
 "# s shadow c #ffcc00",
diff --git a/etc/images/mh-logo.xpm b/etc/images/mh-logo.xpm
index 846859e058..637c456964 100644
--- a/etc/images/mh-logo.xpm
+++ b/etc/images/mh-logo.xpm
@@ -1,32 +1,8 @@
 /* XPM */
-/* MH-E Logo
- *
- * Copyright (C) 2003-2022 Free Software Foundation, Inc.
- *
- * Author: Satyaki Das
- *
- * This file is part of GNU Emacs.
- *
- * GNU Emacs is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * GNU Emacs is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.
- */
-static char *mh-e[] = {
-/* width height num_colors chars_per_pixel */
-"    18    13        2            1",
-/* colors */
+static char *mh_logo_xpm[] = {
+"18 13 2 1",
 "# c #666699",
 ". c None s None",
-/* pixels */
 "........##........",
 ".......####.......",
 "......######......",
diff --git a/etc/images/outline-close.pbm b/etc/images/outline-close.pbm
new file mode 100644
index 0000000000..b37b640b55
Binary files /dev/null and b/etc/images/outline-close.pbm differ
diff --git a/etc/images/outline-close.svg b/etc/images/outline-close.svg
new file mode 100644
index 0000000000..ea9157a5fb
--- /dev/null
+++ b/etc/images/outline-close.svg
@@ -0,0 +1,6 @@
+<svg xmlns="http://www.w3.org/2000/svg"; width="20" height="20" viewBox="0 0 20 
20">
+<title>outline-close</title>
+<g transform="rotate(-90, 10, 10)">
+<path d="m17.5 4.75-7.5 7.5-7.5-7.5L1 6.25l9 9 9-9z"/>
+</g>
+</svg>
diff --git a/etc/images/outline-open.pbm b/etc/images/outline-open.pbm
new file mode 100644
index 0000000000..06b520f14c
Binary files /dev/null and b/etc/images/outline-open.pbm differ
diff --git a/etc/images/outline-open.svg b/etc/images/outline-open.svg
new file mode 100644
index 0000000000..75cf6aff9f
--- /dev/null
+++ b/etc/images/outline-open.svg
@@ -0,0 +1,4 @@
+<svg xmlns="http://www.w3.org/2000/svg"; width="20" height="20" viewBox="0 0 20 
20">
+<title>outline-open</title>
+<path d="m17.5 4.75-7.5 7.5-7.5-7.5L1 6.25l9 9 9-9z"/>
+</svg>
diff --git a/etc/images/unchecked.xpm b/etc/images/unchecked.xpm
index b758346b96..85eec75230 100644
--- a/etc/images/unchecked.xpm
+++ b/etc/images/unchecked.xpm
@@ -1,23 +1,4 @@
 /* XPM */
-/* Copyright (C) 2010-2022 Free Software Foundation, Inc.
- *
- * Author: Chong Yidong <cyd@stupidchicken.com>
- *
- * This file is part of GNU Emacs.
- *
- * GNU Emacs is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * GNU Emacs is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.
- */
 static char * unchecked_xpm[] = {
 "12 12 5 1",
 "      c None",
diff --git a/etc/publicsuffix.txt b/etc/publicsuffix.txt
index b5a89c65cc..5676a31c3a 100644
--- a/etc/publicsuffix.txt
+++ b/etc/publicsuffix.txt
@@ -1315,7 +1315,9 @@ web.id
 ie
 gov.ie
 
-// il : http://www.isoc.org.il/domains/
+// il :         http://www.isoc.org.il/domains/
+// see also:    https://en.isoc.org.il/il-cctld/registration-rules
+// ISOC-IL      (operated by .il Registry)
 il
 ac.il
 co.il
@@ -1325,6 +1327,16 @@ k12.il
 muni.il
 net.il
 org.il
+// xn--4dbrk0ce ("Israel", Hebrew) : IL
+ישראל
+// xn--4dbgdty6c.xn--4dbrk0ce.
+אקדמיה.ישראל
+// xn--5dbhl8d.xn--4dbrk0ce.
+ישוב.ישראל
+// xn--8dbq2a.xn--4dbrk0ce.
+צהל.ישראל
+// xn--hebda8b.xn--4dbrk0ce.
+ממשל.ישראל
 
 // im : https://www.nic.im/
 // Submitted by registry <info@nic.im>
@@ -1344,18 +1356,47 @@ tv.im
 // Please note, that nic.in is not an official eTLD, but used by most
 // government institutions.
 in
+5g.in
+6g.in
+ac.in
+ai.in
+am.in
+bihar.in
+biz.in
+business.in
+ca.in
+cn.in
 co.in
+com.in
+coop.in
+cs.in
+delhi.in
+dr.in
+edu.in
+er.in
 firm.in
-net.in
-org.in
 gen.in
+gov.in
+gujarat.in
 ind.in
+info.in
+int.in
+internet.in
+io.in
+me.in
+mil.in
+net.in
 nic.in
-ac.in
-edu.in
+org.in
+pg.in
+post.in
+pro.in
 res.in
-gov.in
-mil.in
+travel.in
+tv.in
+uk.in
+up.in
+us.in
 
 // info : https://en.wikipedia.org/wiki/.info
 info
@@ -7130,7 +7171,7 @@ org.zw
 
 // newGTLDs
 
-// List of new gTLDs imported from 
https://www.icann.org/resources/registries/gtlds/v2/gtlds.json on 
2022-07-28T15:14:54Z
+// List of new gTLDs imported from 
https://www.icann.org/resources/registries/gtlds/v2/gtlds.json on 
2022-09-15T15:17:34Z
 // This list is auto-generated, don't edit it manually.
 // aaa : 2015-02-26 American Automobile Association, Inc.
 aaa
@@ -7639,7 +7680,7 @@ cars
 // casa : 2013-11-21 Registry Services, LLC
 casa
 
-// case : 2015-09-03 Helium TLDs Ltd
+// case : 2015-09-03 Digity, LLC
 case
 
 // cash : 2014-03-06 Binky Moon, LLC
@@ -7825,7 +7866,7 @@ cool
 // corsica : 2014-09-25 Collectivité de Corse
 corsica
 
-// country : 2013-12-19 DotCountry LLC
+// country : 2013-12-19 Internet Naming Company LLC
 country
 
 // coupon : 2015-02-26 Amazon Registry Services, Inc.
@@ -8329,7 +8370,7 @@ gifts
 // gives : 2014-03-06 Public Interest Registry
 gives
 
-// giving : 2014-11-13 Giving Limited
+// giving : 2014-11-13 Public Interest Registry
 giving
 
 // glass : 2013-11-07 Binky Moon, LLC
@@ -10763,6 +10804,52 @@ s3-website.eu-west-2.amazonaws.com
 s3-website.eu-west-3.amazonaws.com
 s3-website.us-east-2.amazonaws.com
 
+// AWS Cloud9 : https://aws.amazon.com/cloud9/
+// Submitted by: AWS Security <psl-maintainers@amazon.com>
+// Reference: 2b6dfa9a-3a7f-4367-b2e7-0321e77c0d59
+vfs.cloud9.af-south-1.amazonaws.com
+webview-assets.cloud9.af-south-1.amazonaws.com
+vfs.cloud9.ap-east-1.amazonaws.com
+webview-assets.cloud9.ap-east-1.amazonaws.com
+vfs.cloud9.ap-northeast-1.amazonaws.com
+webview-assets.cloud9.ap-northeast-1.amazonaws.com
+vfs.cloud9.ap-northeast-2.amazonaws.com
+webview-assets.cloud9.ap-northeast-2.amazonaws.com
+vfs.cloud9.ap-northeast-3.amazonaws.com
+webview-assets.cloud9.ap-northeast-3.amazonaws.com
+vfs.cloud9.ap-south-1.amazonaws.com
+webview-assets.cloud9.ap-south-1.amazonaws.com
+vfs.cloud9.ap-southeast-1.amazonaws.com
+webview-assets.cloud9.ap-southeast-1.amazonaws.com
+vfs.cloud9.ap-southeast-2.amazonaws.com
+webview-assets.cloud9.ap-southeast-2.amazonaws.com
+vfs.cloud9.ca-central-1.amazonaws.com
+webview-assets.cloud9.ca-central-1.amazonaws.com
+vfs.cloud9.eu-central-1.amazonaws.com
+webview-assets.cloud9.eu-central-1.amazonaws.com
+vfs.cloud9.eu-north-1.amazonaws.com
+webview-assets.cloud9.eu-north-1.amazonaws.com
+vfs.cloud9.eu-south-1.amazonaws.com
+webview-assets.cloud9.eu-south-1.amazonaws.com
+vfs.cloud9.eu-west-1.amazonaws.com
+webview-assets.cloud9.eu-west-1.amazonaws.com
+vfs.cloud9.eu-west-2.amazonaws.com
+webview-assets.cloud9.eu-west-2.amazonaws.com
+vfs.cloud9.eu-west-3.amazonaws.com
+webview-assets.cloud9.eu-west-3.amazonaws.com
+vfs.cloud9.me-south-1.amazonaws.com
+webview-assets.cloud9.me-south-1.amazonaws.com
+vfs.cloud9.sa-east-1.amazonaws.com
+webview-assets.cloud9.sa-east-1.amazonaws.com
+vfs.cloud9.us-east-1.amazonaws.com
+webview-assets.cloud9.us-east-1.amazonaws.com
+vfs.cloud9.us-east-2.amazonaws.com
+webview-assets.cloud9.us-east-2.amazonaws.com
+vfs.cloud9.us-west-1.amazonaws.com
+webview-assets.cloud9.us-west-1.amazonaws.com
+vfs.cloud9.us-west-2.amazonaws.com
+webview-assets.cloud9.us-west-2.amazonaws.com
+
 // Amune : https://amune.org/
 // Submitted by Team Amune <cert@amune.org>
 t3l3p0rt.net
@@ -10871,6 +10958,10 @@ theshop.jp
 shopselect.net
 base.shop
 
+// BeagleBoard.org Foundation : https://beagleboard.org
+// Submitted by Jason Kridner <jkridner@beagleboard.org>
+beagleboard.io
+
 // Beget Ltd
 // Submitted by Lev Nekrasov <lnekrasov@beget.com>
 *.beget.app
@@ -11649,6 +11740,11 @@ dynv6.net
 // Submitted by Vladimir Dudr <info@e4you.cz>
 e4.cz
 
+// Easypanel : https://easypanel.io
+// Submitted by Andrei Canta <andrei@easypanel.io>
+easypanel.app
+easypanel.host
+
 // eero : https://eero.com/
 // Submitted by Yue Kang <eero-dynamic-dns@amazon.com>
 eero.online
@@ -11943,6 +12039,10 @@ id.forgerock.io
 // Submitted by Koen Rouwhorst <koenrh@framer.com>
 framer.app
 framercanvas.com
+framer.media
+framer.photos
+framer.website
+framer.wiki
 
 // Frusky MEDIA&PR : https://www.frusky.de
 // Submitted by Victor Pupynin <hallo@frusky.de>
@@ -12820,6 +12920,7 @@ azure-mobile.net
 cloudapp.net
 azurestaticapps.net
 1.azurestaticapps.net
+2.azurestaticapps.net
 centralus.azurestaticapps.net
 eastasia.azurestaticapps.net
 eastus2.azurestaticapps.net
@@ -13554,6 +13655,10 @@ small-web.org
 // Submitted by Dan Kozak <dan@smoove.io>
 vp4.me
 
+// Snowflake Inc : https://www.snowflake.com/
+// Submitted by Faith Olapade <faith.olapade@snowflake.com>
+streamlitapp.com
+
 // Snowplow Analytics : https://snowplowanalytics.com/
 // Submitted by Ian Streeter <ian@snowplowanalytics.com>
 try-snowplow.com
diff --git a/etc/refcards/orgcard.tex b/etc/refcards/orgcard.tex
index bb4bc5b25d..9462df8574 100644
--- a/etc/refcards/orgcard.tex
+++ b/etc/refcards/orgcard.tex
@@ -1,5 +1,5 @@
 % Reference Card for Org Mode
-\def\orgversionnumber{9.5.4}
+\def\orgversionnumber{9.5.5}
 \def\versionyear{2021}          % latest update
 \input emacsver.tex
 
diff --git a/etc/themes/modus-operandi-theme.el 
b/etc/themes/modus-operandi-theme.el
index fd7ffff98f..6e609c0803 100644
--- a/etc/themes/modus-operandi-theme.el
+++ b/etc/themes/modus-operandi-theme.el
@@ -6,7 +6,7 @@
 ;; Maintainer: Modus-Themes Development <~protesilaos/modus-themes@lists.sr.ht>
 ;; URL: https://git.sr.ht/~protesilaos/modus-themes
 ;; Mailing-List: https://lists.sr.ht/~protesilaos/modus-themes
-;; Version: 2.6.0
+;; Version: 2.7.1
 ;; Package-Requires: ((emacs "27.1"))
 ;; Keywords: faces, theme, accessibility
 
diff --git a/etc/themes/modus-themes.el b/etc/themes/modus-themes.el
index c4edb1efcb..d5e1b0a120 100644
--- a/etc/themes/modus-themes.el
+++ b/etc/themes/modus-themes.el
@@ -6,7 +6,7 @@
 ;; Maintainer: Modus-Themes Development <~protesilaos/modus-themes@lists.sr.ht>
 ;; URL: https://git.sr.ht/~protesilaos/modus-themes
 ;; Mailing-List: https://lists.sr.ht/~protesilaos/modus-themes
-;; Version: 2.6.0
+;; Version: 2.7.1
 ;; Package-Requires: ((emacs "27.1"))
 ;; Keywords: faces, theme, accessibility
 
@@ -109,7 +109,7 @@ cover the blue-cyan-magenta side of the spectrum."
   :prefix "modus-themes-"
   :tag "Modus Themes Faces")
 
-(defvar modus-themes--version "2.6.0"
+(defvar modus-themes--version "2.7.0"
   "Current version of the Modus themes.
 
 The version either is the last tagged release, such as '1.0.0',
@@ -634,7 +634,7 @@ symbol and the latter as a string.")
     (bg-diff-focus-added . "#1d3c25") (fg-diff-focus-added . "#b4ddb4")
     (bg-diff-focus-added-deuteran . "#003959") (fg-diff-focus-added-deuteran . 
"#bfe4ff")
     (bg-diff-focus-changed . "#424200") (fg-diff-focus-changed . "#d0daaf")
-    (bg-diff-focus-removed . "#500f29") (fg-diff-focus-removed . "#eebdba")
+    (bg-diff-focus-removed . "#601f29") (fg-diff-focus-removed . "#eebdba")
 
     (bg-mark-sel . "#002f2f") (fg-mark-sel . "#60cfa2")
     (bg-mark-del . "#5a0000") (fg-mark-del . "#ff99aa")
@@ -1435,7 +1435,7 @@ By default, customizing a theme-related user option 
through the
 Custom interfaces or with `customize-set-variable' will not
 reload the currently active Modus theme.
 
-Enable this behaviour by setting this variable to nil."
+Enable this behavior by setting this variable to nil."
   :group 'modus-themes
   :package-version '(modus-themes . "1.5.0")
   :version "28.1"
@@ -2270,7 +2270,7 @@ follows (order is not significant):
 
 The `popup' key takes the same values as `selection'.
 
-Apart from specfying each key separately, a fallback list is
+Apart from specifying each key separately, a fallback list is
 accepted.  This is only useful when the desired aesthetic is the
 same across all keys that are not explicitly referenced.  For
 example, this:
@@ -4387,6 +4387,7 @@ by virtue of calling either of 
`modus-themes-load-operandi' and
     `(help-key-binding ((,class :inherit modus-themes-key-binding)))
     `(homoglyph ((,class :foreground ,red-alt-faint)))
     `(ibuffer-locked-buffer ((,class :foreground ,yellow-alt-other-faint)))
+    `(icon-button ((,class :inherit modus-themes-box-button)))
     `(italic ((,class :slant italic)))
     `(nobreak-hyphen ((,class :foreground ,fg-escape-char-construct)))
     `(nobreak-space ((,class :foreground ,fg-escape-char-construct :underline 
t)))
@@ -4396,6 +4397,7 @@ by virtue of calling either of 
`modus-themes-load-operandi' and
     `(mm-uu-extract ((,class :background ,bg-dim :foreground 
,fg-special-mild)))
     `(next-error ((,class :inherit modus-themes-subtle-red :extend t)))
     `(pgtk-im-0 ((,class :inherit modus-themes-refine-cyan)))
+    `(read-multiple-choice-face ((,class :inherit (bold 
modus-themes-mark-alt))))
     `(rectangle-preview ((,class :inherit modus-themes-special-warm)))
     `(region ((,class ,@(modus-themes--region bg-region fg-main
                                               bg-hl-alt-intense 
bg-region-accent
@@ -4532,9 +4534,9 @@ by virtue of calling either of 
`modus-themes-load-operandi' and
     `(font-latex-string-face ((,class :inherit font-lock-string-face)))
     `(font-latex-subscript-face ((,class :height 0.95)))
     `(font-latex-superscript-face ((,class :height 0.95)))
+    `(font-latex-underline-face ((,class :inherit underline)))
     `(font-latex-verbatim-face ((,class :inherit 
modus-themes-markup-verbatim)))
     `(font-latex-warning-face ((,class :inherit font-lock-warning-face)))
-    `(tex-match ((,class :foreground ,blue-alt-other)))
     `(tex-verbatim ((,class :inherit modus-themes-markup-verbatim)))
     `(texinfo-heading ((,class :foreground ,magenta)))
     `(TeX-error-description-error ((,class :inherit error)))
@@ -4657,6 +4659,18 @@ by virtue of calling either of 
`modus-themes-load-operandi' and
     `(calibredb-mark-face ((,class :inherit modus-themes-mark-sel)))
     `(calibredb-size-face (( )))
     `(calibredb-tag-face ((,class :foreground ,magenta-alt-faint)))
+;;;;; centaur-tabs
+    `(centaur-tabs-active-bar-face ((,class :background ,blue-active)))
+    `(centaur-tabs-close-mouse-face ((,class :inherit bold :foreground 
,red-active :underline t)))
+    `(centaur-tabs-close-selected ((,class :inherit centaur-tabs-selected)))
+    `(centaur-tabs-close-unselected ((,class :inherit 
centaur-tabs-unselected)))
+    `(centaur-tabs-modified-marker-selected ((,class :inherit 
centaur-tabs-selected)))
+    `(centaur-tabs-modified-marker-unselected ((,class :inherit 
centaur-tabs-unselected)))
+    `(centaur-tabs-default ((,class :background ,bg-main)))
+    `(centaur-tabs-selected ((,class :inherit modus-themes-tab-active)))
+    `(centaur-tabs-selected-modified ((,class :inherit (italic 
centaur-tabs-selected))))
+    `(centaur-tabs-unselected ((,class :inherit modus-themes-tab-inactive)))
+    `(centaur-tabs-unselected-modified ((,class :inherit (italic 
centaur-tabs-unselected))))
 ;;;;; cfrs
     `(cfrs-border-color ((,class :background ,fg-window-divider-inner)))
 ;;;;; change-log and log-view (`vc-print-log' and `vc-print-root-log')
@@ -4669,6 +4683,7 @@ by virtue of calling either of 
`modus-themes-load-operandi' and
     `(change-log-list ((,class :foreground ,magenta-alt)))
     `(change-log-name ((,class :foreground ,magenta-alt-other)))
     `(log-edit-header ((,class :foreground ,fg-special-warm)))
+    `(log-edit-headers-separator ((,class :height 1 :background 
,fg-window-divider-inner :extend t)))
     `(log-edit-summary ((,class :inherit bold :foreground ,blue)))
     `(log-edit-unknown-header ((,class :inherit shadow)))
     `(log-view-commit-body ((,class :foreground ,blue-nuanced-fg)))
@@ -4743,6 +4758,8 @@ by virtue of calling either of 
`modus-themes-load-operandi' and
     `(company-preview-common ((,class :inherit company-echo-common)))
     `(company-preview-search ((,class :inherit modus-themes-special-calm)))
     `(company-template-field ((,class :inherit modus-themes-intense-magenta)))
+    `(company-scrollbar-bg ((,class :background ,bg-active)))
+    `(company-scrollbar-fg ((,class :background ,fg-active)))
     `(company-tooltip ((,class :background ,bg-alt)))
     `(company-tooltip-annotation ((,class :inherit completions-annotations)))
     `(company-tooltip-common ((,class :inherit company-echo-common)))
@@ -4824,6 +4841,13 @@ by virtue of calling either of 
`modus-themes-load-operandi' and
     `(cperl-nonoverridable-face ((,class :foreground unspecified)))
     `(cperl-array-face ((,class :inherit font-lock-keyword-face)))
     `(cperl-hash-face ((,class :inherit font-lock-variable-name-face)))
+;;;;; crontab-mode
+    `(crontab-minute ((,class :foreground ,blue-alt)))
+    `(crontab-hour ((,class :foreground ,magenta-alt-other)))
+    `(crontab-month-day ((,class :foreground ,magenta-alt)))
+    `(crontab-month ((,class :foreground ,blue)))
+    `(crontab-week-day ((,class :foreground ,cyan)))
+    `(crontab-predefined ((,class :foreground ,blue-alt)))
 ;;;;; css-mode
     `(css-property ((,class :inherit font-lock-type-face)))
     `(css-selector ((,class :inherit font-lock-keyword-face)))
@@ -5041,7 +5065,7 @@ by virtue of calling either of 
`modus-themes-load-operandi' and
     `(diredp-tagged-autofile-name ((,class :inherit 
modus-themes-refine-magenta)))
     `(diredp-write-priv ((,class :foreground ,cyan)))
 ;;;;; display-fill-column-indicator-mode
-    `(fill-column-indicator ((,class :height 1 :background ,bg-inactive 
:foreground ,bg-inactive)))
+    `(fill-column-indicator ((,class :height 1 :background ,bg-region 
:foreground ,bg-region)))
 ;;;;; doom-modeline
     `(doom-modeline-bar ((,class :inherit modus-themes-active-blue)))
     `(doom-modeline-bar-inactive ((,class :background ,fg-inactive :foreground 
,bg-main)))
@@ -5608,7 +5632,7 @@ by virtue of calling either of 
`modus-themes-load-operandi' and
     `(gnus-summary-low-read ((,class :inherit italic :foreground ,fg-alt)))
     `(gnus-summary-low-ticked ((,class :inherit italic :foreground 
,red-refine-fg)))
     `(gnus-summary-low-undownloaded ((,class :inherit italic :foreground 
,yellow-refine-fg)))
-    `(gnus-summary-low-unread ((,class :inherit bold :foreground 
,fg-special-cold)))
+    `(gnus-summary-low-unread ((,class :inherit italic :foreground 
,fg-special-cold)))
     `(gnus-summary-normal-ancient ((,class :foreground ,fg-special-calm)))
     `(gnus-summary-normal-read ((,class :inherit shadow)))
     `(gnus-summary-normal-ticked ((,class :foreground ,red-alt-other)))
@@ -6723,6 +6747,9 @@ by virtue of calling either of 
`modus-themes-load-operandi' and
     `(powerline-evil-operator-face ((,class :inherit 
modus-themes-active-yellow)))
     `(powerline-evil-replace-face ((,class :inherit modus-themes-active-red)))
     `(powerline-evil-visual-face ((,class :inherit modus-themes-active-cyan)))
+;;;;; prescient
+    `(prescient-primary-highlight ((,class :inherit 
modus-themes-completion-match-0)))
+    `(prescient-secondary-highlight ((,class :inherit 
modus-themes-completion-match-1)))
 ;;;;; proced
     `(proced-mark ((,class :inherit modus-themes-mark-symbol)))
     `(proced-marked ((,class :inherit modus-themes-mark-alt)))
@@ -6841,9 +6868,6 @@ by virtue of calling either of 
`modus-themes-load-operandi' and
     `(selectrum-mouse-highlight ((,class :inherit highlight)))
     `(selectrum-quick-keys-highlight ((,class :inherit bold :background 
,bg-char-0)))
     `(selectrum-quick-keys-match ((,class :inherit bold :background 
,bg-char-1)))
-;;;;; selectrum-prescient
-    `(selectrum-prescient-primary-highlight ((,class :inherit 
modus-themes-completion-match-0)))
-    `(selectrum-prescient-secondary-highlight ((,class :inherit 
modus-themes-completion-match-1)))
 ;;;;; semantic
     `(semantic-complete-inline-face ((,class :foreground ,fg-special-warm 
:underline t)))
     `(semantic-decoration-on-fileless-includes ((,class :inherit 
modus-themes-refine-green)))
@@ -6988,6 +7012,8 @@ by virtue of calling either of 
`modus-themes-load-operandi' and
     `(speedbar-selected-face ((,class :inherit bold :foreground ,cyan)))
     `(speedbar-separator-face ((,class :inherit modus-themes-intense-neutral)))
     `(speedbar-tag-face ((,class :foreground ,yellow-alt-other)))
+;;;;; spell-fu
+    `(spell-fu-incorrect-face ((,class :inherit modus-themes-lang-error)))
 ;;;;; stripes
     `(stripes ((,class :background ,bg-alt)))
 ;;;;; suggest
@@ -7526,7 +7552,7 @@ by virtue of calling either of 
`modus-themes-load-operandi' and
     `(xterm-color-names-bright ["gray35" ,red-alt ,green-alt ,yellow-alt 
,blue-alt ,magenta-alt ,cyan-alt "white"])
     (if (or (eq modus-themes-org-blocks 'tinted-background)
             (eq modus-themes-org-blocks 'rainbow))
-        `(org-src-block-faces              ; TODO this list should be expanded
+        `(org-src-block-faces
           `(("emacs-lisp" modus-themes-nuanced-magenta)
             ("elisp" modus-themes-nuanced-magenta)
             ("clojure" modus-themes-nuanced-magenta)
diff --git a/etc/themes/modus-vivendi-theme.el 
b/etc/themes/modus-vivendi-theme.el
index ba75a2527d..0983e26c78 100644
--- a/etc/themes/modus-vivendi-theme.el
+++ b/etc/themes/modus-vivendi-theme.el
@@ -6,7 +6,7 @@
 ;; Maintainer: Modus-Themes Development <~protesilaos/modus-themes@lists.sr.ht>
 ;; URL: https://git.sr.ht/~protesilaos/modus-themes
 ;; Mailing-List: https://lists.sr.ht/~protesilaos/modus-themes
-;; Version: 2.6.0
+;; Version: 2.7.1
 ;; Package-Requires: ((emacs "27.1"))
 ;; Keywords: faces, theme, accessibility
 
diff --git a/etc/tutorials/TUTORIAL.translators 
b/etc/tutorials/TUTORIAL.translators
index 891b6a1682..64d71b61e8 100644
--- a/etc/tutorials/TUTORIAL.translators
+++ b/etc/tutorials/TUTORIAL.translators
@@ -90,6 +90,10 @@ Maintainer: Mats Lidell <matsl@contactor.se>
 Author:     Virach Sornlertlamvanich <virach@nectec.or.th>
 Maintainer: Virach Sornlertlamvanich <virach@nectec.or.th>
 
+* TUTORIAL.uk:
+Author:     Denys Nykula <vegan@libre.net.ua>
+Maintainer: Denys Nykula <vegan@libre.net.ua>
+
 * TUTORIAL.zh:
 Author:     Chao-Hong Liu <chliu@gnu.org>
 Maintainer: Chao-Hong Liu <chliu@gnu.org>
diff --git a/etc/tutorials/TUTORIAL.uk b/etc/tutorials/TUTORIAL.uk
new file mode 100644
index 0000000000..16190afe3a
--- /dev/null
+++ b/etc/tutorials/TUTORIAL.uk
@@ -0,0 +1,1150 @@
+Посібник Emacs.  Умови копіювання в кінці.
+
+Команди Emacs передбачають використання клавіші CONTROL (часто
+підписаної CTRL чи CTL) або клавіші META (зазвичай підписаної ALT).
+Щоб не писати цього щоразу повністю, використовуємо такі скорочення:
+
+ C-<симв> означає затиснути клавішу CONTROL і натиснути клавішу
+          <симв>.  Тобто C-f — це затиснути CONTROL і натиснути f.
+
+ M-<симв> означає затиснути клавішу META чи ALT і натиснути <симв>.
+          Якщо не маєте клавіші META чи ALT, натисніть по черзі
+          клавіші ESC та <симв>.  Клавішу ESC ми позначаємо <ESC>.
+
+Щоб редагувати український текст в Emacs, перемкніться на англійську
+розкладку й наберіть C-\ — це активує український режим введення
+Emacs, у якому працюватимуть як слід усі описані в посібнику команди.
+Щоб знову вводити англійські символи, повторіть C-\.
+
+Зауважте: щоб завершити сеанс Emacs, наберіть C-x C-c.  (Два символи.)
+Щоб скасувати частково введену команду, наберіть C-g.  Щоб закрити
+посібник, наберіть C-x k, а тоді при запиті натисніть <Return> (цю
+клавішу може бути підписано Enter).  Абзаци, що містять „»»“ на
+початку, — це вказівки, які вам слід виконати, щоб спробувати
+використати ту чи іншу команду. Наприклад:
+<<Програма help-with-tutorial огортає наступний рядок відступами>>
+[Кілька навмисне порожніх рядків.  Скоро зрозумієте, навіщо.]
+»» Прямо зараз наберіть C-v (view — переглянути), щоб прогорнути
+   посібник униз.  (Затисніть клавішу CONTROL і наберіть v.)
+   Робіть так щоразу, коли дочитуєте до кінця екрана.
+
+Як бачите, гортається не цілий екран, а на два рядки менше; це
+допомагає не губитись, коли читаєте довгі тексти.
+
+Це одноразова, трошки адаптована спеціально для вас копія тексту
+посібника Emacs.  Будемо пропонувати вам випробовувати прямо на цьому
+тексті різні команди, які його змінюватимуть.  Якщо зміните цей текст
+самостійно навіть без наших вказівок — чудово, це називається
+«редагування» й саме для цього створено Emacs.
+
+Перш за все навчимось пересуватися текстом.  Ви вже знаєте, як
+прогорнути один екран уперед: C-v.  Прогорнути один екран назад — це
+M-v (затисніть клавішу META й наберіть v — або наберіть <ESC>v, якщо
+не маєте ні клавіші META, ні клавіші ALT).
+
+»» Спробуйте набрати M-v й тоді C-v кілька разів по черзі.
+
+Якщо знаєте інші способи гортати текст, то можете, звісно,
+застосовувати і їх.
+
+* ПІДСУМОК
+----------
+
+Наступні команди допомагають гортати екрани тексту:
+
+        C-v     (View — переглянути.)
+                Перейти на наступний екран.
+
+        M-v     Перейти на попередній екран.
+
+        C-l     (Clear — очистити.)
+                Очистити екран і вивести текст заново, центруючи екран
+                на тексті біля курсора.  (Це CONTROL-L, не CONTROL-1.)
+
+»» Знайдіть курсор і зверніть увагу, який біля нього текст.  Наберіть
+   C-l.  Знайдіть, куди змістився курсор, і зауважте, що біля курсора
+   досі той самий текст, просто він тепер у центрі екрана.  Якщо знов
+   натиснете C-l, цей самий текст посунеться догори екрана.  Натисніть
+   C-l іще раз, і він посунеться донизу.
+
+Можете також гортати екрани клавішами PageUp і PageDn, якщо ваш
+термінал їх має.  Але з C-v та M-v ви редагуватимете спритніше.
+
+* ОСНОВИ КЕРУВАННЯ КУРСОРОМ
+---------------------------
+
+Гортати екран за екраном — це вже непогано, але як перейти до
+конкретного місця в тексті на екрані?
+
+Є кілька способів.  Можна рухатись клавішами стрілок, але зручніше
+тримати руки в стандартному положенні й використовувати команди C-p
+(previous — попередній), C-b (backward — назад), C-f (forward —
+уперед) і C-n (next — наступний).  Ці символи рівнозначні
+чотирьом клавішам стрілок:
+
+                         Попередній рядок, C-p
+                                  :
+                                  :
+            Назад, C-b .... Де курсор зараз .... Уперед, C-f
+                                  :
+                                  :
+                         Наступний рядок, C-n
+
+»» Посуньте курсор до рядка посередині рисунка вгорі за допомогою C-n
+   та C-p.  Тоді наберіть C-l, щоб центрувати екран на рисунку.
+
+Літери в скороченнях Emacs легко запам'ятати за відповідними їм
+англійськими словами, як зазначено вгорі.  Ці основні команди руху
+курсора ви будете використовувати весь час.
+
+»» Наберіть C-n кілька разів, щоб посунути курсор до цього рядка.
+
+»» Посуньте курсор до якого-небудь слова в рядку, набравши C-f
+   декілька разів, а тоді поверніться вгору кількома натисками C-p.
+   Зауважте, як C-p поводиться, коли курсор не на початку рядка.
+
+Кожен рядок тексту закінчується символом нового рядка: цей символ
+технічно відділяє рядок від наступного.  (Зазвичай останній рядок у
+файлі теж закінчується символом нового рядка, проте Emacs цього не
+вимагає.)
+
+»» Спробуйте C-b на початку рядка.  Курсор має зміститись у кінець
+   попереднього рядка, бо рух назад (backward) «проїде» по символу
+   нового рядка.
+
+C-f — рух уперед (forward) — уміє «їздити» по символах нового рядка
+так само, як і C-b.
+
+»» Наберіть C-b іще кілька разів, щоб відчути, яким чином рухається
+   курсор.  Тоді поверніться в кінець того рядка, з якого почали рух:
+   кілька разів наберіть C-f.  Тоді перейдіть іще одним C-f до
+   наступного рядка.
+
+Коли ваш рух перетинає верх чи низ екрана, текст із-за краю
+посувається на екран.  Це називається «гортання», або scrolling.
+Таким чином курсор, куди б у тексті ви його не спрямували, залишається
+в межах екрана Emacs.
+
+»» Спробуйте посунути курсор за нижню межу екрана кількома натисками
+   C-n — і зверніть увагу на поведінку.
+
+Коли рухатися символами надто повільно, рухайтеся словами.  M-f
+(META-f) посуває курсор через ціле слово вперед (forward), а M-b —
+через слово назад (backward).
+
+»» Наберіть M-f і M-b по кілька разів.
+
+Коли ви посеред слова, M-f посуває курсор до кінця слова.  Коли ви на
+пробілі, M-f посуває курсор у кінець наступного слова.  M-b працює
+аналогічно в зворотному напрямі.
+
+»» Наберіть іще кілька M-f і M-b, розділяючи їх натисками C-f і C-b,
+   щоб поспостерігати за поведінкою M-f і M-b залежно від перебування
+   курсора в різних місцях усередині слів і між словами.
+
+Простежте паралелі між C-f і C-b з одного боку та M-f і M-b з іншого.
+Часто Meta-символи використовуються для роботи з одиницями,
+визначеними мовою: зі словами, реченнями, абзацами.  Тоді як
+Control-символи працюють із низькорівневими одиницями, без огляду на
+редаговане середовище: з символами, рядками тощо.
+
+Ця паралель стосується також рядків і речень: C-a та C-e посувають до
+початку чи кінця рядка, тоді як M-a та M-e посувають до початку чи
+кінця речення.
+
+»» Наберіть C-a кілька разів, тоді наберіть C-e кілька разів.
+   Наберіть M-a кілька разів, тоді наберіть M-e кілька разів.
+
+Зверніть увагу на те, як повторні натиски C-a не викликають нових
+рухів, а от повтори M-a щоразу рухають вас на одне речення назад.
+Поведінка дещо різна, втім природна для кожного контексту.
+
+Координати курсора в тексті називаються «точкою».  Іншими словами,
+курсор позначає на екрані поточну точку в тексті.
+
+Ось підсумок простих операцій посування курсора, зокрема команд руху
+словами й реченнями:
+
+        C-f     (Forward — уперед.)  Перейти на символ уперед
+        C-b     (Backward — назад.)  Перейти на символ назад
+
+        M-f     Перейти на слово вперед
+        M-b     Перейти на слово назад
+
+        C-n     (Next — наступний.)  До наступного рядка
+        C-p     (Previous — попередній.)  До попереднього рядка
+
+        C-a     (Alpha — початок.)  Перейти на початок рядка
+        C-e     (End — кінець.)  Перейти в кінець рядка
+
+        M-a     Перейти назад на початок речення
+        M-e     Перейти вперед у кінець речення
+
+»» Випробуйте всі ці команди по кілька разів для практики.
+   Ними ви користуватиметесь частіше за всі інші.
+
+Дві інші важливі команди руху курсора — це M-< (META-менше), яка веде
+на початок усього тексту, й M-> (META-більше), яка веде в кінець
+усього тексту.
+
+На більшості терміналів знак „<“ — над латинською комою, тобто ви
+затискаєте SHIFT, щоб його набрати.  Відповідно M-< означає затиск не
+лише клавіші META, а ще й клавіші SHIFT.  Без SHIFT вийде M-кома.
+
+»» Спробуйте М-< зараз, щоб перейти на початок посібника.
+   Тоді кілька разів наберіть C-v, щоб повернутись сюди.
+
+»» Спробуйте М-> зараз, щоб перейти в кінець посібника.
+   Тоді кілька разів наберіть M-v, щоб повернутись сюди.
+
+Можете також рухати курсор клавішами стрілок, якщо ваш термінал має
+такі клавіші.  Втім, радимо опанувати C-b, C-f, C-n і C-p з трьох
+причин.  По-перше, вони працюють на будь-яких терміналах.  По-друге,
+практикуючи використання Emacs, ви невдовзі виявите, що набирати ці
+Control-символи швидше, ніж набирати стрілки (бо вам не доводиться
+зсувати руки з набірного положення).  По-третє, щойно ви звикнете до
+використання цих команд із Control-символами, вам стане легко
+опанувати й інші розширені команди посування курсора.
+
+Більшість команд Emacs приймають числовий аргумент; для більшості
+команд він означає кількість повторів.  Щоб передати команді кількість
+повторів, набирайте C-u і цифри, перш ніж набрати команду.  Якщо маєте
+клавішу META (чи ALT), то маєте й інший, альтернативний шлях увести
+числовий аргумент: наберіть цифри, затиснувши клавішу META.  Радимо
+опанувати шлях C-u, бо він працює на будь-яких терміналах.  Числовий
+аргумент також називається «префіксним аргументом», бо ви набираєте
+його перед відповідною йому командою.
+
+Наприклад, C-u 8 C-f посуває курсор на вісім символів уперед.
+
+»» Спробуйте використати C-n чи C-p з числовим аргументом, щоб
+   посунути курсор до рядка поруч із цим рядком єдиною командою.
+
+Числовий аргумент означає для більшості команд кількість повторів, але
+деякі команди використовують його інакше.  Декілька команд (але не ті,
+які вам уже відомі) використовують його як прапор: коли передаєте їм
+префіксний аргумент, це незалежно від значення змушує команду робити
+щось нетипове.
+
+C-v та M-v також є винятком.  За наявності аргументу вони гортають
+текст угору чи вниз на означену ним кількість рядків, а не на цілий
+екран.  Наприклад, C-u 8 C-v прокручує 8 рядків.
+
+»» Спробуйте набрати C-u 8 C-v прямо зараз.
+
+Це мало прогорнути текст на 8 рядків угору.  Якщо бажаєте прогорнути
+його знов донизу, передайте аргумент команді M-v.
+
+Якщо ви користуєтесь графічним середовищем, наприклад X чи MS-Windows,
+то з одного боку вікна Emacs має бути висока прямокутна область, яка
+називається панеллю гортання.  Можете гортати текст, натискаючи панель
+гортання кнопкою миші.
+
+Якщо у вашої миші є колесо гортання, можете гортати й ним.
+
+
+* ЯКЩО EMACS НЕ ВІДПОВІДАЄ
+--------------------------
+
+Якщо Emacs перестає відповідати на ваші команди, можете дати йому
+копняка, набравши C-g.  Використовуйте C-g, щоб припинити команду, яка
+виконується надто довго.
+
+Також використовуйте C-g, щоб відхилити числовий аргумент або початок
+команди, яку передумали дописувати.
+
+»» Наберіть C-u 100, щоб зазначити числовий аргумент «100».  Наберіть
+   C-g.  Тепер наберіть C-f.  Курсор має посунутись лише на один
+   символ, бо ви скасували аргумент набранням C-g.
+
+Ненароком набравши <ESC>, можете позбутись і його за допомогою C-g.
+
+
+* ВИМКНЕНІ КОМАНДИ
+------------------
+
+Деякі команди Emacs усталено «вимкнені», щоб не плутати користувачок і
+користувачів, які лише починають його вивчати.
+
+Коли ви набираєте ту чи іншу вимкнену команду, Emacs показує її назву
+й запитує, чи точно ви бажаєте виконати цю команду.
+
+Якщо ви справді бажаєте спробувати команду, наберіть <SPC> (пробіл) у
+відповідь на запитання.  Зазвичай, не бажаючи виконувати вимкнену
+команду, відповідайте на це питання «n».
+
+»» Наберіть C-x C-l (цю команду вимкнено), а тоді наберіть n у
+   відповідь на запитання.
+
+
+* WINDOWS
+---------
+
+Emacs може мати кілька «вікон» — кожне зі своїм текстом.  Згодом
+пояснимо, як використовувати декілька вікон одразу.  Поки що слід
+розібратись, як позбутись зайвих вікон і повернутись до простого
+редагування в одному вікні.  Це просто:
+
+        C-x 1   Одне вікно (тобто припинити всі інші вікна).
+
+Це означає набір CONTROL-x і цифри 1.  C-x 1 розширює вікно, яке
+містить курсор, на весь екран, і видаляє всі інші вікна.
+
+»» Посуньте курсор до цього рядка й наберіть C-u 0 C-l.  Тоді наберіть
+   C-h k C-f.  Це вікно звузиться, й з'явиться нове вікно, в якому
+   буде показано документацію про команду C-f.
+
+»» Наберіть C-x 1 — вікно документації зникне.
+
+Є цілі набори команд, які починаються з CONTROL-x; більшість із них
+стосуються вікон, файлів, буферів тощо.  Ці команди містять два, три
+або чотири символи.
+
+
+* ВВЕДЕННЯ Й ВИДАЛЕННЯ
+----------------------
+
+Якщо бажаєте ввести текст, просто наберіть цей текст.  Звичайні
+символи, такі як A, 7, * тощо, вводяться прямо при набранні.  Аби
+ввести символ нового рядка, натисніть <Return> (ця клавіша на
+клавіатурі часом підписана «Enter»).
+
+Щоб видалити символ перед курсором, наберіть <DEL>.  Ця клавіша на
+клавіатурі зазвичай підписана «Backspace» — та сама, якою за межами
+Emacs ви зазвичай видаляєте останній набраний символ.
+
+На клавіатурі зазвичай є ще одна клавіша з підписом <Delete>, але в
+Emacs ми позначаємо <DEL> не її.
+
+»» Прямо зараз наберіть кілька символів, а тоді видаліть їх, набравши
+   <DEL> кілька разів.  Сміливо змінюйте цей файл; оригінал посібника
+   залишиться недоторканим.  Це ваша особиста одноразова копія.
+
+Коли рядок тексту завеликий для одного рядка на екрані, цей рядок
+тексту «продовжується» на ще один екранний рядок.  Якщо ви
+користуєтесь графічним середовищем, на кожному боці текстової області
+(на лівому й правому «полях») з'являються маленькі вигнуті стрілки —
+позначки, де було продовжено той чи інший рядок.  Якщо ви користуєтесь
+текстовим терміналом, продовжений рядок позначається оберненою скісною
+рискою («\») в крайній правій колонці екрана.
+
+»» Вводьте текст, доки не сягнете правого поля, а тоді вводьте ще.
+   Побачите, як з'явиться рядок продовження.
+
+»» Натискайте <DEL> раз за разом, щоб видаляти символи, доки рядок
+   тексту не вміститься знову в один екранний рядок.  Рядок
+   продовження зникне.
+
+Символ нового рядка можете видалити так само, як будь-який інший.
+Видалення символу нового рядка між двома рядками зливає їх в один
+рядок.  Якщо в результаті сполучений рядок ширший, ніж екран, то його
+буде показано з рядком продовження.
+
+»» Посуньте курсор на початок рядка й наберіть <DEL>.  Це
+   зіллє той рядок із попереднім.
+
+»» Наберіть <Return>, щоб відновити символ нового рядка на місці щойно
+   видаленого.
+
+Клавіша <Return> особлива тим, що її натиск може не лише вводити
+символ нового рядка.  Залежно від навколишнього тексту, її натиск може
+ввести пробіли після символу нового рядка таким чином, щоб текст у
+новоствореному рядку одразу вирівнювався відносно тексту попереднього
+рядка.  Ми називаємо цю поведінку (складнішу дію клавіші, ніж просто
+введення відповідного символу) «електричною».
+
+»» Щоб побачити електричну поведінку <Return>,
+   наберіть <Return> у кінці цього рядка.
+
+Простежте, як при введенні символу нового рядка Emacs вводить пробіли
+таким чином, що курсор опиняється під «н» слова «наберіть».
+
+Пам'ятайте, більшості команд Emacs можливо передати кількість
+повторень; це стосується й текстових символів.  Повторення текстового
+символу вводить цей символ декілька разів.
+
+»» Спробуйте зараз набрати C-u 8 *, аби ввести ********.
+
+Ви опанували основний спосіб набрати щось в Emacs і виправити помилки.
+Також можна видаляти цілі слова й рядки.  Ось підсумок операцій
+видалення:
+
+        <DEL>   Видалити символ перед курсором
+        C-d     (Delete — видалити.)  Видалити символ після курсора
+
+        M-<DEL> Вирізати слово перед курсором
+        M-d     Вирізати слово після курсора
+
+        C-k     (Kill — вирізати.)  Вирізати з рядка все після курсора
+        M-k     Вирізати з речення все після курсора
+
+Зважте на те, як <DEL> і C-d продовжують щодо M-<DEL> і M-d паралелі,
+розпочаті C-f і M-f (технічно <DEL> не є керівним символом, але зараз
+це несуттєво).  C-k та M-k подібні до C-e та M-e тим, як вони працюють
+із рядками й реченнями відповідно.
+
+Є також спосіб вирізати одразу багато тексту.  Перейдіть на початок чи
+в кінець потрібної частини тексту й натисніть C-<SPC>.  (<SPC> —
+пробіл.)  Тоді посуньте курсор на інший край частини тексту, яку
+бажаєте вирізати.  Коли ви це робитимете, Emacs підсвітить текст між
+курсором і тим місцем, де ви набрали C-<SPC>.  Нарешті наберіть C-w
+(whole — повністю).  Це повністю виріже позначений текст.
+
+»» Посуньте курсор до «Є» на початку попереднього абзацу.
+»» Введіть C-<SPC>.  Emacs має показати повідомлення «Mark set»
+   («Позначку встановлено») внизу екрана.
+»» Посуньте курсор до «п» в слові «потрібної» другого рядка абзацу.
+»» Наберіть C-w.  Це виріже текст від «Є» включно до «п» не включно.
+   Якби ви набрали M-w, це його лише скопіювало б.
+
+Вирізання й видалення відрізняються тим, що вирізаний текст можливо
+вставити (будь-куди), тоді як видалене вставити неможливо (втім,
+можливо скасувати видалення — про це згодом).  Вставка, так би мовити,
+виймає (yank) раніше вилучений текст.  Зазвичай усі команди, які
+вилучають багато тексту, вирізають його (їх налаштовано таким чином,
+щоб ви могли потім вставити цей текст); а от команди, які вилучають
+лише один символ або порожні рядки чи пробіли, просто їх видаляють
+(без можливості вставлення).  <DEL> і C-d без аргументу здійснюють
+видалення.  За наявності аргументу вони вирізають.
+
+»» Посуньте курсор на початок рядка, в якому є символи.
+   Тоді наберіть C-k, щоб вирізати текст у цьому рядку.
+»» Наберіть C-k іще раз.  Це виріже той символ нового рядка,
+   що завершує цей рядок.
+
+Зауважте, перший C-k вирізає вміст рядка, а другий C-k вирізає сам
+рядок і зсуває догори всі наступні рядки.  C-k із числовим аргументом
+працює інакше: вирізає водночас вміст і самі рядки.  Це не просто
+повторення.  C-u 2 C-k вирізає два рядки разом із їхніми символами
+нового рядка; набравши C-k двічі, ви отримаєте інший результат.
+
+Можете вставити вирізаний текст туди ж, звідки його вирізали, або до
+певного іншого місця в редагованому тексті — навіть в інший файл.
+Можете вставляти один і той самий текст кілька разів; це створить
+кілька його копій.  Англійською вирізання називають «kill» або «cut»,
+виймання (вставку) — «yank» або «paste»; перегляньте згодом глосарій у
+підручнику Emacs.
+
+Команда C-y вставляє останній вирізаний текст туди, де зараз курсор.
+
+»» Спробуйте набрати C-y, щоб вставити текст назад.
+
+Якщо наберете C-k декілька разів поспіль, увесь вирізаний текст
+збережеться разом, щоб одною командою C-y ви могли вставити водночас
+усі рядки.
+
+»» Спробуйте зараз набрати C-k декілька разів.
+
+Тепер отримаймо вирізаний текст:
+
+»» Наберіть C-y.  Посуньте курсор на кілька рядків униз і наберіть C-y
+   знову.  Тепер ви знаєте, як копіювати текст.
+
+Що робити, коли вже вирізали певний текст для вставки, але після цього
+вирізали ще щось?  C-y вставляє найновіше вирізане.  Проте минулого
+вирізаного не втрачено.  Можете повернутись до нього командою M-y.
+Набравши C-y та вийнявши цим найновіше вирізане, наберіть M-y іще раз,
+щоб замінити вставлений текст на попереднє вирізане.  Набирайте M-y
+знову й знову, щоб виймати минулі вирізання.  Досягнувши шуканого
+тексту, ви не маєте більше нічого робити, щоб його зафіксувати.
+Просто продовжуйте редагувати, залишивши вставлений текст на місці.
+
+Набравши M-y достатньо разів, виймете найновіше вирізане знову.
+
+»» Виріжте рядок, посуньте курсор і виріжте ще один рядок.
+
+»» Тоді наберіть C-y, щоб отримати другий вирізаний рядок.  Тоді
+   наберіть M-y, і той рядок заміниться на перший вирізаний рядок.
+
+»» Наберіть M-y іще кілька разів і простежте, що вийматиметься.
+   Продовжуйте набирати M-y, доки не виймете знову другий вирізаний
+   рядок, і тоді наберіть ще декілька M-y.  За бажання спробуйте
+   передати M-y додатні чи від'ємні аргументи.
+
+
+* СКАСУВАННЯ
+------------
+
+Помилково змінивши текст, можете скасувати цю зміну командою
+скасування C-_, тобто затисніть Control та Shift і натисніть дефіс.
+
+Зазвичай C-_ скасовує зміни, внесені одною командою; якщо ви наберете
+кілька C-_ поспіль, кожне повторення скасує ще по одній команді.
+
+Але є два винятки.  Команди, які не змінюють тексту, не враховуються
+(зокрема це команди руху курсора й команди гортання).  Й текстові
+символи зазвичай обробляються по 20 водночас.  (Це щоб зменшити
+кількість C-_, потрібних для скасування вставки тексту.)
+
+»» Виріжте цей рядок за допомогою C-k. Тоді наберіть C-_, щоб він
+   з'явився знову.
+
+C-/ — альтернативна команда скасування; вона працює так само, як C-_.
+Деякі термінали дають змогу набирати C-_ без клавіші Shift.  Деякі
+термінали насправді передають Emacs саме C-_, коли ви набираєте C-/.
+Ще є команда C-x u, котра працює так само, як C-_, але менш зручна для
+набрання.  Посібники для латинських розкладок описують команду C-/ як
+основну, проте українською C-_ набирати легше.
+
+Числовий аргумент для C-_, C-/ чи C-x u працює як кількість повторень.
+
+Можете скасувати видалення тексту так само, як скасовуєте вирізання.
+Відмінність між вирізанням і видаленням полягає в тому, чи можете ви
+вставити текст за допомогою C-y; для скасування нема жодної різниці.
+
+
+* ФАЙЛИ
+-------
+
+Щоб зробити зміни до тексту постійними, їх треба зберегти до файлу.
+Інакше ці зміни зникнуть при виході з Emacs.  Аби зберегти текст до
+файлу, вам потрібно «знайти» файл, перш ніж ввести текст.  (Це також
+називають «відкриттям» файлу.)
+
+Знаходження файлу показує його вміст усередині Emacs.  Це виглядає,
+ніби ви редагуєте файл безпосередньо.  Проте зміни, які ви вносите за
+допомогою Emacs, не стають постійними, доки ви не «збережете» файл.
+Це щоб не залишати лише частково зміненого файлу в системі, коли ви
+цього не бажаєте.  Навіть після збереження Emacs залишає початковий
+файл під іншою назвою, щоб ви змогли згодом скасувати зміни, якщо вони
+виявляться помилковими.
+
+Якщо ви роздивитесь низ екрану, то помітите рядок, який містить дефіси
+й починається з чогось на кшталт « -:--- TUTORIAL».  Ця частина екрану
+зазвичай показує назву файлу, який ви редагуєте.  Зараз ви
+переглядаєте свою особисту копію посібника Emacs, названу «TUTORIAL».
+Коли ви знаходите файл за допомогою Emacs, назва цього файлу
+показується саме в тому місці.
+
+Команда знаходження файлу особлива тим, що запитує вас, який файл вам
+треба.  Іншими словами, команда «зчитує аргумент» (у цьому випадку
+аргумент — назва файлу).  Після набрання команди:
+
+        C-x C-f   (Find — знайти.)  Знайти файл
+
+Emacs попрохає вас набрати назву файлу.  При введенні назву файлу
+показано в нижньому рядку екрана.  Нижній рядок називають мінібуфером,
+коли він використовується для таких запитів.  Можете використовувати
+звичні команди редагування Emacs, щоб редагувати назву файлу.
+
+Коли ви вводите назву файлу (чи заповнюєте інший мінібуфер), можете
+скасувати команду за допомогою C-g.
+
+»» Наберіть C-x C-f, а тоді наберіть C-g.  Це скасує мінібуфер, а
+   також команду C-x C-f, яка використовувала мінібуфер.  Тому ви не
+   знайдете жодного файлу.
+
+Коли ви введете назву файлу до кінця, наберіть <Return>, щоб завершити
+введення.  Мінібуфер зникне, й команда C-x C-f спробує знайти обраний
+вами файл.
+
+Вміст файлу з'явиться на екрані, й ви зможете редагувати цей вміст.
+Коли вирішите зберегти зміни на постійно, наберіть команду:
+
+        C-x C-s   (Save — зберегти.)  Зберегти
+
+Це скопіює текст із Emacs до файлу.  Коли ви робите це вперше, Emacs
+перейменовує початковий файл, щоб його не було втрачено.  Нова назва
+формується доданням «~» у кінець назви початкового файлу.  Завершивши
+збереження, Emacs показує назву записаного файлу.
+
+»» Наберіть C-x C-s TUTORIAL <Return>.
+   Це має зберегти посібник у файл із назвою «TUTORIAL» і показати
+   «Wrote ...TUTORIAL» унизу екрана.
+
+Можете знайти вже наявний файл, щоб переглянути чи відредагувати його.
+Або можете «знайти» файл, якого ще не існує.  Так в Emacs створюють
+файли: відкривають порожній файл і починають вводити текст у цей файл.
+Коли ви попросите «зберегти» файл, Emacs насправді створить файл зі
+введеним вами текстом.  Відтоді можете вважати, що редагуєте вже
+наявний файл.
+
+
+* БУФЕРИ
+--------
+
+Коли знаходите ще один файл за допомогою C-x C-f, перший файл
+залишається в Emacs.  Можете перемкнутись на нього, знайшовши його
+знову за допомогою C-x C-f.  Таким чином в Emacs можливо відкрити
+чимало файлів.
+
+Emacs зберігає текст кожного файлу всередині об'єкта — так званого
+«буфера».  Знаходження файлу створює буфер усередині Emacs.  Щоб
+переглянути перелік уже наявних буферів, наберіть
+
+        C-x C-b   (Buffer — буфер.)  Перелічити буфери
+
+»» Спробуйте C-x C-b прямо зараз.
+
+Зауважте, що кожен буфер має власну назву, а ще біля нього може бути
+назва того файлу, вміст якого він утримує.  Будь-який текст, який ви
+бачите у вікні Emacs, завжди є частиною певного буфера.
+
+»» Наберіть C-x 1, щоб позбутись переліку буферів.
+
+Коли буферів декілька, лише один із них може бути «поточним».  Це той
+буфер, який ви редагуєте.  Якщо бажаєте редагувати інший буфер, на
+нього слід «перемкнутись».  Якщо бажаєте перемкнутись на буфер, що
+відповідає певному файлу, відкрийте файл знову за допомогою C-x C-f.
+Але простіше використати команду C-x b.  Цій команді достатньо
+передати назву буфера.
+
+»» Відкрийте файл «foo», набравши C-x C-f foo <Return>.  Тоді наберіть
+   C-x b TUTORIAL <Return>, щоб повернутись до цього посібника.
+
+Зазвичай назва буфера збігається з назвою файлу (без шляху).  Втім, це
+не завжди так.  Перелік буферів, відкритий за допомогою C-x C-b,
+показує як назву буфера, так і назву файлу кожного буфера.
+
+Деякі буфери не відповідають файлам.  Буфер *Buffer List* («Перелік
+буферів»), який показує створені за допомогою C-x C-b буфери, не має
+жодного файлу.  Цей буфер TUTORIAL спершу не мав файлу, але тепер має,
+бо в попередньому розділі ви набрали C-x C-s і зберегли його до файлу.
+
+Буфер *Messages* також не відповідає жодному файлу.  Цей буфер містить
+повідомлення, які з'являлись у нижньому рядку вашого сеансу Emacs.
+
+»» Наберіть C-x b *Messages* <Return>, щоб переглянути буфер
+   повідомлень.  Тоді наберіть C-x b TUTORIAL <Return>, щоб
+   повернутись до цього посібника.
+
+Якщо змінити текст одного файлу й знайти інший файл, то перший файл не
+буде збережено.  Зміни залишаться всередині Emacs у буфері цього
+файлу.  Створення чи редагування буфера другого файлу не впливає на
+буфер першого файлу.  Це дуже корисно, але буфер першого файлу теж
+треба якось зберігати.  Перемикатись на цей буфер, щоб зберегти його
+за допомогою C-x C-s, було б незручно.  Тож маємо
+
+        C-x s     (Save — зберегти.)
+                  Зберегти певні буфери до їхніх файлів
+
+C-x s запитує вас про кожен буфер, який ви змінили й не зберегли, чи
+бажаєте ви його зберегти.
+
+»» Введіть рядок тексту й наберіть C-x s.
+   Маєте отримати запитання, чи бажаєте ви зберегти буфер TUTORIAL.
+   Погодьтесь, набравши у відповідь «y».
+
+
+* РОЗШИРЕННЯ НАБОРУ КОМАНД
+--------------------------
+
+Команд Emacs значно більше, ніж можливо розкласти по всіх Control- і
+Meta-символах.  Emacs обходить це за допомогою команди x (extend —
+розширити).  Вона має два різновиди:
+
+        C-x     Запитує один символ.
+        M-x     Запитує англійську назву команди.
+
+Ці команди дуже корисні, проте використовуються менш часто, ніж ті
+команди, які ви вже опанували.  Деякі з цих команд ви вже бачили,
+наприклад файлові команди C-x C-f для знаходження й C-x C-s для
+збереження.  Ще один приклад — команда завершення сеансу Emacs: C-x
+C-c пропонує зберегти кожен змінений файл, перш ніж зупинити Emacs.
+
+Якщо ви користуєтесь графічним середовищем, вам не потрібні жодні
+додаткові команди для переходу від Emacs до іншого застосунку.  Можете
+переходити мишею чи командами менеджера вікон.  Але якщо ви
+користуєтесь текстовим терміналом, який може показувати лише один
+повноекранний застосунок, то вам потрібно «призупинити» Emacs, щоб
+перейти до іншого застосунку.
+
+Команда C-z виходить з Emacs *тимчасово* — тобто ви можете повернутись
+до цього ж сеансу Emacs згодом.  Коли Emacs запущено в текстовому
+терміналі, C-z «призупиняє» Emacs, тобто повертає вас до оболонки, але
+не припиняє завдання Emacs.  У найпоширеніших оболонках ви можете
+відновити Emacs командою «fg» або «%emacs».
+
+Використовувати C-x C-c слід перед виходом із системи.  Ще цією
+командою слід закривати той Emacs, який ви запустили для швидкого
+редагування, наприклад за допомогою знаряддя обробки пошти.
+
+Команд C-x чимало.  Ось перелік тих, які ви вже опанували:
+
+        C-x C-f         (Find — знайти.)  Знайти файл.
+        C-x C-s         (Save — зберегти.)  Зберегти буфер до файлу.
+        C-x s           Зберегти певні буфери до їхніх файлів.
+        C-x C-b         (Buffer — буфер.)  Перелічити буфери.
+        C-x b           Перемкнути буфер.
+        C-x C-c         (Close — закрити.)  Вийти з Emacs.
+        C-x 1           Видалити всі вікна, крім одного.
+        C-x u           (Undo — скасувати.)  Скасувати останню дію.
+
+Розширені команди, які використовуються менш часто чи лише в певних
+режимах, мають довші англійські назви.  Наприклад, команда
+replace-string («замінити рядок») змінює в буфері один рядок на інший.
+Коли ви набираєте M-x, Emacs показує внизу екрана запит M-x, у
+відповідь на який вам слід набрати назву команди — «replace-string» у
+цьому випадку.  Просто наберіть «repl s<TAB>» — і Emacs допише назву
+команди.  (<TAB> — це клавіша Tab, яку зазвичай розміщено над клавішею
+Caps Lock чи Shift ліворуч на клавіатурі.)  Підтвердьте назву команди
+клавішею <Return>.
+
+Команда replace-string потребує двох аргументів: рядка, який слід
+замінити, й рядка, яким ви його замінюєте.  Введення кожного аргументу
+потрібно завершити клавішею <Return>.
+
+»» Посуньте курсор до порожнього рядка двома рядки нижче, ніж цей.
+   Тоді наберіть M-x repl s<Return>змінено<Return>перетворено<Return>.
+
+   Зверніть увагу на те, як зміниться цей рядок: ви заміните слово
+   «змінено» на слово «перетворено» в усіх місцях після курсора, де
+   воно зустрічається.
+
+
+* САМОЗБЕРЕЖЕННЯ
+----------------
+
+Коли ви змінюєте файл, але не зберігаєте змін, то ці зміни може бути
+втрачено, якщо комп'ютер зазнає збою.  Emacs захищає вас від цього
+періодичним записом файлів «самозбереження» для кожного редагованого
+файлу.  Назва файлу самозбереження починається й закінчується символом
+#, тобто якщо ваш файл називається «hello.c», його файл самозбереження
+називатиметься «#hello.c#».  Коли ви зберігаєте файл звичним шляхом,
+Emacs видаляє відповідний файл самозбереження.
+
+Після збою комп'ютера ви можете відновити самозбережені правки,
+знайшовши файл як зазвичай (редагований файл, не файл самозбереження)
+й набравши M-x recover-this-file <Return>.  (Команда перекладається як
+«відновити цей файл».)  У відповідь на запит підтвердження, наберіть
+yes<Return>, щоб погодитись і відновити дані самозбереження.
+
+
+* ВІДЛУННЯ
+----------
+
+Коли Emacs помічає, що ви поволі набираєте багатосимвольну команду,
+він показує вам набрану частину команди внизу екрана в області, яку
+називають echo — відлунням.  Ця область покриває нижній рядок екрана.
+
+
+* РЯДОК РЕЖИМУ
+--------------
+
+Рядок прямо над областю відлуння називають «рядком режиму».  В рядку
+режиму має виводитись щось таке:
+
+ -:**-  TUTORIAL       63% L749    (Fundamental)
+
+Цей рядок надає корисні дані про стан Emacs і редагованого тексту.
+
+Ви вже знаєте, що значить TUTORIAL: це назва того файлу, який ви
+знайшли раніше.  ЧЧ% повідомляє, в якій частині буфера тексту ви
+перебуваєте; це означає, що ЧЧ відсотків буфера зараз над екраном.
+Якщо верх буфера видно на екрані, то замість «0%» буде «Top» — «Верх».
+Якщо низ буфера видно на екрані, то буде показано «Bot» — «Низ».
+Якщо ви читаєте такий маленький буфер, що весь його вміст видно на
+одному екрані, то в рядку режиму буде «All» — «Усе».
+
+Літера L (line — рядок) і цифри показують інший аспект вашого
+місцеперебування — номер рядка поточної точки.
+
+Зірки спереду вказують, що ви змінили текст.  Коли файл тільки
+відкрито чи збережено, ця частина рядка режиму показує лише дефіси,
+без зірок.
+
+Частина рядка режиму всередині дужок вказує, які режими редагування
+наразі чинні.  Типовий режим — це Fundamental («основний»); саме ним
+ви зараз користуєтесь.  Це один із «вищих» режимів.
+
+Emacs має чимало різних вищих режимів.  Деякі з них призначені для
+редагування різних мов і/або видів тексту, зокрема режим Lisp (для
+мови програмування Лісп), режим Text (для тексту) тощо.  В певну мить
+активним може бути один і тільки один вищий режим, і його назву завжди
+можна знайти в рядку режиму — там, де зараз ви бачите Fundamental.
+
+Кожен вищий режим змінює поведінку декількох команд.  Наприклад, є
+команди для створення коментарів у програмі, й оскільки кожна мова
+програмування по-своєму визначає вигляд коментарів, то й кожен вищий
+режим має вставляти коментарі по-своєму.  Кожен вищий режим є назвою
+тої команди розширення, якою ви його вмикаєте.  Наприклад, командою
+M-x fundamental-mode ви вмикаєте основний режим.
+
+Для редагування тексту мовами людського спілкування, такими як цей
+файл, ви зазвичай використовуватимете режим Text.
+
+»» Наберіть M-x text-mode <Return>.
+
+Не переймайтесь, жодні з уже відомих вам команд Emacs не стануть
+поводитись геть інакше.  Проте ви можете помітити, що M-f і M-b тепер
+обробляють апострофи як частини слів.  Досі, в режимі Fundamental, M-f
+і M-b обробляли апострофи як пунктуацію.
+
+Вищі режими зазвичай вносять саме такі невеличкі зміни: більшість
+команд продовжують виконувати звичні завдання — з деякими
+відмінностями в деталях.
+
+Щоб переглянути документацію про поточний вищий режим, наберіть C-h m.
+
+»» Посуньте курсор до рядка, наступного після цього.
+»» Наберіть C-l C-l, щоб посунути цей рядок угору екрана.
+»» Наберіть C-h m, щоб побачити різницю режимів Text і Fundamental.
+»» Наберіть C-x 1, щоб вилучити документацію з екрана.
+
+Вищі режими називаються вищими, тому що бувають і нижчі режими.  Нижчі
+режими не заміщають вищих режимів, а трошки їх змінюють. Кожен нижчий
+режим можна ввімкнути чи вимкнути окремо, незалежно від усіх інших
+нижчих режимів — і незалежно від вашого вищого режиму.  Тож ви можете
+не використовувати нижчих режимів зовсім, або використовувати єдиний
+нижчий режим, або поєднувати декілька нижчих режимів.
+
+Один із дуже корисних нижчих режимів, особливо для редагування тексту
+мовами людського спілкування, — це режим Auto Fill (самозаповнення).
+Коли цей режим увімкнено, Emacs автоматично вставляє символ нового
+рядка між словами, коли при введенні тексту певний рядок виявляється
+занадто широким.
+
+Режим самозаповнення вмикається командою M-x auto-fill-mode <Return>.
+Коли режим увімкнено, можете вимкнути його знову тою ж командою.  Якщо
+режим вимкнено, команда його вмикає; а коли режим увімкнено, команда
+його вимикає.  Можна сказати, що команда «перемикає режим».
+
+»» Наберіть M-x auto-fill-mode <Return>.  Тоді набирайте «йцук » знов
+   і знов, доки рядок не поділиться на два.  Пробіли слід набирати,
+   оскільки самозаповнення розбиває рядки тільки на пробілах.
+
+Поле зазвичай починається після 70 символів, але можете змінити це
+командою C-x f.  Передайте бажане значення поля числовим аргументом
+цій команді.
+
+»» Наберіть C-x f із аргументом 20.  (C-u 2 0 C-x f).
+   Тоді наберіть будь-який текст і зверніть увагу, яким чином Emacs
+   поділить його на 20-символьні рядки.  Тоді поверніть полю значення
+   70 за допомогою ще одної команди C-x f.
+
+Якщо ви зміните щось посеред абзацу, режим самозаповнення не ділитиме
+абзацу на рядки заново без вашого дозволу.
+Щоб поділити абзац заново (замінюючи наявні символи нового рядка),
+посуньте курсор до цього абзацу й наберіть M-q (META-q).
+
+»» Посуньте курсор до попереднього абзацу й наберіть M-q.
+
+
+* ПОШУК
+-------
+
+Emacs уміє шукати рядки (групи послідовних символів) в тексті як після
+курсора, так і перед ним.  Пошук рядка є командою руху курсора, тобто
+посуває курсор до наступного місця, де з'являється цей рядок.
+
+Команда пошуку Emacs є покроковою.  Тобто пошук відбувається прямо при
+набранні рядка, який ви бажаєте шукати.
+
+Пошук уперед розпочинає команда C-s (search — пошук), а пошук назад —
+команда C-r (reverse — навпаки).  АЛЕ ЗАЧЕКАЙТЕ!  Не поспішайте
+випробовувати ці команди.
+
+Набравши C-s, ви помітите, що область відлуння покаже рядок
+«I-search».  Це означатиме, що Emacs перейшов до режиму incremental
+search — покрокового пошуку — й очікує, поки ви наберете, що бажаєте
+шукати.  <Return> завершує пошук.
+
+»» Тепер наберіть C-s, щоб розпочати пошук.  ПОВОЛІ, літера за
+   літерою, наберіть слово «курсор», зупиняючись після кожного символу
+   й звертаючи увагу на те, що відбувається з курсором.  Отже, ви
+   щойно пошукали «курсор» один раз.
+»» Наберіть C-s іще раз — знайдіть наступну послідовність «курсор».
+»» Тепер наберіть <DEL> чотири рази й зауважте, куди зсунеться курсор.
+»» Наберіть <Return>, щоб завершити пошук.
+
+Щойно ви спостерігали, як покроковий пошук Emacs намагається перейти
+до послідовності, збіжної з набраним вами рядком.  Щоб перейти до
+наступної послідовності «курсор», наберіть C-s іще раз.  Якщо такої
+послідовності не існує, Emacs сигналить і повідомляє, що пошук наразі
+«failing» — неуспішний.  C-g завершує пошук у будь-якому разі.
+
+Якщо під час покрокового пошуку набрати <DEL», пошук «повертається» до
+попередньої точки.  Якщо набрати <DEL> одразу після C-s — переходу до
+наступного збігу з шуканим рядком — то <DEL> поверне курсор до
+попереднього збігу.  Якщо збігів перед курсором нема, <DEL> зітре
+останній символ рядка пошуку.  Наприклад, наберіть «к», щоб пошукати
+перший збіг із «к».  Тоді наберіть «у» — це посуне курсор до першого
+збігу з «ку».  Нарешті наберіть <DEL> — це зітре «у» з рядка пошуку,
+тож курсор повернеться до першого збігу з «к».
+
+Якщо посеред пошуку ви набираєте Control- чи Meta-символ (за винятком
+символів власне пошуку, як-от C-s чи C-r), пошук припиняється.
+
+C-s розпочинає пошук будь-якого збігу з шуканим рядком ПІСЛЯ того
+місця, де курсор зараз.  Якщо бажаєте пошукати щось у більш ранньому
+тексті, наберіть натомість C-r.  Усе, що ми зазначили про C-s,
+стосується й C-r, — крім напряму пошуку, який стає зворотним.
+
+
+* ДЕКІЛЬКА ВІКОН
+----------------
+
+Emacs має ту файну властивість, що ви можете переглядати водночас
+декілька вікон на одному екрані.  (Зауважте, що Emacs використовує
+роз'яснений у наступному розділі термін «рамка», або frame, замість
+вжитого в деяких інших застосунках терміна «вікно».)  Підручник Emacs
+містить глосарій термінів Emacs.
+
+»» Посуньте курсор до цього рядка й наберіть C-l C-l.
+
+»» Наберіть C-x 2 — розділіть екран на два вікна.  Обидва вікна
+   показуватимуть цей посібник.  Курсор редагування залишатиметься в
+   горішньому вікні.
+
+»» Наберіть C-M-v — перегорніть нижнє вікно.
+   (Якщо не маєте клавіші META, наберіть <ESC> C-v.)
+
+»» Наберіть C-x o (other — інше), щоб посунути курсор до нижнього
+   вікна.  Погортайте нижнє вікно за допомогою C-v та M-v.  Читайте ці
+   вказівки далі в горішньому вікні.
+
+»» Наберіть C-x o ще раз, щоб посунути курсор назад до горішнього
+   вікна.  Курсор у горішньому вікні опиниться там же, де був досі.
+
+Можете й надалі перемикатись між вікнами за допомогою C-x o.  «Обране»
+вікно, в якому переважно відбувається редагування, — це те вікно, в
+якому ви бачите помітний курсор, який блимає, коли ви нічого не
+набираєте.  Кожне вікно має власну точку, на яку вказує його курсор;
+якщо ви запускаєте Emacs у графічному середовищі, ці курсори не
+блимають і мають вигляд порожніх коробок.
+
+Команда C-M-v дуже корисна, коли ви редагуєте текст в одному вікні й
+використовуєте інше вікно лише як довідку.  Не виходячи з обраного
+вікна, ви можете гортати текст в іншому вікні за допомогою C-M-v.
+
+C-M-v — це один із CONTROL-META-символів.  Якщо ви маєте клавішу META
+(чи Alt), то можете набрати C-M-v, затиснувши водночас клавіші CONTROL
+та META й натиснувши v.  Неважливо, яку з клавіш CONTROL і META ви
+затиснете першою, бо ці клавіші не діють самостійно, а змінюють
+символи, які ви набираєте.
+
+Якщо у вас нема клавіші META, можете використати натомість <ESC>, але
+тоді порядок таки важливий: наберіть спершу <ESC>, а тоді CONTROL-v.
+CONTROL-<ESC> v не спрацює, бо <ESC> не змінює інші символи, а є
+повноцінною клавішею.
+
+»» Наберіть C-x 1 (у горішньому вікні), щоб позбутись нижнього вікна.
+
+(Якби ви набрали C-x 1 у нижньому вікні, то позбулися б горішнього
+вікна.  Уявляйте цю команду як «Залишити лише одне вікно — те, в якому
+я зараз».)
+
+Показувати один і той самий буфер в обох вікнах необов'язково.  Якщо в
+одному вікні ви наберете C-x C-f і знайдете файл, то на інше вікно це
+не вплине.  Можете знаходити файли в обох вікнах незалежно.
+
+Ось ще один шлях застосування двох вікон для показу двох різних речей:
+
+»» Наберіть C-x 4 C-f та назву одного зі своїх файлів.  Завершіть за
+   допомогою <Return>.  Зазначений файл з'явиться в нижньому вікні.
+   Курсор також туди посунеться.
+
+»» Наберіть C-x o, щоб перейти до горішнього вікна,
+   й C-x 1, щоб видалити нижнє вікно.
+
+
+* ДЕКІЛЬКА РАМОК
+----------------
+
+Emacs також дає змогу створити декілька «рамок» (frames).  Рамкою
+називають будь-яку добірку вікон разом із її меню, панелями гортання,
+областю відлуння тощо.  У графічних середовищах Emacs називає рамкою
+те, що багато інших застосунків називають «вікном».  Екран може
+показувати декілька графічних рамок водночас.  На текстовому терміналі
+може бути видно лише одну рамку.
+
+»» Наберіть C-x 5 2.
+   На вашому екрані з'явиться нова рамка.
+
+У новій рамці ви можете робити все те, що й у початковій рамці.
+Початкова рамка не є чимось особливим.
+
+»» Наберіть C-x 5 0.
+   Це вилучить обрану рамку.
+
+Вилучити рамку можна й тим звичним шляхом, який передбачає графічне
+середовище (зазвичай це кнопка «X» в одному з горішніх кутів рамки).
+Якщо вилучити таким чином останню рамку сеансу Emacs, увесь Emacs буде
+припинено.
+
+
+* РІВНІ РЕКУРСІЇ РЕДАГУВАННЯ
+----------------------------
+
+Інколи ви потраплятимете в так званий «рівень рекурсії редагування».
+Його позначають квадратні дужки в рядку режиму навколо круглих дужок
+назви вищого режиму.  Наприклад, ви можете бачити [(Fundamental)]
+замість (Fundamental).
+
+Щоб вийти з рівня рекурсії редагування, наберіть <ESC> <ESC> <ESC>.
+Це всеохопна команда виходу.  Можете використовувати її й для закриття
+зайвих вікон чи виходу з мінібуфера.
+
+»» Наберіть M-x, щоб потрапити до мінібуфера.
+   Тоді наберіть <ESC> <ESC> <ESC>, щоб із нього вийти.
+
+Вийти з рівня рекурсії редагування за допомогою C-g неможливо, бо C-g
+використовується для скасування команд і аргументів УСЕРЕДИНІ рівня
+рекурсії редагування.
+
+
+* ОТРИМАТИ БІЛЬШЕ ДОВІДКИ
+-------------------------
+
+Цей посібник намагається надати необхідний для початку роботи з Emacs
+мінімум даних.  Emacs уміє стільки всього, що одним посібником описати
+всі його функції було б неможливо.  Проте вам буде цікаво дізнатись
+більше про Emacs та його корисні функції.  Emacs надає команди для
+читання документації про команди Emacs.  Усі команди довідки (help)
+починаються з CONTROL-h — символу довідки.
+
+Для отримання довідки наберіть символ C-h, а тоді той символ, довідку
+якого бажаєте прочитати.  Якщо ви СПРАВДІ загубились, C-h ? попрохає
+Emacs перелічити розділи довідки.  Якщо ви набрали C-h і передумали
+читати довідку, натисніть C-g для скасування.
+
+(Якщо C-h не показує довідкового повідомлення внизу екрана, спробуйте
+набрати клавішу F1 чи M-x help <Return> натомість.)
+
+Основна функція довідки — це C-h c.  Наберіть C-h, символ c, а також
+символ чи послідовність команди; тоді Emacs покаже коротенький
+англійський опис цієї команди.
+
+»» Наберіть C-h c C-p.
+
+Має з'явитись повідомлення на кшталт:
+
+        C-p runs the command previous-line
+
+Тобто «C-p виконує команду previous-line (попередній рядок)».  Це
+повідомляє вам назву функції.  Оскільки назви функцій добираються
+таким чином, щоб зображати дії, виконувані відповідною командою,
+вони можуть слугувати коротенькою документацією — достатньою, щоб
+нагадати вам команди, які ви вже опанували.
+
+Багатосимвольні команди, такі як C-x C-s чи <ESC>v (замість M-v, якщо
+не маєте клавіш META й ALT), також можна зазначати після C-h c.
+
+Щоб дізнатися більше про команду, зокрема про клавіші, які можна
+набирати після неї, використайте C-h k (keys — клавіші) замість C-h c
+(command — команда).
+
+»» Наберіть C-h k C-p.
+
+Це покаже у вікні Emacs документацію функції та її назву.  Дочитавши
+показане, наберіть C-x 1, щоб позбутись того вікна.  Робити це одразу
+необов'язково.  Можете продовжити редагування, звіряючись із текстом
+довідки, й набрати C-x 1 лише згодом.
+
+Ось кілька інших корисних варіантів C-h:
+
+   C-h x        Описати команду.  Вам буде треба набрати назву команди.
+
+»» Спробуйте набрати C-h x previous-line <Return>.
+   Це покаже всі наявні в Emacs дані про ту функцію,
+   яка втілює команду C-p.
+
+Схожа команда C-h v показує документацію змінних — зокрема тих,
+значення яких ви можете змінити для налаштування поведінки Emacs.
+Наберіть назву змінної, коли Emacs її у вас запитає.
+
+   C-h a        (Apropos — пов'язане.)  Наберіть ключове слово — й
+                Emacs покаже всі команди, назви яких містять це слово.
+                Всі ці команди можна викликати за допомогою META-x.
+                Для деяких команд Apropos наводить також символи чи
+                послідовності, які виконують цю ж команду.
+
+»» Наберіть C-h a file <Return>.
+
+Це покаже в ще одному вікні перелік усіх команд M-x, назви яких
+містять «file» — файл.  Поруч із назвами команд ви побачите відповідні
+їм символи (як-от C-x C-f поруч із find-file — знайти файл).
+
+»» Наберіть C-M-v, щоб прогорнути вікно довідки.
+   Повторіть це декілька разів.
+
+»» Наберіть C-x 1, щоб видалити вікно довідки.
+
+   C-h i        (Info — дані.)  Читати вкладені підручники.  Ця команда
+                веде вас до особливого буфера *info*, в якому ви можете
+                читати підручники про встановлені у вашій системі пакунки.
+                Наберіть m emacs <Return>, щоб почитати підручник Emacs.
+                Якщо ви досі не користувались Info, наберіть h — і Emacs
+                запустить для вас путівник можливостями режиму Info.
+                Опрацювавши цей посібник, звертайтесь до Info-підручника
+                Emacs як до основної документації.
+
+
+* БІЛЬШЕ ФУНКЦІЙ
+----------------
+
+Щоб дізнатись більше про Emacs, почитайте його підручник усередині
+Emacs, скориставшись меню Help (Довідка) чи набравши C-h r.  Дві
+функції, які можуть вас особливо зацікавити, це Completion —
+доповнення, яке заощаджує натиски клавіш; і Dired — редагування
+каталогів, яке спрощує керування файлами.
+
+Доповнення дає змогу набирати менше тексту.  Наприклад, щоб
+перемкнутись на буфер *Messages*, можете набрати C-x b *M<Tab> — і
+Emacs заповнить решту назви буфера настільки, наскільки зможе вивести
+з уже набраного вами тексту.  Доповнення також працює з назвами команд
+і файлів.  Підручник Emacs описує доповнення в розділі «Completion».
+
+Dired дає змогу перелічувати файли в каталозі (а на вимогу ще й у
+підкаталогах), рухатися цим переліком, відкривати, перейменовувати й
+видаляти файли тощо.  Підручник Emacs описує керування файлами в
+розділі «Dired».
+
+Підручник також описує безліч інших функцій Emacs.
+
+
+* ВСТАНОВЛЕННЯ ПАКУНКІВ
+-----------------------
+
+Спільнотою Emacs розроблено величезну добірку пакунків, які роблять
+Emacs потужнішим.  Ці пакунки містять підтримку нових мов, теми
+оформлення, додатки для взаємодії з іншими застосунками — й ще
+багато-багато всього.
+
+Щоб переглянути перелік усіх доступних пакунків, наберіть M-x
+list-packages.  З'явиться екран, за допомогою якого ви можете
+встановлювати й видаляти пакунки, а також читати описи пакунків.
+Підручник Emacs розповідає про керування пакунками докладніше.
+
+
+* ОТОЖ
+------
+
+Щоб вийти з Emacs, наберіть C-x C-c.
+
+Посібник має на меті бути зрозумілим усім новим користувачкам і
+користувачам, тож якщо будь-що роз'яснено нечітко чи помилково —
+скаржтесь!  Український переклад: Денис Никула <vegan@libre.net.ua>.
+
+
+* КОПІЮВАННЯ
+------------
+
+Цей посібник — нащадок цілого родоводу посібників Emacs, починаючи з
+первісного посібника Emacs, який написав Стюарт Кракрафт.
+
+Ця версія посібника — складник GNU Emacs.  Її захищено авторським
+правом.  Розповсюджувати копії дозволено за певних умов:
+
+  Copyright (C) 1985, 1996, 1998, 2001-2022 Free Software Foundation,
+  Inc.  (Фонд вільного програмного забезпечення, Inc.)
+
+  Цей файл — складник GNU Emacs.
+
+  GNU Emacs — вільне програмне забезпечення: можете розповсюджувати
+  й/або змінювати його згідно з умовами Загальної громадської ліцензії
+  GNU в тому вигляді, в якому її публікує Фонд вільного програмного
+  забезпечення, — версії 3 чи (на ваш розсуд) будь-якої новішої.
+
+  GNU Emacs поширюється зі сподіванням, що він стане в нагоді, але БЕЗ
+  ЖОДНИХ ГАРАНТІЙ; без навіть уявної гарантії КОМЕРЦІЙНОЇ ПРИДАТНОСТІ
+  чи ВІДПОВІДНОСТІ БУДЬ-ЯКОМУ ПЕВНОМУ ЗАСТОСУВАННЮ.  Докладніше про це
+  йдеться в Загальній громадській ліцензії GNU.
+
+  Ви мали отримати копію Загальної громадської ліцензії GNU разом із
+  GNU Emacs.  Якщо ні, перегляньте <https://www.gnu.org/licenses/>.
+
+Будь ласка, прочитайте файл COPYING і поширте після цього копії
+GNU Emacs вашим подругам і друзям.  Зупинімо гальмування розвитку
+програмного забезпечення (так звану «власність» над ним),
+використовуючи, пишучи й поширюючи вільні програми!
diff --git a/leim/Makefile.in b/leim/Makefile.in
index 29b9f3b2f8..fbd733b7f6 100644
--- a/leim/Makefile.in
+++ b/leim/Makefile.in
@@ -128,7 +128,6 @@ leim-list.el: ${leimdir}/leim-list.el
 ${leimdir}/leim-list.el: ${srcdir}/leim-ext.el ${TIT_MISC}
        $(AM_V_GEN)rm -f $@
        $(AM_V_at)${RUN_EMACS} -l international/quail \
-         --eval "(setq max-specpdl-size 5000)" \
          --eval "(update-leim-list-file (unmsys--file-name \"${leimdir}\"))"
        $(AM_V_at)sed -n -e '/^[^;]/p' -e 's/^;\(;*\)inc /;\1 /p' < $< >> $@
 
@@ -139,7 +138,6 @@ ${leimdir}/ja-dic/ja-dic.el: | $(leimdir)/ja-dic
 generate-ja-dic: ${leimdir}/ja-dic/ja-dic.el
 ${leimdir}/ja-dic/ja-dic.el: $(srcdir)/SKK-DIC/SKK-JISYO.L
        $(AM_V_GEN)$(RUN_EMACS) -batch -l ja-dic-cnv \
-         --eval "(setq max-specpdl-size 5000)" \
          -f batch-skkdic-convert -dir "$(leimdir)/ja-dic" 
$(JA_DIC_NO_REDUCTION_OPTION) "$<"
 
 ${srcdir}/../lisp/language/pinyin.el: ${srcdir}/MISC-DIC/pinyin.map
diff --git a/lib-src/Makefile.in b/lib-src/Makefile.in
index cf4659fc2c..cfad3fc394 100644
--- a/lib-src/Makefile.in
+++ b/lib-src/Makefile.in
@@ -306,8 +306,8 @@ $(DESTDIR)${archlibdir}: all
        $(info $ )
        $(info Installing utilities run internally by Emacs.)
        umask 022 && ${MKDIR_P} "$(DESTDIR)${archlibdir}"
-       exp_archlibdir=`cd "$(DESTDIR)${archlibdir}" && /bin/pwd` && \
-       if [ "$$exp_archlibdir" != "`/bin/pwd`" ]; then \
+       exp_archlibdir=`cd "$(DESTDIR)${archlibdir}" && pwd -P` && \
+       if [ "$$exp_archlibdir" != "`pwd -P`" ]; then \
          for file in ${UTILITIES}; do \
            $(INSTALL_PROGRAM) $(INSTALL_STRIP) $$file \
              "$(DESTDIR)${archlibdir}/$$file" || exit; \
@@ -333,8 +333,8 @@ $(DESTDIR)${archlibdir}: all
         chmod u=rwx,g=rwx,o=rx "$(DESTDIR)${gamedir}"
     endif
   endif
-       exp_archlibdir=`cd "$(DESTDIR)${archlibdir}" && /bin/pwd` && \
-       if [ "$$exp_archlibdir" != "`cd ${srcdir} && /bin/pwd`" ]; then \
+       exp_archlibdir=`cd "$(DESTDIR)${archlibdir}" && pwd -P` && \
+       if [ "$$exp_archlibdir" != "`cd ${srcdir} && pwd -P`" ]; then \
          for file in ${SCRIPTS}; do \
            $(INSTALL_SCRIPT) ${srcdir}/$$file \
              "$(DESTDIR)${archlibdir}/$$file" || exit; \
diff --git a/lib-src/emacsclient.c b/lib-src/emacsclient.c
index 73c8e45a86..425db8cfac 100644
--- a/lib-src/emacsclient.c
+++ b/lib-src/emacsclient.c
@@ -1,6 +1,6 @@
 /* Client process that communicates with GNU Emacs acting as server.
 
-Copyright (C) 1986-1987, 1994, 1999-2022 Free Software Foundation, Inc.
+Copyright (C) 1986-2022 Free Software Foundation, Inc.
 
 This file is part of GNU Emacs.
 
@@ -66,6 +66,8 @@ char *w32_getenv (const char *);
 
 #endif /* !WINDOWSNT */
 
+#define DEFAULT_TIMEOUT (30)
+
 #include <ctype.h>
 #include <errno.h>
 #include <getopt.h>
@@ -144,6 +146,9 @@ static char const *socket_name;
 /* If non-NULL, the filename of the authentication file.  */
 static char const *server_file;
 
+/* Seconds to wait before timing out (0 means wait forever).  */
+static uintmax_t timeout;
+
 /* If non-NULL, the tramp prefix emacs must use to find the files.  */
 static char const *tramp_prefix;
 
@@ -178,6 +183,7 @@ static struct option const longopts[] =
   { "server-file",     required_argument, NULL, 'f' },
   { "display", required_argument, NULL, 'd' },
   { "parent-id", required_argument, NULL, 'p' },
+  { "timeout", required_argument, NULL, 'w' },
   { "tramp",   required_argument, NULL, 'T' },
   { 0, 0, 0, 0 }
 };
@@ -185,7 +191,7 @@ static struct option const longopts[] =
 /* Short options, in the same order as the corresponding long options.
    There is no '-p' short option.  */
 static char const shortopts[] =
-  "nqueHVtca:F:"
+  "nqueHVtca:F:w:"
 #ifdef SOCKETS_IN_FILE_SYSTEM
   "s:"
 #endif
@@ -497,6 +503,7 @@ decode_options (int argc, char **argv)
       if (opt < 0)
        break;
 
+      char* endptr;
       switch (opt)
        {
        case 0:
@@ -530,6 +537,17 @@ decode_options (int argc, char **argv)
          nowait = true;
          break;
 
+       case 'w':
+         timeout = strtoumax (optarg, &endptr, 10);
+         if (timeout <= 0 ||
+             ((timeout == INTMAX_MAX || timeout == INTMAX_MIN)
+              && errno == ERANGE))
+           {
+             fprintf (stderr, "Invalid timeout: \"%s\"\n", optarg);
+             exit (EXIT_FAILURE);
+           }
+         break;
+
        case 'e':
          eval = true;
          break;
@@ -671,6 +689,7 @@ The following OPTIONS are accepted:\n\
                        Set the parameters of a new frame\n\
 -e, --eval             Evaluate the FILE arguments as ELisp expressions\n\
 -n, --no-wait          Don't wait for the server to return\n\
+-w, --timeout          Seconds to wait before timing out\n\
 -q, --quiet            Don't display messages on success\n\
 -u, --suppress-output   Don't display return values from the server\n\
 -d DISPLAY, --display=DISPLAY\n\
@@ -1059,7 +1078,9 @@ set_tcp_socket (const char *local_server_file)
 
   /* The cast to 'const char *' is to avoid a compiler warning when
      compiling for MS-Windows sockets.  */
-  setsockopt (s, SOL_SOCKET, SO_LINGER, (const char *) &l_arg, sizeof l_arg);
+  int ret = setsockopt (s, SOL_SOCKET, SO_LINGER, (const char *) &l_arg, 
sizeof l_arg);
+  if (ret < 0)
+    sock_err_message ("setsockopt");
 
   /* Send the authentication.  */
   auth_string[AUTH_KEY_LENGTH] = '\0';
@@ -1870,6 +1891,43 @@ start_daemon_and_retry_set_socket (void)
   return emacs_socket;
 }
 
+static void
+set_socket_timeout (HSOCKET socket, int seconds)
+{
+  int ret;
+
+#ifndef WINDOWSNT
+  struct timeval timeout;
+  timeout.tv_sec = seconds;
+  timeout.tv_usec = 0;
+  ret = setsockopt (socket, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof timeout);
+#else
+  DWORD timeout;
+
+  if (seconds > INT_MAX / 1000)
+    timeout = INT_MAX;
+  else
+    timeout = seconds * 1000;
+  ret = setsockopt (socket, SOL_SOCKET, SO_RCVTIMEO, (char *) &timeout, sizeof 
timeout);
+#endif
+
+  if (ret < 0)
+    sock_err_message ("setsockopt");
+}
+
+static bool
+check_socket_timeout (int rl)
+{
+#ifndef WINDOWSNT
+  return (rl == -1)
+    && (errno == EAGAIN)
+    && (errno == EWOULDBLOCK);
+#else
+  return (rl == SOCKET_ERROR)
+    && (WSAGetLastError() == WSAETIMEDOUT);
+#endif
+}
+
 int
 main (int argc, char **argv)
 {
@@ -2086,19 +2144,40 @@ main (int argc, char **argv)
     }
   fflush (stdout);
 
+  set_socket_timeout (emacs_socket, timeout > 0 ? timeout : DEFAULT_TIMEOUT);
+  bool saw_response = false;
   /* Now, wait for an answer and print any messages.  */
   while (exit_status == EXIT_SUCCESS)
     {
+      bool retry = true;
+      bool msg_showed = quiet;
       do
        {
          act_on_signals (emacs_socket);
          rl = recv (emacs_socket, string, BUFSIZ, 0);
+         retry = check_socket_timeout (rl);
+         if (retry && !saw_response)
+           {
+             if (timeout > 0)
+               {
+                 /* Don't retry if we were given a --timeout flag.  */
+                 fprintf (stderr, "\nServer not responding; timed out after 
%ju seconds",
+                          timeout);
+                 retry = false;
+               }
+             else if (!msg_showed)
+               {
+                 msg_showed = true;
+                 fprintf (stderr, "\nServer not responding; use Ctrl+C to 
break");
+               }
+           }
        }
-      while (rl < 0 && errno == EINTR);
+      while ((rl < 0 && errno == EINTR) || retry);
 
       if (rl <= 0)
         break;
 
+      saw_response = true;
       string[rl] = '\0';
 
       /* Loop over all NL-terminated messages.  */
diff --git a/lib-src/seccomp-filter.c b/lib-src/seccomp-filter.c
index 9f0de7d64f..041bf5c749 100644
--- a/lib-src/seccomp-filter.c
+++ b/lib-src/seccomp-filter.c
@@ -39,7 +39,6 @@ variants of those files that can be used to sandbox Emacs 
before
 #include <errno.h>
 #include <limits.h>
 #include <stdarg.h>
-#include <stdbool.h>
 #include <stdlib.h>
 #include <stdint.h>
 #include <stdio.h>
@@ -60,7 +59,6 @@ variants of those files that can be used to sandbox Emacs 
before
 #include <unistd.h>
 
 #include <attribute.h>
-#include <verify.h>
 
 #ifndef ARCH_CET_STATUS
 #define ARCH_CET_STATUS 0x3001
@@ -71,19 +69,16 @@ fail (int error, const char *format, ...)
 {
   va_list ap;
   va_start (ap, format);
+  vfprintf (stderr, format, ap);
+  va_end (ap);
   if (error == 0)
-    {
-      vfprintf (stderr, format, ap);
-      fputc ('\n', stderr);
-    }
+    fputc ('\n', stderr);
   else
     {
-      char buffer[1000];
-      vsnprintf (buffer, sizeof buffer, format, ap);
+      fputs (": ", stderr);
       errno = error;
-      perror (buffer);
+      perror (NULL);
     }
-  va_end (ap);
   fflush (NULL);
   exit (EXIT_FAILURE);
 }
@@ -168,12 +163,12 @@ main (int argc, char **argv)
   set_attribute (SCMP_FLTATR_CTL_NNP, 1);
   set_attribute (SCMP_FLTATR_CTL_TSYNC, 1);
 
-  verify (CHAR_BIT == 8);
-  verify (sizeof (int) == 4 && INT_MIN == INT32_MIN
-          && INT_MAX == INT32_MAX);
-  verify (sizeof (long) == 8 && LONG_MIN == INT64_MIN
-          && LONG_MAX == INT64_MAX);
-  verify (sizeof (void *) == 8);
+  static_assert (CHAR_BIT == 8);
+  static_assert (sizeof (int) == 4 && INT_MIN == INT32_MIN
+                && INT_MAX == INT32_MAX);
+  static_assert (sizeof (long) == 8 && LONG_MIN == INT64_MIN
+                && LONG_MAX == INT64_MAX);
+  static_assert (sizeof (void *) == 8);
   assert ((uintptr_t) NULL == 0);
 
   /* Allow a clean exit.  */
@@ -183,8 +178,8 @@ main (int argc, char **argv)
   /* Allow `mmap' and friends.  This is necessary for dynamic loading,
      reading the portable dump file, and thread creation.  We don't
      allow pages to be both writable and executable.  */
-  verify (MAP_PRIVATE != 0);
-  verify (MAP_SHARED != 0);
+  static_assert (MAP_PRIVATE != 0);
+  static_assert (MAP_SHARED != 0);
   RULE (SCMP_ACT_ALLOW, SCMP_SYS (mmap),
         SCMP_A2_32 (SCMP_CMP_MASKED_EQ,
                     ~(PROT_NONE | PROT_READ | PROT_WRITE)),
@@ -256,9 +251,9 @@ main (int argc, char **argv)
 
   /* Allow opening files, assuming they are only opened for
      reading.  */
-  verify (O_WRONLY != 0);
-  verify (O_RDWR != 0);
-  verify (O_CREAT != 0);
+  static_assert (O_WRONLY != 0);
+  static_assert (O_RDWR != 0);
+  static_assert (O_CREAT != 0);
   RULE (SCMP_ACT_ALLOW, SCMP_SYS (open),
         SCMP_A1_32 (SCMP_CMP_MASKED_EQ,
                     ~(O_RDONLY | O_BINARY | O_CLOEXEC | O_PATH
diff --git a/lib/acl-internal.h b/lib/acl-internal.h
index 93533762dd..94553fab25 100644
--- a/lib/acl-internal.h
+++ b/lib/acl-internal.h
@@ -19,7 +19,6 @@
 
 #include "acl.h"
 
-#include <stdbool.h>
 #include <stdlib.h>
 
 /* All systems define the ACL related API in <sys/acl.h>.  */
diff --git a/lib/acl.h b/lib/acl.h
index f4d0df8061..0be6ef1cea 100644
--- a/lib/acl.h
+++ b/lib/acl.h
@@ -20,7 +20,6 @@
 #ifndef _GL_ACL_H
 #define _GL_ACL_H 1
 
-#include <stdbool.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 
diff --git a/lib/assert.in.h b/lib/assert.in.h
new file mode 100644
index 0000000000..2c358ba62e
--- /dev/null
+++ b/lib/assert.in.h
@@ -0,0 +1,27 @@
+/* Substitute for and wrapper around <assert.h>
+   Copyright (C) 2011-2022 Free Software Foundation, Inc.
+
+   This file is free software: you can redistribute it and/or modify
+   it under the terms of the GNU Lesser General Public License as
+   published by the Free Software Foundation; either version 2.1 of the
+   License, or (at your option) any later version.
+
+   This file is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public License
+   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
+
+/* Do not guard the include, since <assert.h> is supposed to define
+   the assert macro each time it is included.  */
+
+#if __GNUC__ >= 3
+@PRAGMA_SYSTEM_HEADER@
+#endif
+@PRAGMA_COLUMNS@
+
+#@INCLUDE_NEXT@ @NEXT_ASSERT_H@
+
+/* The definition of static_assert is copied here.  */
diff --git a/lib/c-ctype.h b/lib/c-ctype.h
index 1a4f603898..1202ff8a36 100644
--- a/lib/c-ctype.h
+++ b/lib/c-ctype.h
@@ -23,8 +23,6 @@
 #ifndef C_CTYPE_H
 #define C_CTYPE_H
 
-#include <stdbool.h>
-
 #ifndef _GL_INLINE_HEADER_BEGIN
  #error "Please include config.h first."
 #endif
diff --git a/lib/canonicalize-lgpl.c b/lib/canonicalize-lgpl.c
index a7fa7feb62..8c3d7f7cf8 100644
--- a/lib/canonicalize-lgpl.c
+++ b/lib/canonicalize-lgpl.c
@@ -30,7 +30,6 @@
 #include <errno.h>
 #include <fcntl.h>
 #include <limits.h>
-#include <stdbool.h>
 #include <string.h>
 #include <sys/stat.h>
 #include <unistd.h>
diff --git a/lib/cloexec.h b/lib/cloexec.h
index 7a22d77532..15d2d5efe2 100644
--- a/lib/cloexec.h
+++ b/lib/cloexec.h
@@ -15,8 +15,6 @@
    You should have received a copy of the GNU Lesser General Public License
    along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
 
-#include <stdbool.h>
-
 /* Set the 'FD_CLOEXEC' flag of DESC if VALUE is true,
    or clear the flag if VALUE is false.
    Return 0 on success, or -1 on error with 'errno' set.
diff --git a/lib/close-stream.c b/lib/close-stream.c
index 9b0e97b271..0fdca79bf8 100644
--- a/lib/close-stream.c
+++ b/lib/close-stream.c
@@ -20,7 +20,6 @@
 #include "close-stream.h"
 
 #include <errno.h>
-#include <stdbool.h>
 
 #include "fpending.h"
 
diff --git a/lib/count-leading-zeros.h b/lib/count-leading-zeros.h
index 354641af0a..4b4f5d4f9a 100644
--- a/lib/count-leading-zeros.h
+++ b/lib/count-leading-zeros.h
@@ -43,13 +43,17 @@ extern "C" {
 # define COUNT_LEADING_ZEROS(BUILTIN, MSC_BUILTIN, TYPE)                \
   return x ? BUILTIN (x) : CHAR_BIT * sizeof x;
 #elif _MSC_VER
-# pragma intrinsic _BitScanReverse
-# pragma intrinsic _BitScanReverse64
+# pragma intrinsic (_BitScanReverse)
+# if defined _M_X64
+#  pragma intrinsic (_BitScanReverse64)
+# endif
 # define COUNT_LEADING_ZEROS(BUILTIN, MSC_BUILTIN, TYPE)                \
     do                                                                  \
       {                                                                 \
         unsigned long result;                                           \
-        return MSC_BUILTIN (&result, x) ? result : CHAR_BIT * sizeof x; \
+        if (MSC_BUILTIN (&result, x))                                   \
+          return CHAR_BIT * sizeof x - 1 - result;                      \
+        return CHAR_BIT * sizeof x;                                     \
       }                                                                 \
     while (0)
 #else
@@ -109,8 +113,18 @@ count_leading_zeros_l (unsigned long int x)
 COUNT_LEADING_ZEROS_INLINE int
 count_leading_zeros_ll (unsigned long long int x)
 {
+#if (defined _MSC_VER && !defined __clang__) && !defined _M_X64
+  /* 32-bit MSVC does not have _BitScanReverse64, only _BitScanReverse.  */
+  unsigned long result;
+  if (_BitScanReverse (&result, (unsigned long) (x >> 32)))
+    return CHAR_BIT * sizeof x - 1 - 32 - result;
+  if (_BitScanReverse (&result, (unsigned long) x))
+    return CHAR_BIT * sizeof x - 1 - result;
+  return CHAR_BIT * sizeof x;
+#else
   COUNT_LEADING_ZEROS (__builtin_clzll, _BitScanReverse64,
                        unsigned long long int);
+#endif
 }
 
 #ifdef __cplusplus
diff --git a/lib/count-trailing-zeros.h b/lib/count-trailing-zeros.h
index 9a989a4324..61fbdf2980 100644
--- a/lib/count-trailing-zeros.h
+++ b/lib/count-trailing-zeros.h
@@ -43,8 +43,10 @@ extern "C" {
 # define COUNT_TRAILING_ZEROS(BUILTIN, MSC_BUILTIN, TYPE)               \
   return x ? BUILTIN (x) : CHAR_BIT * sizeof x;
 #elif _MSC_VER
-# pragma intrinsic _BitScanForward
-# pragma intrinsic _BitScanForward64
+# pragma intrinsic (_BitScanForward)
+# if defined _M_X64
+#  pragma intrinsic (_BitScanForward64)
+# endif
 # define COUNT_TRAILING_ZEROS(BUILTIN, MSC_BUILTIN, TYPE)               \
     do                                                                  \
       {                                                                 \
@@ -101,8 +103,18 @@ count_trailing_zeros_l (unsigned long int x)
 COUNT_TRAILING_ZEROS_INLINE int
 count_trailing_zeros_ll (unsigned long long int x)
 {
+#if (defined _MSC_VER && !defined __clang__) && !defined _M_X64
+  /* 32-bit MSVC does not have _BitScanForward64, only _BitScanForward.  */
+  unsigned long result;
+  if (_BitScanForward (&result, (unsigned long) x))
+    return result;
+  if (_BitScanForward (&result, (unsigned long) (x >> 32)))
+    return result + 32;
+  return CHAR_BIT * sizeof x;
+#else
   COUNT_TRAILING_ZEROS (__builtin_ctzll, _BitScanForward64,
                         unsigned long long int);
+#endif
 }
 
 #ifdef __cplusplus
diff --git a/lib/diffseq.h b/lib/diffseq.h
index 0f76ea1d5a..a8b0e7bd40 100644
--- a/lib/diffseq.h
+++ b/lib/diffseq.h
@@ -70,7 +70,6 @@
 
    Before including this file, you also need to include:
      #include <limits.h>
-     #include <stdbool.h>
      #include "minmax.h"
  */
 
diff --git a/lib/filevercmp.c b/lib/filevercmp.c
index 7e54793e61..844505a6bf 100644
--- a/lib/filevercmp.c
+++ b/lib/filevercmp.c
@@ -20,11 +20,9 @@
 #include <config.h>
 #include "filevercmp.h"
 
-#include <stdbool.h>
 #include <c-ctype.h>
 #include <limits.h>
 #include <idx.h>
-#include <verify.h>
 
 /* Return the length of a prefix of S that corresponds to the suffix
    defined by this extended regular expression in the C locale:
@@ -75,7 +73,7 @@ order (char const *s, idx_t pos, idx_t len)
     return -2;
   else
     {
-      verify (UCHAR_MAX <= (INT_MAX - 1 - 2) / 2);
+      static_assert (UCHAR_MAX <= (INT_MAX - 1 - 2) / 2);
       return c + UCHAR_MAX + 1;
     }
 }
diff --git a/lib/fsusage.h b/lib/fsusage.h
index 0443d19f92..27085b7b41 100644
--- a/lib/fsusage.h
+++ b/lib/fsusage.h
@@ -22,7 +22,6 @@
 # define FSUSAGE_H_
 
 # include <stdint.h>
-# include <stdbool.h>
 
 struct fs_usage
 {
diff --git a/lib/getloadavg.c b/lib/getloadavg.c
index 37e8280867..1fddee97af 100644
--- a/lib/getloadavg.c
+++ b/lib/getloadavg.c
@@ -82,7 +82,6 @@
 #include <stdlib.h>
 
 #include <errno.h>
-#include <stdbool.h>
 #include <stdio.h>
 
 # include <sys/types.h>
diff --git a/lib/getrandom.c b/lib/getrandom.c
index e146873093..c05a48167e 100644
--- a/lib/getrandom.c
+++ b/lib/getrandom.c
@@ -23,7 +23,6 @@
 
 #include <errno.h>
 #include <fcntl.h>
-#include <stdbool.h>
 #include <unistd.h>
 
 #if defined _WIN32 && ! defined __CYGWIN__
diff --git a/lib/gnulib.mk.in b/lib/gnulib.mk.in
index 5bb78740d6..04644bdabe 100644
--- a/lib/gnulib.mk.in
+++ b/lib/gnulib.mk.in
@@ -60,7 +60,6 @@
 #  --avoid=sigprocmask \
 #  --avoid=stat \
 #  --avoid=stdarg \
-#  --avoid=stdbool \
 #  --avoid=threadlib \
 #  --avoid=tzset \
 #  --avoid=unsetenv \
@@ -147,6 +146,7 @@
 #  stat-time \
 #  std-gnu11 \
 #  stdalign \
+#  stdbool \
 #  stddef \
 #  stdio \
 #  stpcpy \
@@ -180,6 +180,7 @@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
 APPLE_UNIVERSAL_BUILD = @APPLE_UNIVERSAL_BUILD@
 AR = @AR@
 ARFLAGS = @ARFLAGS@
+ASSERT_H = @ASSERT_H@
 AUTO_DEPEND = @AUTO_DEPEND@
 AWK = @AWK@
 BITSIZEOF_PTRDIFF_T = @BITSIZEOF_PTRDIFF_T@
@@ -307,6 +308,7 @@ GL_COND_OBJ_TIME_RZ_CONDITION = 
@GL_COND_OBJ_TIME_RZ_CONDITION@
 GL_COND_OBJ_TIME_R_CONDITION = @GL_COND_OBJ_TIME_R_CONDITION@
 GL_COND_OBJ_UTIMENSAT_CONDITION = @GL_COND_OBJ_UTIMENSAT_CONDITION@
 GL_GENERATE_ALLOCA_H_CONDITION = @GL_GENERATE_ALLOCA_H_CONDITION@
+GL_GENERATE_ASSERT_H_CONDITION = @GL_GENERATE_ASSERT_H_CONDITION@
 GL_GENERATE_BYTESWAP_H_CONDITION = @GL_GENERATE_BYTESWAP_H_CONDITION@
 GL_GENERATE_ERRNO_H_CONDITION = @GL_GENERATE_ERRNO_H_CONDITION@
 GL_GENERATE_EXECINFO_H_CONDITION = @GL_GENERATE_EXECINFO_H_CONDITION@
@@ -951,6 +953,8 @@ MKDIR_P = @MKDIR_P@
 MODULES_OBJ = @MODULES_OBJ@
 MODULES_SECONDARY_SUFFIX = @MODULES_SECONDARY_SUFFIX@
 MODULES_SUFFIX = @MODULES_SUFFIX@
+NEXT_ASSERT_H = @NEXT_ASSERT_H@
+NEXT_AS_FIRST_DIRECTIVE_ASSERT_H = @NEXT_AS_FIRST_DIRECTIVE_ASSERT_H@
 NEXT_AS_FIRST_DIRECTIVE_DIRENT_H = @NEXT_AS_FIRST_DIRECTIVE_DIRENT_H@
 NEXT_AS_FIRST_DIRECTIVE_ERRNO_H = @NEXT_AS_FIRST_DIRECTIVE_ERRNO_H@
 NEXT_AS_FIRST_DIRECTIVE_FCNTL_H = @NEXT_AS_FIRST_DIRECTIVE_FCNTL_H@
@@ -1440,6 +1444,39 @@ EXTRA_DIST += allocator.h
 endif
 ## end   gnulib module allocator
 
+## begin gnulib module assert-h
+ifeq (,$(OMIT_GNULIB_MODULE_assert-h))
+
+BUILT_SOURCES += $(ASSERT_H)
+
+# We need the following in order to create <assert.h> when the system
+# doesn't have one that works with the given compiler.
+ifneq (,$(GL_GENERATE_ASSERT_H_CONDITION))
+assert.h: assert.in.h verify.h $(top_builddir)/config.status
+       $(gl_V_at){ $(SED_HEADER_STDOUT) \
+             -e 's|@''INCLUDE_NEXT''@|$(INCLUDE_NEXT)|g' \
+             -e 's|@''PRAGMA_SYSTEM_HEADER''@|@PRAGMA_SYSTEM_HEADER@|g' \
+             -e 's|@''PRAGMA_COLUMNS''@|@PRAGMA_COLUMNS@|g' \
+             -e 's|@''NEXT_ASSERT_H''@|$(NEXT_ASSERT_H)|g' \
+             < $(srcdir)/assert.in.h && \
+         sed -e '/@assert.h omit start@/,/@assert.h omit end@/d' \
+             -e 's|_gl_verify|_gl_static_assert|g' \
+             -e 's|_GL_VERIFY|_GL_STATIC_ASSERT|g' \
+             -e 's|_GL\(_STATIC_ASSERT_H\)|_GL\1|g' \
+             < $(srcdir)/verify.h; \
+       } > $@-t
+       $(AM_V_at)mv $@-t $@
+else
+assert.h: $(top_builddir)/config.status
+       rm -f $@
+endif
+MOSTLYCLEANFILES += assert.h assert.h-t
+
+EXTRA_DIST += assert.in.h verify.h
+
+endif
+## end   gnulib module assert-h
+
 ## begin gnulib module at-internal
 ifeq (,$(OMIT_GNULIB_MODULE_at-internal))
 
diff --git a/lib/malloc/dynarray.h b/lib/malloc/dynarray.h
index f16fd950df..df1aa4167d 100644
--- a/lib/malloc/dynarray.h
+++ b/lib/malloc/dynarray.h
@@ -94,7 +94,6 @@
 #ifndef _DYNARRAY_H
 #define _DYNARRAY_H
 
-#include <stdbool.h>
 #include <stddef.h>
 #include <string.h>
 
diff --git a/lib/md5.c b/lib/md5.c
index 57489ed74c..c16ac4a93a 100644
--- a/lib/md5.c
+++ b/lib/md5.c
@@ -27,7 +27,6 @@
 #endif
 #include "md5.h"
 
-#include <stdalign.h>
 #include <stdint.h>
 #include <string.h>
 #include <sys/types.h>
diff --git a/lib/mini-gmp.c b/lib/mini-gmp.c
index 95f067f82d..ea037b801d 100644
--- a/lib/mini-gmp.c
+++ b/lib/mini-gmp.c
@@ -1,8 +1,9 @@
 /* mini-gmp, a minimalistic implementation of a GNU GMP subset.
 
    Contributed to the GNU project by Niels Möller
+   Additional functionalities and improvements by Marco Bodrato.
 
-Copyright 1991-1997, 1999-2021 Free Software Foundation, Inc.
+Copyright 1991-1997, 1999-2022 Free Software Foundation, Inc.
 
 This file is part of the GNU MP Library.
 
@@ -3098,7 +3099,7 @@ mpz_powm (mpz_t r, const mpz_t b, const mpz_t e, const 
mpz_t m)
 
   if (en == 0)
     {
-      mpz_set_ui (r, 1);
+      mpz_set_ui (r, mpz_cmpabs_ui (m, 1));
       return;
     }
 
diff --git a/lib/nanosleep.c b/lib/nanosleep.c
index 446794edc0..55d6fa650e 100644
--- a/lib/nanosleep.c
+++ b/lib/nanosleep.c
@@ -23,9 +23,7 @@
 #include <time.h>
 
 #include "intprops.h"
-#include "verify.h"
 
-#include <stdbool.h>
 #include <stdio.h>
 #include <sys/types.h>
 #include <sys/select.h>
@@ -59,7 +57,7 @@ nanosleep (const struct timespec *requested_delay,
 
   {
     /* Verify that time_t is large enough.  */
-    verify (TYPE_MAXIMUM (time_t) / 24 / 24 / 60 / 60);
+    static_assert (TYPE_MAXIMUM (time_t) / 24 / 24 / 60 / 60);
     const time_t limit = 24 * 24 * 60 * 60;
     time_t seconds = requested_delay->tv_sec;
     struct timespec intermediate;
diff --git a/lib/nstrftime.c b/lib/nstrftime.c
index c1dd554247..37034eb9fb 100644
--- a/lib/nstrftime.c
+++ b/lib/nstrftime.c
@@ -65,7 +65,6 @@ extern char *tzname[];
 #include <stddef.h>
 #include <stdlib.h>
 #include <string.h>
-#include <stdbool.h>
 
 #include "attribute.h"
 #include <intprops.h>
diff --git a/lib/openat.h b/lib/openat.h
index 56919ef8dc..c2f64ff50e 100644
--- a/lib/openat.h
+++ b/lib/openat.h
@@ -24,7 +24,6 @@
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <unistd.h>
-#include <stdbool.h>
 
 #ifndef _GL_INLINE_HEADER_BEGIN
  #error "Please include config.h first."
diff --git a/lib/pipe2.c b/lib/pipe2.c
index 400aff001a..a3cbb7f261 100644
--- a/lib/pipe2.c
+++ b/lib/pipe2.c
@@ -23,7 +23,6 @@
 #include <fcntl.h>
 
 #include "binary-io.h"
-#include "verify.h"
 
 #if GNULIB_defined_O_NONBLOCK
 # include "nonblocking.h"
@@ -95,7 +94,7 @@ pipe2 (int fd[2], int flags)
     }
 # else
   {
-    verify (O_NONBLOCK == 0);
+    static_assert (O_NONBLOCK == 0);
   }
 # endif
 
diff --git a/lib/rawmemchr.c b/lib/rawmemchr.c
index ea68c1bfc6..bdd7307de4 100644
--- a/lib/rawmemchr.c
+++ b/lib/rawmemchr.c
@@ -23,10 +23,8 @@
 #if !HAVE_RAWMEMCHR
 
 # include <limits.h>
-# include <stdalign.h>
 # include <stdint.h>
 
-# include "verify.h"
 
 /* Find the first occurrence of C in S.  */
 void *
@@ -36,7 +34,7 @@ rawmemchr (const void *s, int c_in)
   typedef uintptr_t longword;
   /* If you change the "uintptr_t", you should change UINTPTR_WIDTH to match.
      This verifies that the type does not have padding bits.  */
-  verify (UINTPTR_WIDTH == UCHAR_WIDTH * sizeof (longword));
+  static_assert (UINTPTR_WIDTH == UCHAR_WIDTH * sizeof (longword));
 
   const unsigned char *char_ptr;
   unsigned char c = c_in;
diff --git a/lib/regex_internal.h b/lib/regex_internal.h
index 57a455b1f4..784d2d4586 100644
--- a/lib/regex_internal.h
+++ b/lib/regex_internal.h
@@ -29,7 +29,6 @@
 #include <locale.h>
 #include <wchar.h>
 #include <wctype.h>
-#include <stdbool.h>
 #include <stdint.h>
 
 #ifndef _LIBC
diff --git a/lib/sha1.c b/lib/sha1.c
index 79e50ba0b0..5a18213edc 100644
--- a/lib/sha1.c
+++ b/lib/sha1.c
@@ -29,7 +29,6 @@
 #endif
 #include "sha1.h"
 
-#include <stdalign.h>
 #include <stdint.h>
 #include <string.h>
 
diff --git a/lib/sha256.c b/lib/sha256.c
index c9ca618c67..60cd763612 100644
--- a/lib/sha256.c
+++ b/lib/sha256.c
@@ -28,7 +28,6 @@
 #endif
 #include "sha256.h"
 
-#include <stdalign.h>
 #include <stdint.h>
 #include <string.h>
 
diff --git a/lib/sha512.c b/lib/sha512.c
index 6776bb464d..fd17a7dc76 100644
--- a/lib/sha512.c
+++ b/lib/sha512.c
@@ -28,7 +28,6 @@
 #endif
 #include "sha512.h"
 
-#include <stdalign.h>
 #include <stdint.h>
 #include <string.h>
 
diff --git a/lib/signal.in.h b/lib/signal.in.h
index 640b5022f5..c0d4848db0 100644
--- a/lib/signal.in.h
+++ b/lib/signal.in.h
@@ -55,13 +55,21 @@
 #ifndef _@GUARD_PREFIX@_SIGNAL_H
 #define _@GUARD_PREFIX@_SIGNAL_H
 
-/* Mac OS X 10.3, FreeBSD 6.4, OpenBSD 3.8, OSF/1 4.0, Solaris 2.6, Android,
+/* For testing the OpenBSD version.  */
+#if (@GNULIB_PTHREAD_SIGMASK@ || defined GNULIB_POSIXCHECK) \
+    && defined __OpenBSD__
+# include <sys/param.h>
+#endif
+
+/* Mac OS X 10.3, FreeBSD < 8.0, OpenBSD < 5.1, OSF/1 4.0, Solaris 2.6, 
Android,
    OS/2 kLIBC declare pthread_sigmask in <pthread.h>, not in <signal.h>.
    But avoid namespace pollution on glibc systems.*/
 #if (@GNULIB_PTHREAD_SIGMASK@ || defined GNULIB_POSIXCHECK) \
     && ((defined __APPLE__ && defined __MACH__) \
-        || defined __FreeBSD__ || defined __OpenBSD__ || defined __osf__ \
-        || defined __sun || defined __ANDROID__ || defined __KLIBC__) \
+        || (defined __FreeBSD__ && __FreeBSD__ < 8) \
+        || (defined __OpenBSD__ && OpenBSD < 201205) \
+        || defined __osf__ || defined __sun || defined __ANDROID__ \
+        || defined __KLIBC__) \
     && ! defined __GLIBC__
 # include <pthread.h>
 #endif
diff --git a/lib/stdalign.in.h b/lib/stdalign.in.h
index 3b117df11f..58fd245c62 100644
--- a/lib/stdalign.in.h
+++ b/lib/stdalign.in.h
@@ -42,10 +42,7 @@
    '-malign-double' is used.
 
    The result cannot be used as a value for an 'enum' constant, if you
-   want to be portable to HP-UX 10.20 cc and AIX 3.2.5 xlc.
-
-   Include <stddef.h> for offsetof.  */
-#include <stddef.h>
+   want to be portable to HP-UX 10.20 cc and AIX 3.2.5 xlc.  */
 
 /* FreeBSD 9.1 <sys/cdefs.h>, included by <stddef.h> and lots of other
    standard headers, defines conflicting implementations of _Alignas
@@ -61,17 +58,19 @@
          && !defined __clang__) \
      || (defined __clang__ && __clang_major__ < 8))
 # ifdef __cplusplus
-#  if 201103 <= __cplusplus
+#  if (201103 <= __cplusplus || defined _MSC_VER)
 #   define _Alignof(type) alignof (type)
 #  else
    template <class __t> struct __alignof_helper { char __a; __t __b; };
 #   define _Alignof(type) offsetof (__alignof_helper<type>, __b)
+#   define _GL_STDALIGN_NEEDS_STDDEF 1
 #  endif
 # else
 #  define _Alignof(type) offsetof (struct { char __a; type __b; }, __b)
+#  define _GL_STDALIGN_NEEDS_STDDEF 1
 # endif
 #endif
-#if ! (defined __cplusplus && 201103 <= __cplusplus)
+#if ! (defined __cplusplus && (201103 <= __cplusplus || defined _MSC_VER))
 # define alignof _Alignof
 #endif
 #define __alignof_is_defined 1
@@ -102,7 +101,7 @@
    */
 
 #if !defined __STDC_VERSION__ || __STDC_VERSION__ < 201112
-# if defined __cplusplus && 201103 <= __cplusplus
+# if defined __cplusplus && (201103 <= __cplusplus || defined _MSC_VER)
 #  define _Alignas(a) alignas (a)
 # elif (!defined __attribute__ \
         && ((defined __APPLE__ && defined __MACH__ \
@@ -116,12 +115,19 @@
 #  define _Alignas(a) __declspec (align (a))
 # endif
 #endif
-#if ((defined _Alignas && ! (defined __cplusplus && 201103 <= __cplusplus)) \
+#if ((defined _Alignas \
+      && !(defined __cplusplus && (201103 <= __cplusplus || defined 
_MSC_VER))) \
      || (defined __STDC_VERSION__ && 201112 <= __STDC_VERSION__))
 # define alignas _Alignas
 #endif
-#if defined alignas || (defined __cplusplus && 201103 <= __cplusplus)
+#if (defined alignas \
+     || (defined __cplusplus && (201103 <= __cplusplus || defined _MSC_VER)))
 # define __alignas_is_defined 1
 #endif
 
+/* Include <stddef.h> if needed for offsetof.  */
+#if _GL_STDALIGN_NEEDS_STDDEF
+# include <stddef.h>
+#endif
+
 #endif /* _GL_STDALIGN_H */
diff --git a/lib/stdckdint.in.h b/lib/stdckdint.in.h
index 90fa62e596..762d3fdb79 100644
--- a/lib/stdckdint.in.h
+++ b/lib/stdckdint.in.h
@@ -20,8 +20,6 @@
 
 #include "intprops-internal.h"
 
-#include <stdbool.h>
-
 /* Store into *R the low-order bits of A + B, A - B, A * B, respectively.
    Return 1 if the result overflows, 0 otherwise.
    A, B, and *R can have any integer type other than char, bool, a
diff --git a/lib/stdlib.in.h b/lib/stdlib.in.h
index a86643c3ca..8e0a609f1f 100644
--- a/lib/stdlib.in.h
+++ b/lib/stdlib.in.h
@@ -226,7 +226,7 @@ _GL_FUNCDECL_SYS (aligned_alloc, void *,
 _GL_CXXALIAS_SYS (aligned_alloc, void *, (size_t alignment, size_t size));
 #  endif
 # endif
-# if @HAVE_ALIGNED_ALLOC@
+# if (__GLIBC__ >= 2) && @HAVE_ALIGNED_ALLOC@
 _GL_CXXALIASWARN (aligned_alloc);
 # endif
 #else
@@ -1363,7 +1363,9 @@ _GL_CXXALIAS_SYS (strtol, long,
                   (const char *restrict string, char **restrict endptr,
                    int base));
 # endif
+# if __GLIBC__ >= 2
 _GL_CXXALIASWARN (strtol);
+# endif
 #elif defined GNULIB_POSIXCHECK
 # undef strtol
 # if HAVE_RAW_DECL_STRTOL
@@ -1444,7 +1446,9 @@ _GL_CXXALIAS_SYS (strtoul, unsigned long,
                   (const char *restrict string, char **restrict endptr,
                    int base));
 # endif
+# if __GLIBC__ >= 2
 _GL_CXXALIASWARN (strtoul);
+# endif
 #elif defined GNULIB_POSIXCHECK
 # undef strtoul
 # if HAVE_RAW_DECL_STRTOUL
diff --git a/lib/string.in.h b/lib/string.in.h
index 3996da9fcb..e56f6db0c9 100644
--- a/lib/string.in.h
+++ b/lib/string.in.h
@@ -943,7 +943,9 @@ _GL_FUNCDECL_SYS (mbslen, size_t, (const char *string)
                                   _GL_ARG_NONNULL ((1)));
 _GL_CXXALIAS_SYS (mbslen, size_t, (const char *string));
 # endif
+# if __GLIBC__ >= 2
 _GL_CXXALIASWARN (mbslen);
+# endif
 #endif
 
 #if @GNULIB_MBSNLEN@
diff --git a/lib/strtoimax.c b/lib/strtoimax.c
index cad12d0d9b..29d16d29ce 100644
--- a/lib/strtoimax.c
+++ b/lib/strtoimax.c
@@ -25,8 +25,6 @@
 
 #include <stdlib.h>
 
-#include "verify.h"
-
 #ifdef UNSIGNED
 # ifndef HAVE_DECL_STRTOULL
 "this configure-time declaration test was not run"
@@ -62,8 +60,8 @@ long long int strtoll (char const *, char **, int);
 Int
 Strtoimax (char const *ptr, char **endptr, int base)
 {
-  verify (sizeof (Int) == sizeof (Unsigned long int)
-          || sizeof (Int) == sizeof (Unsigned long long int));
+  static_assert (sizeof (Int) == sizeof (Unsigned long int)
+                 || sizeof (Int) == sizeof (Unsigned long long int));
 
   if (sizeof (Int) != sizeof (Unsigned long int))
     return Strtoll (ptr, endptr, base);
diff --git a/lib/sys_random.in.h b/lib/sys_random.in.h
index e730e6139f..c91bcd2cd0 100644
--- a/lib/sys_random.in.h
+++ b/lib/sys_random.in.h
@@ -84,7 +84,9 @@ _GL_FUNCDECL_SYS (getrandom, ssize_t,
 _GL_CXXALIAS_SYS (getrandom, ssize_t,
                   (void *buffer, size_t length, unsigned int flags));
 # endif
+# if __GLIBC__ + (__GLIBC_MINOR__ >= 25) > 2
 _GL_CXXALIASWARN (getrandom);
+# endif
 #elif defined GNULIB_POSIXCHECK
 # undef getrandom
 # if HAVE_RAW_DECL_GETRANDOM
diff --git a/lib/sys_select.in.h b/lib/sys_select.in.h
index 2bd0e0f79a..860e957fe0 100644
--- a/lib/sys_select.in.h
+++ b/lib/sys_select.in.h
@@ -82,9 +82,10 @@
    of 'struct timeval', and no definition of this type.
    Also, Mac OS X, AIX, HP-UX, IRIX, Solaris, Interix declare select()
    in <sys/time.h>.
-   But avoid namespace pollution on glibc systems and "unknown type
-   name" problems on Cygwin.  */
-# if !(defined __GLIBC__ || defined __CYGWIN__)
+   But avoid namespace pollution on glibc systems, a circular include
+   <sys/select.h> -> <sys/time.h> -> <sys/select.h> on FreeBSD 13.1, and
+   "unknown type name" problems on Cygwin.  */
+# if !(defined __GLIBC__ || defined __FreeBSD__ || defined __CYGWIN__)
 #  include <sys/time.h>
 # endif
 
@@ -287,7 +288,9 @@ _GL_CXXALIAS_SYS_CAST (pselect, int,
                         struct timespec const *restrict,
                         const sigset_t *restrict));
 # endif
+# if __GLIBC__ >= 2
 _GL_CXXALIASWARN (pselect);
+# endif
 #elif defined GNULIB_POSIXCHECK
 # undef pselect
 # if HAVE_RAW_DECL_PSELECT
diff --git a/lib/sys_stat.in.h b/lib/sys_stat.in.h
index 714c3cb189..0ec320f58c 100644
--- a/lib/sys_stat.in.h
+++ b/lib/sys_stat.in.h
@@ -596,44 +596,6 @@ _GL_WARN_ON_USE (lchmod, "lchmod is unportable - "
 #endif
 
 
-#if @GNULIB_LSTAT@
-# if ! @HAVE_LSTAT@
-/* mingw does not support symlinks, therefore it does not have lstat.  But
-   without links, stat does just fine.  */
-#  if !(defined __cplusplus && defined GNULIB_NAMESPACE)
-#   define lstat stat
-#  endif
-_GL_CXXALIAS_RPL_1 (lstat, stat, int,
-                    (const char *restrict name, struct stat *restrict buf));
-# elif @REPLACE_LSTAT@
-#  if !(defined __cplusplus && defined GNULIB_NAMESPACE)
-#   undef lstat
-#   define lstat rpl_lstat
-#  endif
-_GL_FUNCDECL_RPL (lstat, int,
-                  (const char *restrict name, struct stat *restrict buf)
-                  _GL_ARG_NONNULL ((1, 2)));
-_GL_CXXALIAS_RPL (lstat, int,
-                  (const char *restrict name, struct stat *restrict buf));
-# else
-_GL_CXXALIAS_SYS (lstat, int,
-                  (const char *restrict name, struct stat *restrict buf));
-# endif
-# if @HAVE_LSTAT@
-_GL_CXXALIASWARN (lstat);
-# endif
-#elif @GNULIB_OVERRIDES_STRUCT_STAT@
-# undef lstat
-# define lstat lstat_used_without_requesting_gnulib_module_lstat
-#elif defined GNULIB_POSIXCHECK
-# undef lstat
-# if HAVE_RAW_DECL_LSTAT
-_GL_WARN_ON_USE (lstat, "lstat is unportable - "
-                 "use gnulib module lstat for portability");
-# endif
-#endif
-
-
 #if @GNULIB_MKDIR@
 # if @REPLACE_MKDIR@
 #  if !(defined __cplusplus && defined GNULIB_NAMESPACE)
@@ -895,6 +857,44 @@ _GL_WARN_ON_USE (stat, "stat is unportable - "
 #endif
 
 
+#if @GNULIB_LSTAT@
+# if ! @HAVE_LSTAT@
+/* mingw does not support symlinks, therefore it does not have lstat.  But
+   without links, stat does just fine.  */
+#  if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+#   define lstat stat
+#  endif
+_GL_CXXALIAS_RPL_1 (lstat, stat, int,
+                    (const char *restrict name, struct stat *restrict buf));
+# elif @REPLACE_LSTAT@
+#  if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+#   undef lstat
+#   define lstat rpl_lstat
+#  endif
+_GL_FUNCDECL_RPL (lstat, int,
+                  (const char *restrict name, struct stat *restrict buf)
+                  _GL_ARG_NONNULL ((1, 2)));
+_GL_CXXALIAS_RPL (lstat, int,
+                  (const char *restrict name, struct stat *restrict buf));
+# else
+_GL_CXXALIAS_SYS (lstat, int,
+                  (const char *restrict name, struct stat *restrict buf));
+# endif
+# if @HAVE_LSTAT@
+_GL_CXXALIASWARN (lstat);
+# endif
+#elif @GNULIB_OVERRIDES_STRUCT_STAT@
+# undef lstat
+# define lstat lstat_used_without_requesting_gnulib_module_lstat
+#elif defined GNULIB_POSIXCHECK
+# undef lstat
+# if HAVE_RAW_DECL_LSTAT
+_GL_WARN_ON_USE (lstat, "lstat is unportable - "
+                 "use gnulib module lstat for portability");
+# endif
+#endif
+
+
 #if @GNULIB_MDA_UMASK@
 /* On native Windows, map 'umask' to '_umask', so that -loldnames is not
    required.  In C++ with GNULIB_NAMESPACE, avoid differences between
diff --git a/lib/tempname.c b/lib/tempname.c
index 11b4796b34..dbff638f70 100644
--- a/lib/tempname.c
+++ b/lib/tempname.c
@@ -20,7 +20,6 @@
 # include "tempname.h"
 #endif
 
-#include <stdbool.h>
 #include <errno.h>
 
 #include <stdio.h>
diff --git a/lib/time.in.h b/lib/time.in.h
index 6d4c771963..6aa67498f5 100644
--- a/lib/time.in.h
+++ b/lib/time.in.h
@@ -435,8 +435,10 @@ _GL_WARN_ON_USE (asctime, "asctime can overrun buffers in 
some cases - "
 # endif
 # if defined GNULIB_POSIXCHECK
 #  undef asctime_r
+#  if HAVE_RAW_DECL_ASCTIME_R
 _GL_WARN_ON_USE (asctime_r, "asctime_r can overrun buffers in some cases - "
                  "better use strftime (or even sprintf) instead");
+#  endif
 # endif
 # if defined GNULIB_POSIXCHECK
 #  undef ctime
@@ -445,8 +447,10 @@ _GL_WARN_ON_USE (ctime, "ctime can overrun buffers in some 
cases - "
 # endif
 # if defined GNULIB_POSIXCHECK
 #  undef ctime_r
+#  if HAVE_RAW_DECL_CTIME_R
 _GL_WARN_ON_USE (ctime_r, "ctime_r can overrun buffers in some cases - "
                  "better use strftime (or even sprintf) instead");
+#  endif
 # endif
 
 #endif
diff --git a/lib/time_rz.c b/lib/time_rz.c
index 1a91d3778e..601ce5950e 100644
--- a/lib/time_rz.c
+++ b/lib/time_rz.c
@@ -27,7 +27,6 @@
 #include <time.h>
 
 #include <errno.h>
-#include <stdbool.h>
 #include <stddef.h>
 #include <stdlib.h>
 #include <string.h>
diff --git a/lib/unistd.in.h b/lib/unistd.in.h
index 57df09ecdf..50f6e56550 100644
--- a/lib/unistd.in.h
+++ b/lib/unistd.in.h
@@ -1143,7 +1143,9 @@ _GL_FUNCDECL_SYS (getdomainname, int, (char *name, size_t 
len)
 #  endif
 _GL_CXXALIAS_SYS (getdomainname, int, (char *name, size_t len));
 # endif
+# if __GLIBC__ >= 2
 _GL_CXXALIASWARN (getdomainname);
+# endif
 #elif defined GNULIB_POSIXCHECK
 # undef getdomainname
 # if HAVE_RAW_DECL_GETDOMAINNAME
@@ -2055,7 +2057,7 @@ _GL_CXXALIAS_MDA_CAST (swab, void, (char *from, char *to, 
int n));
 # else
 #  if defined __hpux /* HP-UX */
 _GL_CXXALIAS_SYS (swab, void, (const char *from, char *to, int n));
-#  elif defined __sun && !defined _XPG4 /* Solaris */
+#  elif defined __sun && (defined __SunOS_5_10 || defined __XOPEN_OR_POSIX) && 
!defined _XPG4 /* Solaris */
 _GL_CXXALIAS_SYS (swab, void, (const char *from, char *to, ssize_t n));
 #  else
 _GL_CXXALIAS_SYS (swab, void, (const void *from, void *to, ssize_t n));
diff --git a/lib/utimens.c b/lib/utimens.c
index 2fa1251850..23b9180935 100644
--- a/lib/utimens.c
+++ b/lib/utimens.c
@@ -26,7 +26,6 @@
 
 #include <errno.h>
 #include <fcntl.h>
-#include <stdbool.h>
 #include <string.h>
 #include <sys/stat.h>
 #include <sys/time.h>
diff --git a/lib/verify.h b/lib/verify.h
index 47b6ee661b..99af802993 100644
--- a/lib/verify.h
+++ b/lib/verify.h
@@ -25,19 +25,19 @@
    works as per C11.  This is supported by GCC 4.6.0+ and by clang 4+.
 
    Define _GL_HAVE__STATIC_ASSERT1 to 1 if _Static_assert (R) works as
-   per C2x.  This is supported by GCC 9.1+.
+   per C23.  This is supported by GCC 9.1+.
 
    Support compilers claiming conformance to the relevant standard,
    and also support GCC when not pedantic.  If we were willing to slow
    'configure' down we could also use it with other compilers, but
    since this affects only the quality of diagnostics, why bother?  */
 #ifndef __cplusplus
-# if (201112L <= __STDC_VERSION__ \
+# if (201112 <= __STDC_VERSION__ \
       || (!defined __STRICT_ANSI__ \
           && (4 < __GNUC__ + (6 <= __GNUC_MINOR__) || 5 <= __clang_major__)))
 #  define _GL_HAVE__STATIC_ASSERT 1
 # endif
-# if (202000L <= __STDC_VERSION__ \
+# if (202000 <= __STDC_VERSION__ \
       || (!defined __STRICT_ANSI__ && 9 <= __GNUC__))
 #  define _GL_HAVE__STATIC_ASSERT1 1
 # endif
@@ -202,12 +202,12 @@ template <int w>
 
    This macro requires three or more arguments but uses at most the first
    two, so that the _Static_assert macro optionally defined below supports
-   both the C11 two-argument syntax and the C2x one-argument syntax.
+   both the C11 two-argument syntax and the C23 one-argument syntax.
 
    Unfortunately, unlike C11, this implementation must appear as an
    ordinary declaration, and cannot appear inside struct { ... }.  */
 
-#if 200410 <= __cpp_static_assert
+#if 202311 <= __STDC_VERSION__ || 200410 <= __cpp_static_assert
 # define _GL_VERIFY(R, DIAGNOSTIC, ...) static_assert (R, DIAGNOSTIC)
 #elif defined _GL_HAVE__STATIC_ASSERT
 # define _GL_VERIFY(R, DIAGNOSTIC, ...) _Static_assert (R, DIAGNOSTIC)
@@ -223,11 +223,30 @@ template <int w>
 /* _GL_STATIC_ASSERT_H is defined if this code is copied into assert.h.  */
 #ifdef _GL_STATIC_ASSERT_H
 # if !defined _GL_HAVE__STATIC_ASSERT1 && !defined _Static_assert
-#  define _Static_assert(...) \
-     _GL_VERIFY (__VA_ARGS__, "static assertion failed", -)
+#  define _Static_assert(R, ...) \
+     _GL_VERIFY ((R), "static assertion failed", -)
 # endif
-# if __cpp_static_assert < 201411 && !defined static_assert
-#  define static_assert _Static_assert /* C11 requires this #define.  */
+# if (!defined static_assert \
+      && __STDC_VERSION__ < 202311 \
+      && (!defined __cplusplus \
+          || (__cpp_static_assert < 201411 \
+              && __GNUG__ < 6 && __clang_major__ < 6)))
+#  if defined __cplusplus && _MSC_VER >= 1900 && !defined __clang__
+/* MSVC 14 in C++ mode supports the two-arguments static_assert but not
+   the one-argument static_assert, and it does not support _Static_assert.
+   We have to play preprocessor tricks to distinguish the two cases.
+   Since the MSVC preprocessor is not ISO C compliant (cf.
+   <https://stackoverflow.com/questions/5134523/>), the solution is specific
+   to MSVC.  */
+#   define _GL_EXPAND(x) x
+#   define _GL_SA1(a1) static_assert ((a1), "static assertion failed")
+#   define _GL_SA2 static_assert
+#   define _GL_SA3 static_assert
+#   define _GL_SA_PICK(x1,x2,x3,x4,...) x4
+#   define static_assert(...) 
_GL_EXPAND(_GL_SA_PICK(__VA_ARGS__,_GL_SA3,_GL_SA2,_GL_SA1)) (__VA_ARGS__)
+#  else
+#   define static_assert _Static_assert /* C11 requires this #define. */
+#  endif
 # endif
 #endif
 
@@ -303,7 +322,7 @@ template <int w>
 # define assume(R) ((R) ? (void) 0 : __builtin_unreachable ())
 #elif 1200 <= _MSC_VER
 # define assume(R) __assume (R)
-#elif 202311L <= __STDC_VERSION__
+#elif 202311 <= __STDC_VERSION__
 # include <stddef.h>
 # define assume(R) ((R) ? (void) 0 : unreachable ())
 #elif (defined GCC_LINT || defined lint) && _GL_HAS_BUILTIN_TRAP
diff --git a/lisp/Makefile.in b/lisp/Makefile.in
index c73a623cce..256017f6c5 100644
--- a/lisp/Makefile.in
+++ b/lisp/Makefile.in
@@ -31,10 +31,16 @@ EXEEXT = @EXEEXT@
 XARGS_LIMIT = @XARGS_LIMIT@
 
 HAVE_NATIVE_COMP = @HAVE_NATIVE_COMP@
+NATIVE_COMPILATION_AOT = @NATIVE_COMPILATION_AOT@
 ifeq ($(HAVE_NATIVE_COMP),yes)
+# Environment variable to enable Ahead-Of-Time compilation.
 ifndef NATIVE_FULL_AOT
 NATIVE_SKIP_NONDUMP = 1
 endif
+# Configured for Ahead-Of-Time compilation.
+ifeq ($(NATIVE_COMPILATION_AOT),yes)
+NATIVE_SKIP_NONDUMP = ""
+endif
 endif
 
 -include ${top_builddir}/src/verbose.mk
@@ -70,9 +76,7 @@ BYTE_COMPILE_FLAGS = \
   --eval "(setq load-prefer-newer t byte-compile-warnings 'all)" \
        $(BYTE_COMPILE_EXTRA_FLAGS)
 # ... but we must prefer .elc files for those in the early bootstrap.
-# A larger `max-specpdl-size' is needed for emacs-lisp/comp.el.
-compile-first: BYTE_COMPILE_FLAGS = \
-  --eval '(setq max-specpdl-size 5000)' $(BYTE_COMPILE_EXTRA_FLAGS)
+compile-first: BYTE_COMPILE_FLAGS = $(BYTE_COMPILE_EXTRA_FLAGS)
 
 # Files to compile before others during a bootstrap.  This is done to
 # speed up the bootstrap process.  They're ordered by size, so we use
@@ -306,16 +310,18 @@ endif
 ifeq ($(HAVE_NATIVE_COMP),yes)
 ifeq ($(ANCIENT),yes)
 # The first compilation of compile-first, using an interpreted compiler:
-# The resulting .elc files get given a date of 1971-01-01 so that their
-# date stamp is earlier than the source files, causing these to be compiled
-# into native code at the second recursive invocation of this $(MAKE),
-# using these .elc's.  This is faster than just compiling the native code
-# directly using the interpreted compile-first files.  (Note: 1970-01-01
-# fails on some systems.)
+# The resulting .elc files get given a timestamp of the Unix epoch,
+# 1970-01-01, so that their timestamps are earlier than the source files,
+# causing these to be compiled into native code at the second recursive
+# invocation of this $(MAKE), using these .elc's.  This is faster than just
+# compiling the native code directly using the interpreted compile-first
+# files.  Note that the epoch date is hard-coded into Fload in src/lread.c
+# which uses it to avoid displaying certain messages which might be
+# irritating/misleading during a bootstrap.
 .el.elc:
        $(AM_V_ELC)$(emacs) $(BYTE_COMPILE_FLAGS) \
        -l comp -f batch-byte-compile $<
-       touch -t 197101010000 $@
+       TZ=UTC0 touch -t 197001010000 $@
 else
 .el.elc:
        $(AM_V_ELC)$(emacs) $(BYTE_COMPILE_FLAGS) \
@@ -342,8 +348,8 @@ compile-first: $(COMPILE_FIRST)
 
 .PHONY: compile-targets
 # TARGETS is set dynamically in the recursive call from 'compile-main'.
-# Do not build comp.el unless necessary not to exceed max-specpdl-size and
-# max-lisp-eval-depth in normal builds.
+# Do not build comp.el unless necessary not to exceed max-lisp-eval-depth
+# in normal builds.
 ifneq ($(HAVE_NATIVE_COMP),yes)
 compile-targets: $(filter-out ./emacs-lisp/comp-cstr.elc,$(filter-out 
./emacs-lisp/comp.elc,$(TARGETS)))
 else
diff --git a/lisp/abbrev.el b/lisp/abbrev.el
index 718938df0c..a4f0196a78 100644
--- a/lisp/abbrev.el
+++ b/lisp/abbrev.el
@@ -1,7 +1,6 @@
 ;;; abbrev.el --- abbrev mode commands for Emacs -*- lexical-binding: t -*-
 
-;; Copyright (C) 1985-1987, 1992, 2001-2022 Free Software Foundation,
-;; Inc.
+;; Copyright (C) 1985-2022 Free Software Foundation, Inc.
 
 ;; Maintainer: emacs-devel@gnu.org
 ;; Keywords: abbrev convenience
@@ -1220,13 +1219,28 @@ SORTFUN is passed to `sort' to change the default 
ordering."
            (sort entries (lambda (x y)
                            (funcall sortfun (nth 2 x) (nth 2 y)))))))
 
+(defface abbrev-table-name
+  '((t :inherit font-lock-function-name-face))
+  "Face used for displaying the abbrev table name in `edit-abbrev-mode'."
+  :version "29.1")
+
+(defvar edit-abbrevs-mode-font-lock-keywords
+  `((,(rx bol "("
+          ;; lisp-mode-symbol-regexp
+          (regexp "\\(?:\\sw\\|\\s_\\|\\\\.\\)+")
+          ")" eol)
+     0 'abbrev-table-name)))
+
 ;; Keep it after define-abbrev-table, since define-derived-mode uses
 ;; define-abbrev-table.
 (define-derived-mode edit-abbrevs-mode fundamental-mode "Edit-Abbrevs"
   "Major mode for editing the list of abbrev definitions.
 This mode is for editing abbrevs in a buffer prepared by `edit-abbrevs',
 which see."
-  :interactive nil)
+  :interactive nil
+  (setq-local font-lock-defaults
+              '(edit-abbrevs-mode-font-lock-keywords nil nil ((?_ . "w"))))
+  (setq font-lock-multiline nil))
 
 (defun abbrev--possibly-save (query &optional arg)
   ;; Query mode.
diff --git a/lisp/allout-widgets.el b/lisp/allout-widgets.el
index 736fb7d99d..7a65777d32 100644
--- a/lisp/allout-widgets.el
+++ b/lisp/allout-widgets.el
@@ -312,7 +312,7 @@ enhancements, directly.")
 (defvar-local allout-inhibit-body-modification-hook nil
   "Override de-escaping of text-prefixes in item bodies during specific 
changes.
 
-This is used by `allout-buffer-modification-handler' to signal such changes
+This is used by `allout-body-modification-handler' to signal such changes
 to `allout-body-modification-handler', and is always reset by
 `allout-post-command-business'.")
 ;;;_    = allout-widgets-icons-cache
@@ -2180,7 +2180,7 @@ Operation is inhibited by 
`allout-inhibit-body-modification-handler'."
 ;; `allout-before-modification-handler' and
 ;; `allout-inhibit-body-modification-handler'.
 ;;
-;; Adds the overlay to the `allout-unresolved-body-mod-workhash' during
+;; Adds the overlay to the `allout-unresolved-body-mod-workroster' during
 ;; before-change operation, and removes from that list during after-change
 ;; operation.
   (cond (allout-inhibit-body-modification-hook nil)))
diff --git a/lisp/allout.el b/lisp/allout.el
index fb922608b0..5f7087829e 100644
--- a/lisp/allout.el
+++ b/lisp/allout.el
@@ -1352,8 +1352,6 @@ their settings before `allout-mode' was started."
   "Symbol for use as allout invisible-text overlay category.")
 
 ;;;_   = allout-exposure-change-functions
-(define-obsolete-variable-alias 'allout-exposure-change-hook
-  'allout-exposure-change-functions "24.3")
 (defcustom allout-exposure-change-functions nil
   "Abnormal hook run after allout outline subtree exposure changes.
 It is run at the conclusion of `allout-flag-region'.
@@ -1370,8 +1368,6 @@ This hook might be invoked multiple times by a single 
command."
   :version "24.3")
 
 ;;;_   = allout-structure-added-functions
-(define-obsolete-variable-alias 'allout-structure-added-hook
-  'allout-structure-added-functions "24.3")
 (defcustom allout-structure-added-functions nil
   "Abnormal hook run after adding items to an Allout outline.
 Functions on the hook should take two arguments:
@@ -1385,8 +1381,6 @@ This hook might be invoked multiple times by a single 
command."
   :version "24.3")
 
 ;;;_   = allout-structure-deleted-functions
-(define-obsolete-variable-alias 'allout-structure-deleted-hook
-  'allout-structure-deleted-functions "24.3")
 (defcustom allout-structure-deleted-functions nil
   "Abnormal hook run after deleting subtrees from an Allout outline.
 Functions on the hook must take two arguments:
@@ -1403,8 +1397,6 @@ This hook might be invoked multiple times by a single 
command."
   :version "24.3")
 
 ;;;_   = allout-structure-shifted-functions
-(define-obsolete-variable-alias 'allout-structure-shifted-hook
-  'allout-structure-shifted-functions "24.3")
 (defcustom allout-structure-shifted-functions nil
   "Abnormal hook run after shifting items in an Allout outline.
 Functions on the hook should take two arguments:
diff --git a/lisp/ansi-osc.el b/lisp/ansi-osc.el
new file mode 100644
index 0000000000..34154998cd
--- /dev/null
+++ b/lisp/ansi-osc.el
@@ -0,0 +1,203 @@
+;;; ansi-osc.el --- Support for OSC escape sequences      -*- lexical-binding: 
t; -*-
+
+;; Copyright (C) 2022  Free Software Foundation, Inc.
+
+;; Author: Augusto Stoffel <arstoffel@gmail.com>
+;;         Matthias Meulien <orontee@gmail.com>
+;; Maintainer: emacs-devel@gnu.org
+;; Keywords: processes, terminals, services
+
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; Interpretation of OSC (Operating System Commands) escape sequences.
+;; Handlers for OSC 2, 7 and 8 (for window title, current directory
+;; and hyperlinks respectively) are provided.
+
+;; The function `ansi-osc-compilation-filter' can be added to
+;; `compilation-filter-hook' to collect OSC sequences in compilation
+;; buffers.  The variable `ansi-osc-for-compilation-buffer' tells what
+;; to do with collected sequences.
+
+;;; Code:
+
+(defconst ansi-osc-control-seq-regexp
+  ;; See ECMA 48, section 8.3.89 "OSC - OPERATING SYSTEM COMMAND".
+  "\e\\][\x08-\x0D]*[\x20-\x7E]*\\(\a\\|\e\\\\\\)"
+  "Regexp matching an OSC control sequence.")
+
+(defun ansi-osc-filter-region (begin end)
+  "Filter out all OSC control sequences from region between BEGIN and END."
+  (save-excursion
+    (goto-char begin)
+    ;; Delete escape sequences.
+    (while (re-search-forward ansi-osc-control-seq-regexp end t)
+      (delete-region (match-beginning 0) (match-end 0)))))
+
+(defvar-local ansi-osc-handlers '(("2" . ansi-osc-window-title-handler)
+                                  ("7" . ansi-osc-directory-tracker)
+                                  ("8" . ansi-osc-hyperlink-handler))
+  "Alist of handlers for OSC escape sequences.
+See `ansi-osc-apply-on-region' for details.")
+
+(defvar-local ansi-osc--marker nil)
+;; The function `ansi-osc-apply-on-region' can set `ansi-osc--marker'
+;; to the start position of an escape sequence without termination.
+
+(defun ansi-osc-apply-on-region (begin end)
+  "Interpret OSC escape sequences in region between BEGIN and END.
+This function searches for escape sequences of the forms
+
+    ESC ] command ; text BEL
+    ESC ] command ; text ESC \\
+
+Every occurrence of such escape sequences is removed from the
+buffer.  Then, if `command' is a key in the alist that is the
+value of the local variable `ansi-osc-handlers', that key's
+value, which should be a function, is called with `command' and
+`text' as arguments, with point where the escape sequence was
+located."
+  (save-excursion
+    (goto-char (or ansi-osc--marker begin))
+    (when (eq (char-before) ?\e) (backward-char))
+    (while (re-search-forward "\e]" end t)
+      (let ((pos0 (match-beginning 0))
+            (code (and (re-search-forward "\\=\\([0-9A-Za-z]*\\);" end t)
+                       (match-string 1)))
+            (pos1 (point)))
+        (if (re-search-forward "\a\\|\e\\\\" end t)
+            (let ((text (buffer-substring-no-properties
+                         pos1 (match-beginning 0))))
+              (setq ansi-osc--marker nil)
+              (delete-region pos0 (point))
+              (when-let ((fun (cdr (assoc-string code ansi-osc-handlers))))
+                (funcall fun code text)))
+          (put-text-property pos0 end 'invisible t)
+          (setq ansi-osc--marker (copy-marker pos0)))))))
+
+;; Window title handling (OSC 2)
+
+(defvar-local ansi-osc-window-title nil)
+(defun ansi-osc-window-title-handler (_ text)
+  "Set value of `ansi-osc-window-title' from an OSC 2 escape sequence.
+The variable `ansi-osc-window-title' can then be referenced in
+`frame-title-format' to dynamically set the frame title.
+
+This function is intended to be included as an element of the
+list that is the value of `ansi-osc-handlers'."
+  (setq ansi-osc-window-title text))
+
+;; Current directory tracking (OSC 7)
+
+(declare-function url-host "url/url-parse.el")
+(declare-function url-type "url/url-parse.el")
+(declare-function url-filename "url/url-parse.el")
+(defun ansi-osc-directory-tracker (_ text)
+  "Update `default-directory' from OSC 7 escape sequences.
+
+This function is intended to be included as an element of the
+the list that is the value of `ansi-osc-handlers'.  You should arrange
+for your shell to print the appropriate escape sequence at each prompt,
+such as with the following command:
+
+    printf \"\\e]7;file://%s%s\\e\\\\\" \"$HOSTNAME\" \"$PWD\"
+
+This functionality serves as an alternative to `dirtrack-mode'
+and `shell-dirtrack-mode'."
+  (let ((url (url-generic-parse-url text)))
+    (when (and (string= (url-type url) "file")
+               (or (null (url-host url))
+                   (string= (url-host url) (system-name))))
+      (ignore-errors
+        (cd-absolute (url-unhex-string (url-filename url)))))))
+
+;; Hyperlink handling (OSC 8)
+
+(defvar ansi-osc-hyperlink-map
+  (let ((map (make-sparse-keymap)))
+    (define-key map "\C-c\r" 'browse-url-button-open)
+    (define-key map [mouse-2] 'browse-url-button-open)
+    (define-key map [follow-link] 'mouse-face)
+    map)
+  "Keymap used by OSC 8 hyperlink buttons.")
+
+(define-button-type 'ansi-osc-hyperlink
+  'keymap ansi-osc-hyperlink-map
+  'help-echo (lambda (_ buffer pos)
+               (when-let ((url (get-text-property pos 'browse-url-data 
buffer)))
+                 (format "mouse-2, C-c RET: Open %s" url))))
+
+(defvar-local ansi-osc-hyperlink--state nil)
+
+(defun ansi-osc-hyperlink-handler (_ text)
+  "Create a hyperlink from an OSC 8 escape sequence.
+This function is intended to be included as an elemnt of the list
+that is the value of `ansi-osc-handlers'."
+  (when ansi-osc-hyperlink--state
+    (let ((start (car ansi-osc-hyperlink--state))
+          (url (cdr ansi-osc-hyperlink--state)))
+      (make-text-button start (point)
+                        'type 'ansi-osc-hyperlink
+                        'browse-url-data url)))
+  (setq ansi-osc-hyperlink--state
+        (and (string-match ";\\(.+\\)" text)
+             (cons (point-marker) (match-string-no-properties 1 text)))))
+
+(defgroup ansi-osc nil
+  "Interpretation of OSC escape sequences.
+Handlers for OSC 2, 7 and 8 (for window title, current directory
+and hyperlinks respectively) are provided.  OSC (Operating System
+Commands) control sequences are defined in section 8.3.89 of the
+ECMA-48 standard is freely available at
+<URL:https://www.ecma-international.org/publications/standards/Ecma-048.htm>
+as a PDF file."
+  :version "29.1"
+  :group 'processes)
+
+(defcustom ansi-osc-for-compilation-buffer 'filter
+  "What to do with OSC escape sequences in compilation output.
+
+If nil, do nothing.
+
+If the symbol `filter', then filter out all OSC control sequences.
+
+If any other non-nil value, then collect OSC control sequences
+and call the appropriate handlers as described in `ansi-osc-handlers'.
+
+In order for this to have any effect, `ansi-osc-compilation-filter'
+must be in `compilation-filter-hook'."
+  :type '(choice (const :tag "Do nothing" nil)
+                 (const :tag "Filter out OSC" filter)
+                 (other :tag "Translate OSC" t))
+  :group 'ansi-osc
+  :version "29.1")
+
+(defvar compilation-filter-start)
+
+;;;###autoload
+(defun ansi-osc-compilation-filter ()
+  "Maybe collect OSC control sequences.
+This function depends on the variable `ansi-osc-for-compilation-buffer',
+and is meant to be used in `compilation-filter-hook'."
+  (let ((inhibit-read-only t))
+    (pcase ansi-osc-for-compilation-buffer
+      ('nil nil)
+      ('filter
+       (ansi-osc-filter-region compilation-filter-start (point)))
+      (_
+       (ansi-osc-apply-on-region compilation-filter-start (point))))))
+
+(provide 'ansi-osc)
+;;; ansi-osc.el ends here
diff --git a/lisp/arc-mode.el b/lisp/arc-mode.el
index 632ae57852..b6f7794e33 100644
--- a/lisp/arc-mode.el
+++ b/lisp/arc-mode.el
@@ -125,8 +125,6 @@ A non-local file is one whose file name is not proper 
outside Emacs.
 A local copy of the archive will be used when updating."
   :type 'regexp)
 
-(define-obsolete-variable-alias 'archive-extract-hooks
-  'archive-extract-hook "24.3")
 (defcustom archive-extract-hook nil
   "Hook run when an archive member has been extracted."
   :type 'hook)
diff --git a/lisp/auth-source-pass.el b/lisp/auth-source-pass.el
index 86e0b48a79..0955e2ed07 100644
--- a/lisp/auth-source-pass.el
+++ b/lisp/auth-source-pass.el
@@ -319,6 +319,16 @@ then NAME & USER, then NAME & PORT, then just NAME."
     (list
      (format "%s" name)))))
 
+(defun auth-source-pass-file-name-p (file)
+  "Say whether FILE is used by `auth-source-pass'."
+  (and (stringp file) (stringp auth-source-pass-filename)
+       (string-equal
+        (expand-file-name file) (expand-file-name auth-source-pass-filename))))
+
+(with-eval-after-load 'bookmark
+  (add-hook 'bookmark-inhibit-context-functions
+           #'auth-source-pass-file-name-p))
+
 (provide 'auth-source-pass)
 ;;; auth-source-pass.el ends here
 
diff --git a/lisp/auth-source.el b/lisp/auth-source.el
index c79e5b81f7..feefd391a8 100644
--- a/lisp/auth-source.el
+++ b/lisp/auth-source.el
@@ -522,6 +522,21 @@ parameters."
 
 ;; (mapcar #'auth-source-backend-parse auth-sources)
 
+(defun auth-source-file-name-p (file)
+  "Say whether FILE is used by `auth-sources'."
+  (let* ((backends (mapcar #'auth-source-backend-parse auth-sources))
+         (files
+          (mapcar (lambda (x)
+                    (when (member (slot-value x 'type) '(json netrc plstore))
+                      (slot-value x 'source)))
+                  backends)))
+    (member (expand-file-name file)
+            (mapcar #'expand-file-name (remq nil files)))))
+
+(with-eval-after-load 'bookmark
+  (add-hook 'bookmark-inhibit-context-functions
+           #'auth-source-file-name-p))
+
 (cl-defun auth-source-search (&rest spec
                               &key max require create delete
                               &allow-other-keys)
diff --git a/lisp/autoinsert.el b/lisp/autoinsert.el
index 29d10bc629..580f6b3ced 100644
--- a/lisp/autoinsert.el
+++ b/lisp/autoinsert.el
@@ -1,7 +1,6 @@
 ;;; autoinsert.el --- automatic mode-dependent insertion of text into new 
files  -*- lexical-binding: t -*-
 
-;; Copyright (C) 1985-1987, 1994-1995, 1998, 2000-2022 Free Software
-;; Foundation, Inc.
+;; Copyright (C) 1985-2022 Free Software Foundation, Inc.
 
 ;; Author: Charlie Martin <crm@cs.duke.edu>
 ;; Adapted-By: Daniel Pfeiffer <occitan@esperanto.org>
@@ -25,27 +24,27 @@
 
 ;;; Commentary:
 
-;;  The following defines an association list for text to be
-;;  automatically inserted when a new file is created, and a function
-;;  which automatically inserts these files; the idea is to insert
-;;  default text much as the mode is automatically set using
-;;  auto-mode-alist.
+;; The following defines an association list for text to be
+;; automatically inserted when a new file is created, and a function
+;; which automatically inserts these files; the idea is to insert
+;; default text much as the mode is automatically set using
+;; auto-mode-alist.
+;;
+;; To use, add this to your Init file:
 ;;
-;;  To use:
 ;;     (auto-insert-mode t)
-;;     setq auto-insert-directory to an appropriate slash-terminated value
+;;     (setq auto-insert-directory "~/some-dir")
 ;;
-;;  You can also customize the variable `auto-insert-mode' to load the
-;;  package.  Alternatively, add the following to your init file:
-;;  (auto-insert-mode 1)
+;; You can also customize the variable `auto-insert-mode' to load the
+;; package.
 ;;
-;;  Author:  Charlie Martin
-;;           Department of Computer Science and
-;;           National Biomedical Simulation Resource
-;;           Box 3709
-;;           Duke University Medical Center
-;;           Durham, NC 27710
-;;           (crm@cs.duke.edu,mcnc!duke!crm)
+;; Author:  Charlie Martin
+;;          Department of Computer Science and
+;;          National Biomedical Simulation Resource
+;;          Box 3709
+;;          Duke University Medical Center
+;;          Durham, NC 27710
+;;           (crm@cs.duke.edu,mcnc!duke!crm)
 
 ;;; Code:
 
@@ -348,9 +347,7 @@ described above, e.g. [\"header.insert\" 
date-and-author-update]."
 
 ;; Establish a default value for auto-insert-directory
 (defcustom auto-insert-directory "~/insert/"
-  "Directory from which auto-inserted files are taken.
-The value must be an absolute directory name;
-thus, on a GNU or Unix system, it must end in a slash."
+  "Directory from which auto-inserted files are taken."
   :type 'directory)
 
 
diff --git a/lisp/autorevert.el b/lisp/autorevert.el
index 872a896689..576659675b 100644
--- a/lisp/autorevert.el
+++ b/lisp/autorevert.el
@@ -677,7 +677,7 @@ will use an up-to-date value of `auto-revert-interval'."
 ;;
 ;; We do this by reverting immediately in response to the first in a
 ;; flurry of notifications. Any notifications during the following
-;; `auto-revert-lockout-interval' seconds are noted but not acted upon
+;; `auto-revert--lockout-interval' seconds are noted but not acted upon
 ;; until the end of that interval.
 
 (defconst auto-revert--lockout-interval 2.5
diff --git a/lisp/bookmark.el b/lisp/bookmark.el
index 8dfc16bf9f..b57ad12986 100644
--- a/lisp/bookmark.el
+++ b/lisp/bookmark.el
@@ -592,9 +592,26 @@ NAME is a suggested name for the constructed bookmark.  It 
can be nil
 in which case a default heuristic will be used.  The function can also
 equivalently just return ALIST without NAME.")
 
+(defcustom bookmark-inhibit-context-functions nil
+  "List of functions to call before making a bookmark record.
+The functions take `buffer-file-name' as argument.  If any of
+these functions returns non-nil, the bookmark does not record
+context strings from the current buffer."
+  :type 'hook
+  :version "29.1")
+
 (defun bookmark-make-record ()
   "Return a new bookmark record (NAME . ALIST) for the current location."
-  (let ((record (funcall bookmark-make-record-function)))
+  (let* ((bookmark-search-size
+          ;; If we're in a buffer that's visiting an encrypted file,
+          ;; don't include any context in the bookmark file, because
+          ;; that would leak (possibly secret) data.
+          (if (and buffer-file-name
+                   (run-hook-with-args-until-success
+                    'bookmark-inhibit-context-functions buffer-file-name))
+              0
+            bookmark-search-size))
+         (record (funcall bookmark-make-record-function)))
     ;; Set up default name if the function does not provide one.
     (unless (stringp (car record))
       (if (car record) (push nil record))
@@ -1441,7 +1458,7 @@ name."
   (let ((final-new-name
          (or new-name   ; use second arg, if non-nil
              (read-from-minibuffer
-              "New name: "
+              (format-prompt "Rename \"%s\" to" nil old-name)
               nil
               (define-keymap
                 :parent minibuffer-local-map
diff --git a/lisp/buff-menu.el b/lisp/buff-menu.el
index 539ef673f0..abf152f058 100644
--- a/lisp/buff-menu.el
+++ b/lisp/buff-menu.el
@@ -54,21 +54,6 @@
   :group 'Buffer-menu)
 (put 'Buffer-menu-buffer 'face-alias 'buffer-menu-buffer)
 
-(defcustom Buffer-menu-buffer+size-width nil
-  "Combined width of buffer name and size columns in Buffer Menu.
-If nil, use `Buffer-menu-name-width' and `Buffer-menu-size-width'.
-
-If non-nil, the value of `Buffer-menu-name-width' is overridden;
-the name column is assigned width `Buffer-menu-buffer+size-width'
-minus `Buffer-menu-size-width'.  This use is deprecated."
-  :type '(choice (const nil) number)
-  :group 'Buffer-menu
-  :version "24.3")
-
-(make-obsolete-variable 'Buffer-menu-buffer+size-width
-                       "use `Buffer-menu-name-width' and 
`Buffer-menu-size-width' instead."
-                       "24.3")
-
 (defun Buffer-menu--dynamic-name-width (buffers)
   "Return a name column width based on the current window width.
 The width will never exceed the actual width of the buffer names,
@@ -679,9 +664,6 @@ means list those buffers and no others."
     (setq name-width (if (functionp Buffer-menu-name-width)
                          (funcall Buffer-menu-name-width (mapcar #'car 
entries))
                        Buffer-menu-name-width))
-    ;; Handle obsolete variable:
-    (if Buffer-menu-buffer+size-width
-       (setq name-width (- Buffer-menu-buffer+size-width size-width)))
     (setq tabulated-list-format
          (vector '("C" 1 t :pad-right 0)
                  '("R" 1 t :pad-right 0)
diff --git a/lisp/calc/calc-embed.el b/lisp/calc/calc-embed.el
index bb427ef86e..4a9ff256f9 100644
--- a/lisp/calc/calc-embed.el
+++ b/lisp/calc/calc-embed.el
@@ -207,9 +207,8 @@
 
 ;; The following is to take care of any minor modes which override
 ;; a Calc command.
-(defvar calc-override-minor-modes-map
-  (make-sparse-keymap)
-  "A list of keybindings that might be overwritten by minor modes.")
+(defvar-keymap calc-override-minor-modes-map
+  :doc "A list of keybindings that might be overwritten by minor modes.")
 
 ;; Add any keys that might be overwritten here.
 (define-key calc-override-minor-modes-map "`" 'calc-edit)
diff --git a/lisp/calc/calc-stuff.el b/lisp/calc/calc-stuff.el
index 0e8ea42bed..758b920184 100644
--- a/lisp/calc/calc-stuff.el
+++ b/lisp/calc/calc-stuff.el
@@ -52,18 +52,14 @@ With a prefix, push that prefix as a number onto the stack."
        (calc-less-recursion-depth n)
      (let ((n (if n (prefix-numeric-value n) 2)))
        (if (> n 1)
-          (setq max-specpdl-size (* max-specpdl-size n)
-                max-lisp-eval-depth (* max-lisp-eval-depth n))))
+          (setq max-lisp-eval-depth (* max-lisp-eval-depth n))))
      (message "max-lisp-eval-depth is now %d" max-lisp-eval-depth))))
 
 (defun calc-less-recursion-depth (n)
   (interactive "P")
   (let ((n (if n (prefix-numeric-value n) 2)))
     (if (> n 1)
-       (setq max-specpdl-size
-             (max (/ max-specpdl-size n) 600)
-             max-lisp-eval-depth
-             (max (/ max-lisp-eval-depth n) 200))))
+       (setq max-lisp-eval-depth (max (/ max-lisp-eval-depth n) 200))))
   (message "max-lisp-eval-depth is now %d" max-lisp-eval-depth))
 
 
diff --git a/lisp/calc/calc-yank.el b/lisp/calc/calc-yank.el
index 504ba5b40d..35014e022b 100644
--- a/lisp/calc/calc-yank.el
+++ b/lisp/calc/calc-yank.el
@@ -668,13 +668,11 @@ Interactively, reads the register using 
`register-read-with-preview'."
   (backward-char 1)
   (calc-set-command-flag 'do-edit))
 
-(defvar calc-edit-mode-map
-  (let ((map (make-sparse-keymap)))
-    (define-key map "\n" #'calc-edit-finish)
-    (define-key map "\r" #'calc-edit-return)
-    (define-key map "\C-c\C-c" #'calc-edit-finish)
-    map)
-  "Keymap for use by the `calc-edit' command.")
+(defvar-keymap calc-edit-mode-map
+  :doc "Keymap for use by the `calc-edit' command."
+  "C-j"     #'calc-edit-finish
+  "RET"     #'calc-edit-return
+  "C-c C-c" #'calc-edit-finish)
 
 (defvar calc-original-buffer nil)
 (defvar calc-return-buffer nil)
diff --git a/lisp/calc/calc.el b/lisp/calc/calc.el
index 6c21430b1b..6ea8a4202f 100644
--- a/lisp/calc/calc.el
+++ b/lisp/calc/calc.el
@@ -1162,7 +1162,7 @@ Used by `calc-user-invocation'.")
 
 
 ;;;; (Autoloads here)
-(load "calc-loaddefs.el" nil t)
+(load "calc-loaddefs" nil t)
 
 ;;;###autoload (define-key ctl-x-map "*" 'calc-dispatch)
 
@@ -1373,10 +1373,8 @@ Notations:  3.14e6     3.14 * 10^6
              (calc-check-defines))
          (setplist 'calc-define nil)))))
 
-(defvar calc-trail-mode-map
-  (let ((map (make-sparse-keymap)))
-    (set-keymap-parent map calc-mode-map)
-    map))
+(defvar-keymap calc-trail-mode-map
+  :parent calc-mode-map)
 
 (defun calc--header-line (long short width &optional fudge)
   "Return a Calc header line appropriate for the buffer WIDTH.
@@ -1627,8 +1625,7 @@ See calc-keypad for details."
          (error
           (if (and (eq (car err) 'error)
                    (stringp (nth 1 err))
-                   (string-match "max-specpdl-size\\|max-lisp-eval-depth"
-                                 (nth 1 err)))
+                   (string-search "max-lisp-eval-depth" (nth 1 err)))
                (error (substitute-command-keys
                        "Computation got stuck or ran too long.  Type \\`M' to 
increase the limit"))
             (setq calc-aborted-prefix nil)
diff --git a/lisp/calendar/cal-move.el b/lisp/calendar/cal-move.el
index 4febad53fc..211e0f1e62 100644
--- a/lisp/calendar/cal-move.el
+++ b/lisp/calendar/cal-move.el
@@ -384,7 +384,8 @@ Moves forward if ARG is negative."
 ;;;###cal-autoload
 (defun calendar-goto-day-of-year (year day &optional noecho)
   "Move cursor to YEAR, DAY number; echo DAY/YEAR unless NOECHO is non-nil.
-Negative DAY counts backward from end of year."
+Negative DAY counts backward from end of year.
+Interactively, prompt for YEAR and DAY number."
   (interactive
    (let* ((year (calendar-read-sexp
                  "Year (>0)"
@@ -394,7 +395,7 @@ Negative DAY counts backward from end of year."
           (day (calendar-read-sexp
                 "Day number (+/- 1-%d)"
                 (lambda (x) (and (<= 1 (abs x)) (<= (abs x) last)))
-                nil
+                (calendar-day-number (calendar-current-date))
                 last)))
      (list year day)))
   (calendar-goto-date
diff --git a/lisp/calendar/timeclock.el b/lisp/calendar/timeclock.el
index 6b6cc517a2..e36119984b 100644
--- a/lisp/calendar/timeclock.el
+++ b/lisp/calendar/timeclock.el
@@ -137,9 +137,6 @@ This variable only has effect if set with \\[customize]."
 (defvar timeclock-update-timer nil
   "The timer used to update `timeclock-mode-string'.")
 
-(define-obsolete-variable-alias 'timeclock-modeline-display
-  'timeclock-mode-line-display "24.3")
-
 ;; For byte-compiler.
 (defvar display-time-hook)
 (defvar timeclock-mode-line-display)
@@ -259,9 +256,6 @@ The time is bracketed by <> if you are clocked in, 
otherwise by [].")
 
 ;;; User Functions:
 
-(define-obsolete-function-alias 'timeclock-modeline-display
-  'timeclock-mode-line-display "24.3")
-
 ;;;###autoload
 (define-minor-mode timeclock-mode-line-display
   "Toggle display of the amount of time left today in the mode line.
@@ -612,9 +606,6 @@ arguments of `completing-read'."
   "Ask the user for the reason they are clocking out."
   (completing-read "Reason for clocking out: " timeclock-reason-list))
 
-(define-obsolete-function-alias 'timeclock-update-modeline
-  'timeclock-update-mode-line "24.3")
-
 (defun timeclock-update-mode-line ()
   "Update the `timeclock-mode-string' displayed in the mode line.
 The value of `timeclock-relative' affects the display as described in
diff --git a/lisp/cedet/ede/autoconf-edit.el b/lisp/cedet/ede/autoconf-edit.el
index 78edea1da8..8458820bdd 100644
--- a/lisp/cedet/ede/autoconf-edit.el
+++ b/lisp/cedet/ede/autoconf-edit.el
@@ -34,8 +34,7 @@
   "Initialize a new configure.ac in ROOTDIR for PROGRAM using TESTFILE.
 ROOTDIR is the root directory of a given autoconf controlled project.
 PROGRAM is the program to be configured.
-TESTFILE is the file used with AC_INIT.
-Configure the initial configure script using `autoconf-new-automake-string'."
+TESTFILE is the file used with AC_INIT."
   (interactive "DRoot Dir: \nsProgram: \nsTest File: ")
   (require 'ede/srecode)
   (if (bufferp rootdir)
diff --git a/lisp/cedet/pulse.el b/lisp/cedet/pulse.el
index 9941f2a0cb..0564cf6d04 100644
--- a/lisp/cedet/pulse.el
+++ b/lisp/cedet/pulse.el
@@ -202,12 +202,8 @@ If POINT is nil or missing, the current point is used 
instead.
 Optional argument FACE specifies the face to do the highlighting."
   (save-excursion
     (goto-char (or point (point)))
-    (let ((start (line-beginning-position))
-          (end (save-excursion
-                 (end-of-line)
-                 (when (not (eobp))
-                   (forward-char 1))
-                 (point))))
+    (let ((start (progn (vertical-motion 0) (point)))
+          (end (progn (vertical-motion 1) (point))))
       (pulse-momentary-highlight-region start end face))))
 
 ;;;###autoload
diff --git a/lisp/cedet/semantic/db-el.el b/lisp/cedet/semantic/db-el.el
index 02ebde4078..f72e206908 100644
--- a/lisp/cedet/semantic/db-el.el
+++ b/lisp/cedet/semantic/db-el.el
@@ -342,9 +342,6 @@ Return a list of tags."
             )
        taglst))))
 
-(define-obsolete-function-alias 'semanticdb-elisp-sym-function-arglist
-  #'help-function-arglist "24.3")
-
 (provide 'semantic/db-el)
 
 ;;; semantic/db-el.el ends here
diff --git a/lisp/cedet/semantic/db-file.el b/lisp/cedet/semantic/db-file.el
index e2c9d618ba..0fc6806e40 100644
--- a/lisp/cedet/semantic/db-file.el
+++ b/lisp/cedet/semantic/db-file.el
@@ -70,8 +70,6 @@ passes a list of predicates in 
`semanticdb-project-predicate-functions'."
   :type '(repeat (choice (string :tag "Directory") (const never) (const always)
                          (const project))))
 
-(define-obsolete-variable-alias 'semanticdb-save-database-hooks
-  'semanticdb-save-database-functions "24.3")
 (defcustom semanticdb-save-database-functions nil
   "Abnormal hook run after a database is saved.
 Each function is called with one argument, the object representing
diff --git a/lisp/cedet/semantic/ede-grammar.el 
b/lisp/cedet/semantic/ede-grammar.el
index ff9f991ff4..40ff8fc86d 100644
--- a/lisp/cedet/semantic/ede-grammar.el
+++ b/lisp/cedet/semantic/ede-grammar.el
@@ -177,10 +177,9 @@ Lays claim to all -by.el, and -wy.el files."
 
 (cl-defmethod ede-proj-makefile-insert-rules :after ((this 
semantic-ede-proj-target-grammar))
     "Insert rules needed by THIS target.
-This raises `max-specpdl-size' and `max-lisp-eval-depth', which can be
-needed for the compilation of the resulting parsers."
-    (insert (format "%s: EMACSFLAGS+= --eval '(setq max-specpdl-size 1500 \
-max-lisp-eval-depth 700)'\n"
+This raises `max-lisp-eval-depth', which can be needed for the compilation
+of the resulting parsers."
+    (insert (format "%s: EMACSFLAGS+= --eval '(setq max-lisp-eval-depth 
700)'\n"
                    (oref this name))))
 
 (cl-defmethod ede-proj-makefile-insert-dist-dependencies ((this 
semantic-ede-proj-target-grammar))
diff --git a/lisp/cedet/semantic/edit.el b/lisp/cedet/semantic/edit.el
index 4679500ed9..7cb6768f7e 100644
--- a/lisp/cedet/semantic/edit.el
+++ b/lisp/cedet/semantic/edit.el
@@ -72,8 +72,6 @@ updated in the current buffer.
 
 For language specific hooks, make sure you define this as a local hook.")
 
-(define-obsolete-variable-alias 'semantic-change-hooks
-  'semantic-change-functions "24.3")
 (defvar semantic-change-functions
   '(semantic-edits-change-function-handle-changes)
   "Abnormal hook run when semantic detects a change in a buffer.
@@ -91,14 +89,10 @@ If the hook returns non-nil, then declare that a reparse is 
needed.
 For language specific hooks, make sure you define this as a local hook.
 Not used yet; part of the next generation reparse mechanism.")
 
-(define-obsolete-variable-alias 'semantic-edits-new-change-hooks
-  'semantic-edits-new-change-functions "24.3")
 (defvar semantic-edits-new-change-functions nil
   "Abnormal hook run when a new change is found.
 Functions must take one argument representing an overlay on that change.")
 
-(define-obsolete-variable-alias 'semantic-edits-delete-change-hooks
-  'semantic-edits-delete-change-functions "24.3")
 (defvar semantic-edits-delete-change-functions nil
   "Abnormal hook run before a change overlay is deleted.
 Deleted changes occur when multiple changes are merged.
@@ -110,8 +104,6 @@ Changes move when a new change overlaps an old change.  The 
old change
 will be moved.
 Functions must take one argument representing an overlay being moved.")
 
-(define-obsolete-variable-alias 'semantic-edits-reparse-change-hooks
-  'semantic-edits-reparse-change-functions "24.3")
 (defvar semantic-edits-reparse-change-functions nil
   "Abnormal hook run after a change results in a reparse.
 Functions are called before the overlay is deleted, and after the
diff --git a/lisp/cedet/semantic/fw.el b/lisp/cedet/semantic/fw.el
index 113323cb33..9917c4c5be 100644
--- a/lisp/cedet/semantic/fw.el
+++ b/lisp/cedet/semantic/fw.el
@@ -339,7 +339,7 @@ calling this one."
   "Call `find-file-noselect' with various features turned off.
 Use this when referencing a file that will be soon deleted.
 FILE, NOWARN, RAWFILE, and WILDCARDS are passed into `find-file-noselect'."
-  (let* ((recentf-exclude #'always)
+  (let* ((recentf-exclude '(always))
         ;; This is a brave statement.  Don't waste time loading in
         ;; lots of modes.  Especially decoration mode can waste a lot
         ;; of time for a buffer we intend to kill.
diff --git a/lisp/cedet/semantic/grammar.el b/lisp/cedet/semantic/grammar.el
index d42022e042..8ba0e346ff 100644
--- a/lisp/cedet/semantic/grammar.el
+++ b/lisp/cedet/semantic/grammar.el
@@ -1009,7 +1009,6 @@ Return non-nil if there were no errors, nil if errors."
              packagename (byte-compile-dest-file packagename))
             (let (;; Some complex grammar table expressions need a few
                   ;; more resources than the default.
-                  (max-specpdl-size    (max 3000 max-specpdl-size))
                   (max-lisp-eval-depth (max 1000 max-lisp-eval-depth))
                   )
               ;; byte compile the resultant file
@@ -1148,9 +1147,9 @@ END is the limit of the search."
 
 (defvar semantic-grammar-mode-keywords-1
   `(("\\(\\<%%\\>\\|\\<%[{}]\\)"
-     0 font-lock-reference-face)
+     0 font-lock-constant-face)
     ("\\(%\\)\\(\\(\\sw\\|\\s_\\)+\\)"
-     (1 font-lock-reference-face)
+     (1 font-lock-constant-face)
      (2 font-lock-keyword-face))
     ("\\<error\\>"
      0 (unless (semantic-grammar-in-lisp-p) 'bold))
@@ -1167,7 +1166,8 @@ END is the limit of the search."
     (,semantic-grammar-lex-c-char-re
      0 ,(if (boundp 'font-lock-constant-face)
             'font-lock-constant-face
-          'font-lock-string-face) t)
+          'font-lock-string-face)
+     t)
     ;; Must highlight :keyword here, because ':' is a punctuation in
     ;; grammar mode!
     ("[\r\n\t ]+:\\sw+\\>"
diff --git a/lisp/cedet/semantic/lex.el b/lisp/cedet/semantic/lex.el
index 75c4ee328d..b3c9e96538 100644
--- a/lisp/cedet/semantic/lex.el
+++ b/lisp/cedet/semantic/lex.el
@@ -718,8 +718,6 @@ This is an alist of (ANCHOR . STREAM) elements where ANCHOR 
is the
 start position of the block, and STREAM is the list of tokens in that
 block.")
 
-(define-obsolete-variable-alias 'semantic-lex-reset-hooks
-  'semantic-lex-reset-functions "24.3")
 (defvar semantic-lex-reset-functions nil
   "Abnormal hook used by major-modes to reset lexical analyzers.
 Hook functions are called with START and END values for the
diff --git a/lisp/cedet/semantic/mru-bookmark.el 
b/lisp/cedet/semantic/mru-bookmark.el
index 9dee0415a3..c3f59a3358 100644
--- a/lisp/cedet/semantic/mru-bookmark.el
+++ b/lisp/cedet/semantic/mru-bookmark.el
@@ -264,11 +264,9 @@ been edited, and you can re-visit them with 
\\[semantic-mrub-switch-tags]."
   :group 'semantic
   :type 'hook)
 
-(defvar semantic-mru-bookmark-mode-map
-  (let ((km (make-sparse-keymap)))
-    (define-key km "\C-xB" #'semantic-mrub-switch-tags)
-    km)
-  "Keymap for mru-bookmark minor mode.")
+(defvar-keymap semantic-mru-bookmark-mode-map
+  :doc "Keymap for mru-bookmark minor mode."
+  "C-x B" #'semantic-mrub-switch-tags)
 
 (define-minor-mode semantic-mru-bookmark-mode
   "Minor mode for tracking tag-based bookmarks automatically.
diff --git a/lisp/cedet/semantic/util-modes.el 
b/lisp/cedet/semantic/util-modes.el
index 33fed9191e..96d1de5a26 100644
--- a/lisp/cedet/semantic/util-modes.el
+++ b/lisp/cedet/semantic/util-modes.el
@@ -196,10 +196,8 @@ Argument OVERLAY is the overlay created to mark the change.
 This function will set the face property on this overlay."
   (overlay-put overlay 'face 'semantic-highlight-edits-face))
 
-(defvar semantic-highlight-edits-mode-map
-  (let ((km (make-sparse-keymap)))
-    km)
-  "Keymap for highlight-edits minor mode.")
+(defvar-keymap semantic-highlight-edits-mode-map
+  :doc "Keymap for highlight-edits minor mode.")
 
 ;;;###autoload
 (define-minor-mode semantic-highlight-edits-mode
@@ -343,11 +341,9 @@ Do not search past BOUND if non-nil."
                 (setq ol (cdr ol))))))
       ol)))
 
-(defvar semantic-show-unmatched-syntax-mode-map
-  (let ((km (make-sparse-keymap)))
-    (define-key km "\C-c,`" #'semantic-show-unmatched-syntax-next)
-    km)
-  "Keymap for command `semantic-show-unmatched-syntax-mode'.")
+(defvar-keymap semantic-show-unmatched-syntax-mode-map
+  :doc "Keymap for command `semantic-show-unmatched-syntax-mode'."
+  "C-c , `" #'semantic-show-unmatched-syntax-next)
 
 ;;;###autoload
 (define-minor-mode semantic-show-unmatched-syntax-mode
@@ -417,10 +413,8 @@ non-nil if the minor mode is enabled.
   :group 'semantic
   :type 'hook)
 
-(defvar semantic-show-parser-state-mode-map
-  (let ((km (make-sparse-keymap)))
-    km)
-  "Keymap for show-parser-state minor mode.")
+(defvar-keymap semantic-show-parser-state-mode-map
+  :doc "Keymap for show-parser-state minor mode.")
 
 ;;;###autoload
 (define-minor-mode semantic-show-parser-state-mode
@@ -553,11 +547,9 @@ to indicate a parse in progress."
   :group 'semantic
   :type 'hook)
 
-(defvar semantic-stickyfunc-mode-map
-  (let ((km (make-sparse-keymap)))
-    (define-key km [ header-line down-mouse-1 ] #'semantic-stickyfunc-menu)
-    km)
-  "Keymap for stickyfunc minor mode.")
+(defvar-keymap semantic-stickyfunc-mode-map
+  :doc "Keymap for stickyfunc minor mode."
+  "<header-line> <down-mouse-1>" #'semantic-stickyfunc-menu)
 
 (defvar semantic-stickyfunc-popup-menu nil
   "Menu used if the user clicks on the header line used by stickyfunc mode.")
@@ -824,11 +816,9 @@ Argument EVENT describes the event that caused this 
function to be called."
   :group 'semantic
   :type 'hook)
 
-(defvar semantic-highlight-func-mode-map
-  (let ((km (make-sparse-keymap)))
-    (define-key km [mouse-3] #'semantic-highlight-func-menu)
-    km)
-  "Keymap for highlight-func minor mode.")
+(defvar-keymap semantic-highlight-func-mode-map
+  :doc "Keymap for highlight-func minor mode."
+  "<mouse-3>" #'semantic-highlight-func-menu)
 
 (defvar semantic-highlight-func-popup-menu nil
   "Menu used if the user clicks on the header line.
diff --git a/lisp/cedet/srecode/srt-mode.el b/lisp/cedet/srecode/srt-mode.el
index 724a6e0a94..cc0983f9f9 100644
--- a/lisp/cedet/srecode/srt-mode.el
+++ b/lisp/cedet/srecode/srt-mode.el
@@ -179,13 +179,11 @@ Don't scan past LIMIT."
 Once the escape_start, and escape_end sequences are known, then
 we can tell font lock about them.")
 
-(defvar srecode-template-mode-map
-  (let ((km (make-sparse-keymap)))
-    (define-key km "\C-c\C-c" #'srecode-compile-templates)
-    (define-key km "\C-c\C-m" #'srecode-macro-help)
-    (define-key km "/" #'srecode-self-insert-complete-end-macro)
-    km)
-  "Keymap used in srecode mode.")
+(defvar-keymap srecode-template-mode-map
+  :doc "Keymap used in srecode mode."
+  "C-c C-c" #'srecode-compile-templates
+  "C-c C-m" #'srecode-macro-help
+  "/"       #'srecode-self-insert-complete-end-macro)
 
 ;;;###autoload
 (define-derived-mode srecode-template-mode fundamental-mode "SRecode"
@@ -260,9 +258,9 @@ we can tell font lock about them.")
            (when (class-abstract-p C)
              (throw 'skip nil))
 
-           (princ (substitute-command-keys "`"))
+            (princ (substitute-quotes "`"))
            (princ name)
-           (princ (substitute-command-keys "'"))
+            (princ (substitute-quotes "'"))
            (when (slot-exists-p C 'key)
              (when key
                (princ " - Character Key: ")
diff --git a/lisp/char-fold.el b/lisp/char-fold.el
index 05ae52cae0..43e3cd45ec 100644
--- a/lisp/char-fold.el
+++ b/lisp/char-fold.el
@@ -24,6 +24,8 @@
 
 ;;; Code:
 
+(eval-when-compile (require 'subr-x))
+
 (eval-and-compile
   (put 'char-fold-table 'char-table-extra-slots 1)
   (defconst char-fold--default-override nil)
@@ -48,6 +50,7 @@
 
 
 (eval-and-compile
+  (defvar char-fold--no-regexp nil)
   (defun char-fold--make-table ()
     (let* ((equiv (make-char-table 'char-fold-table))
            (equiv-multi (make-char-table 'char-fold-table))
@@ -201,11 +204,14 @@
            symmetric)))
 
       ;; Convert the lists of characters we compiled into regexps.
-      (map-char-table
-       (lambda (char decomp-list)
-         (let ((re (regexp-opt (cons (char-to-string char) decomp-list))))
-           (aset equiv char re)))
-       equiv)
+      (unless char-fold--no-regexp
+        ;; Non-nil `char-fold--no-regexp' unoptimized for regexp
+        ;; is used by `describe-char-fold-equivalences'.
+        (map-char-table
+         (lambda (char decomp-list)
+           (let ((re (regexp-opt (cons (char-to-string char) decomp-list))))
+             (aset equiv char re)))
+         equiv))
       equiv)))
 
 (defconst char-fold-table
@@ -421,6 +427,68 @@ BOUND NOERROR COUNT are passed to `re-search-backward'."
   (interactive "sSearch: ")
   (re-search-backward (char-fold-to-regexp string) bound noerror count))
 
+
+;;;###autoload
+(defun describe-char-fold-equivalences (char &optional lax)
+  "Display characters equivalent to CHAR under character-folding.
+Prompt for CHAR (using `read-char-by-name', which see for how to
+specify the character).  With no input, i.e. when CHAR is nil,
+describe all available character equivalences of `char-fold-to-regexp'.
+Optional argument LAX (interactively, the prefix argument), if
+non-nil, means also include partially matching ligatures and
+non-canonical equivalences."
+  (interactive (list (ignore-errors
+                       (read-char-by-name
+                        (format-prompt "Unicode name, single char, or hex"
+                                       "all")
+                        t))
+                     current-prefix-arg))
+  (require 'help-fns)
+  (let ((help-buffer-under-preparation t))
+    (help-setup-xref (list #'describe-char-fold-equivalences)
+                     (called-interactively-p 'interactive))
+    (let* ((equivalences nil)
+           (char-fold--no-regexp t)
+           (table (char-fold--make-table))
+           (extra (char-table-extra-slot table 0)))
+      (if (not char)
+          (map-char-table
+           (lambda (char list)
+             (when lax
+               (setq list (append list (mapcar (lambda (entry)
+                                                 (cdr entry))
+                                               (aref extra char)))))
+             (setq equivalences (cons (cons char list)
+                                      equivalences)))
+           table)
+        (setq equivalences (aref table char))
+        (when lax
+          (setq equivalences (append equivalences
+                                     (mapcar (lambda (entry)
+                                               (cdr entry))
+                                             (aref extra char)))))
+        (setq equivalences (cons (char-to-string char) equivalences)))
+      (with-help-window (help-buffer)
+        (with-current-buffer standard-output
+          (if char
+              (insert
+               (mapconcat
+                (lambda (c)
+                  (format "%s: %s\n"
+                          c
+                          (mapconcat
+                           (lambda (ch)
+                             (format "?\\N{%s}"
+                                     (or (get-char-code-property ch 'name)
+                                         (get-char-code-property ch 
'old-name))))
+                           c)))
+                equivalences))
+            (insert "A list of char-fold equivalences for 
`char-fold-to-regexp':\n\n")
+            (setq-local bidi-paragraph-direction 'left-to-right)
+            (dolist (equiv (nreverse equivalences))
+              (insert (format "%c: %s\n" (car equiv)
+                              (string-join (cdr equiv) " "))))))))))
+
 (provide 'char-fold)
 
 ;;; char-fold.el ends here
diff --git a/lisp/comint.el b/lisp/comint.el
index 3ed04f098c..b1f3ad8259 100644
--- a/lisp/comint.el
+++ b/lisp/comint.el
@@ -103,6 +103,7 @@
 
 (require 'ring)
 (require 'ansi-color)
+(require 'ansi-osc)
 (require 'regexp-opt)                   ;For regexp-opt-charset.
 (eval-when-compile (require 'subr-x))
 
@@ -1944,6 +1945,7 @@ Similarly for Soar, Scheme, etc."
               (when comint-highlight-input
                 (add-text-properties beg end
                                      '( font-lock-face comint-highlight-input
+                                        
comint--fontify-input-inhibit-fontification t
                                         front-sticky t )))
               (unless comint-use-prompt-regexp
                 ;; Give old user input a field property of `input', to
@@ -2148,24 +2150,26 @@ Make backspaces delete the previous character."
            ;; insert-before-markers is a bad thing. XXX
            ;; Luckily we don't have to use it any more, we use
            ;; window-point-insertion-type instead.
-           (insert string)
+           (make-local-variable 'jit-lock-mode)
+           (let ((jit-lock-mode nil))
+             (insert string)
 
-           ;; Advance process-mark
-           (set-marker (process-mark process) (point))
+             ;; Advance process-mark
+             (set-marker (process-mark process) (point))
 
-           (unless comint-inhibit-carriage-motion
+             (unless comint-inhibit-carriage-motion
              ;; Interpret any carriage motion characters (newline, backspace)
              (comint-carriage-motion comint-last-output-start (point)))
 
-           ;; Run these hooks with point where the user had it.
-           (goto-char saved-point)
-           (run-hook-with-args 'comint-output-filter-functions string)
-           (set-marker saved-point (point))
+             ;; Run these hooks with point where the user had it.
+             (goto-char saved-point)
+             (run-hook-with-args 'comint-output-filter-functions string)
+             (set-marker saved-point (point))
 
-           (goto-char (process-mark process)) ; In case a filter moved it.
+             (goto-char (process-mark process)) ; In case a filter moved it.
 
-           (unless comint-use-prompt-regexp
-              (comint--mark-as-output comint-last-output-start (point)))
+             (unless comint-use-prompt-regexp
+                (comint--mark-as-output comint-last-output-start (point))))
 
            ;; Highlight the prompt, where we define `prompt' to mean
            ;; the most recent output that doesn't end with a newline.
@@ -3268,8 +3272,6 @@ See `comint-word'."
 
 (defun comint--unquote-argument (str)
   (car (comint--unquote&requote-argument str)))
-(define-obsolete-function-alias 'comint--unquote&expand-filename
-  #'comint--unquote-argument "24.3")
 
 (defun comint-match-partial-filename ()
   "Return the unquoted&expanded filename at point, or nil if none is found.
@@ -3290,14 +3292,6 @@ Magic characters are those in 
`comint-file-name-quote-list'."
            (setq i (1+ (match-end 0)))))
        filename))))
 
-(defun comint-unquote-filename (filename)
-  "Return FILENAME with quoted characters unquoted."
-  (declare (obsolete nil "24.3"))
-  (if (null comint-file-name-quote-list)
-      filename
-    (save-match-data
-      (replace-regexp-in-string "\\\\\\(.\\)" "\\1" filename t))))
-
 (defun comint--requote-argument (upos qstr)
   ;; See `completion-table-with-quoting'.
   (let ((res (comint--unquote&requote-argument qstr upos)))
@@ -3923,12 +3917,12 @@ REGEXP-GROUP is the regular expression group in REGEXP 
to use."
 ;; to `comint-osc-handlers' allows a customized treatment of further
 ;; sequences.
 
-(defvar-local comint-osc-handlers '(("7" . comint-osc-directory-tracker)
-                                    ("8" . comint-osc-hyperlink-handler))
-  "Alist of handlers for OSC escape sequences.
-See `comint-osc-process-output' for details.")
-
-(defvar-local comint-osc--marker nil)
+;; Aliases defined for reverse compatibility
+(defvaralias 'comint-osc-handlers 'ansi-osc-handlers)
+(defalias 'comint-osc-directory-tracker 'ansi-osc-directory-tracker)
+(defalias 'comint-osc-hyperlink-handler 'ansi-osc-hyperlink-handler)
+(defalias 'comint-osc-hyperlink 'ansi-osc-hyperlink)
+(defvaralias 'comint-osc-hyperlink-map 'ansi-osc-hyperlink-map)
 
 (defun comint-osc-process-output (_)
   "Interpret OSC escape sequences in comint output.
@@ -3944,83 +3938,321 @@ removed from the buffer.  Then, if `command' is a key 
of the
 `comint-osc-handlers' alist, the corresponding value, which
 should be a function, is called with `command' and `text' as
 arguments, with point where the escape sequence was located."
-  (let ((bound (process-mark (get-buffer-process (current-buffer)))))
-    (save-excursion
-      ;; Start one char before last output to catch a possibly stray ESC
-      (goto-char (or comint-osc--marker (1- comint-last-output-start)))
-      (when (eq (char-before) ?\e) (backward-char))
-      (while (re-search-forward "\e]" bound t)
-        (let ((pos0 (match-beginning 0))
-              (code (and (re-search-forward "\\=\\([0-9A-Za-z]*\\);" bound t)
-                         (match-string 1)))
-              (pos1 (point)))
-          (if (re-search-forward "\a\\|\e\\\\" bound t)
-              (let ((text (buffer-substring-no-properties
-                           pos1 (match-beginning 0))))
-                (setq comint-osc--marker nil)
-                (delete-region pos0 (point))
-                (when-let ((fun (cdr (assoc-string code comint-osc-handlers))))
-                  (funcall fun code text)))
-            (put-text-property pos0 bound 'invisible t)
-            (setq comint-osc--marker (copy-marker pos0))))))))
-
-;; Current directory tracking (OSC 7)
-
-(declare-function url-host "url/url-parse.el")
-(declare-function url-type "url/url-parse.el")
-(declare-function url-filename "url/url-parse.el")
-(defun comint-osc-directory-tracker (_ text)
-  "Update `default-directory' from OSC 7 escape sequences.
-
-This function is intended to be included as an entry of
-`comint-osc-handlers'.  You should moreover arrange for your
-shell to print the appropriate escape sequence at each prompt,
-say with the following command:
-
-    printf \"\\e]7;file://%s%s\\e\\\\\" \"$HOSTNAME\" \"$PWD\"
-
-This functionality serves as an alternative to `dirtrack-mode'
-and `shell-dirtrack-mode'."
-  (let ((url (url-generic-parse-url text)))
-    (when (and (string= (url-type url) "file")
-               (or (null (url-host url))
-                   (string= (url-host url) (system-name))))
-      (ignore-errors
-        (cd-absolute (url-unhex-string (url-filename url)))))))
-
-;; Hyperlink handling (OSC 8)
-
-(defvar comint-osc-hyperlink-map
-  (let ((map (make-sparse-keymap)))
-    (define-key map "\C-c\r" 'browse-url-button-open)
-    (define-key map [mouse-2] 'browse-url-button-open)
-    (define-key map [follow-link] 'mouse-face)
-    map)
-  "Keymap used by OSC 8 hyperlink buttons.")
-
-(define-button-type 'comint-osc-hyperlink
-  'keymap comint-osc-hyperlink-map
-  'help-echo (lambda (_ buffer pos)
-               (when-let ((url (get-text-property pos 'browse-url-data 
buffer)))
-                 (format "mouse-2, C-c RET: Open %s" url))))
-
-(defvar-local comint-osc-hyperlink--state nil)
-
-(defun comint-osc-hyperlink-handler (_ text)
-  "Create a hyperlink from an OSC 8 escape sequence.
-This function is intended to be included as an entry of
-`comint-osc-handlers'."
-  (when comint-osc-hyperlink--state
-    (let ((start (car comint-osc-hyperlink--state))
-          (url (cdr comint-osc-hyperlink--state)))
-      (make-text-button start (point)
-                        'type 'comint-osc-hyperlink
-                        'browse-url-data url)))
-  (setq comint-osc-hyperlink--state
-        (and (string-match ";\\(.+\\)" text)
-             (cons (point-marker) (match-string-no-properties 1 text)))))
+  (let ((start (1- comint-last-output-start))
+        ;; Start one char before last output to catch a possibly stray ESC
+        (bound (process-mark (get-buffer-process (current-buffer)))))
+    (ansi-osc-apply-on-region start bound)))
+
+
+;;; Input fontification and indentation through an indirect buffer
+;;============================================================================
+;;
+;; Modes derived from `comint-mode' can set up fontification and
+;; indentation of input text with the help of an indirect buffer whose
+;; major mode and font-lock settings are set accordingly.
+
+(defvar-local comint-indirect-setup-function nil
+  "Function to set up an indirect comint fontification buffer.
+This function is called by `comint-indirect-buffer' with zero
+arguments after making an indirect buffer.  It is usually set to
+a major-mode command whose font-locking and indentation are
+desired for input text.  In order to prevent possible mode hooks
+from running, the variable `delay-mode-hooks' is set to t prior
+to calling this function and `change-major-mode-hook' along with
+`after-change-major-mode-hook' are bound to nil.")
+
+(defcustom comint-indirect-setup-hook nil
+  "Hook run in an indirect buffer for input fontification.
+Input fontification and indentation, if enabled, is performed in
+an indirect buffer, whose major mode and syntax highlighting are
+set up according to `comint-indirect-setup-function'.  After this
+setup is done, run this hook with the indirect buffer as the
+current buffer.  This can be used to further customize
+fontification and other behavior of the indirect buffer."
+  :group 'comint
+  :type 'hook
+  :version "29.1")
+
+(defvar-local comint--indirect-buffer nil
+  "Indirect buffer used for input fontification.")
+
+(defvar-local comint--fontify-input-saved-jit-lock-contextually nil)
+
+(define-minor-mode comint-fontify-input-mode
+  "Enable input fontification in the current comint buffer.
+This minor mode is useful if the current major mode derives from
+`comint-mode' and if `comint-indirect-setup-function' is set.
+Comint modes that support input fontification usually set this
+variable buffer-locally to a major-mode command whose
+font-locking is desired for input text.
+
+Input text is fontified through an indirect buffer created with
+`comint-indirect-buffer', which see.
+
+This function signals an error if `comint-use-prompt-regexp' is
+non-nil.  Input fontification isn't compatible with this
+setting."
+  :lighter nil
+  (if comint-fontify-input-mode
+      (let ((success nil))
+        (unwind-protect
+            (progn
+              (comint--fontify-input-on)
+              (setq success t))
+          (unless success
+            (setq comint-fontify-input-mode nil)
+            (comint--fontify-input-off))))
+    (comint--fontify-input-off)))
+
+(defun comint--fontify-input-on ()
+  "Enable input fontification in the current comint buffer."
+  (comint--fontify-input-off)
+
+  (when comint-use-prompt-regexp
+    (error
+     "Input fontification is incompatible with `comint-use-prompt-regexp'"))
+
+  (add-function :around (local 'font-lock-fontify-region-function)
+                #'comint--fontify-input-fontify-region)
+  ;; `before-change-functions' are only run in the current buffer and
+  ;; not in its indirect buffers, which means that we must manually
+  ;; flush ppss cache
+  (add-hook 'before-change-functions
+            #'comint--fontify-input-ppss-flush-indirect 99 t)
+
+  ;; Set up contextual fontification
+  (unless (booleanp jit-lock-contextually)
+    (setq comint--fontify-input-saved-jit-lock-contextually
+          jit-lock-contextually)
+    (setq-local jit-lock-contextually t)
+    (when jit-lock-mode
+      (jit-lock-mode t))))
+
+(defun comint--fontify-input-off ()
+  "Disable input fontification in the current comint buffer."
+  (remove-function (local 'font-lock-fontify-region-function)
+                   #'comint--fontify-input-fontify-region)
+  (remove-hook 'before-change-functions
+               #'comint--fontify-input-ppss-flush-indirect t)
+
+  ;; Reset contextual fontification
+  (when comint--fontify-input-saved-jit-lock-contextually
+    (setq-local jit-lock-contextually
+                comint--fontify-input-saved-jit-lock-contextually)
+    (setq comint--fontify-input-saved-jit-lock-contextually nil)
+    (when jit-lock-mode
+      (jit-lock-mode t)))
+
+  (font-lock-flush))
+
+(defun comint--fontify-input-ppss-flush-indirect (beg &rest rest)
+  (when-let ((buf (comint-indirect-buffer t)))
+    (with-current-buffer buf
+      (when (memq #'syntax-ppss-flush-cache before-change-functions)
+        (apply #'syntax-ppss-flush-cache beg rest)))))
+
+(defun comint--fontify-input-fontify-region (fun beg end verbose)
+  "Fontify process output and user input in the current comint buffer.
+First, fontify the region between BEG and END using FUN.  Then
+fontify only the input text in the region with the help of an
+indirect buffer.  VERBOSE is passed to the fontify-region
+functions.  Skip fontification of input regions with non-nil
+`comint--fontify-input-inhibit-fontification' text property."
+  (pcase (funcall fun beg end verbose)
+    (`(jit-lock-bounds ,beg1 . ,end1)
+     (setq beg beg1 end end1)))
+  (pcase
+      (let ((min (point-min))
+            (max (point-max)))
+        (with-current-buffer (comint-indirect-buffer)
+          (narrow-to-region min max)
+          (comint--intersect-regions
+           nil (lambda (beg end)
+                 (unless (get-text-property
+                          beg 'comint--fontify-input-inhibit-fontification)
+                   (font-lock-fontify-region beg end verbose)))
+           beg end)))
+    (`((jit-lock-bounds ,beg1 . ,_) . (jit-lock-bounds ,_ . ,end1))
+     (setq beg (min beg beg1))
+     (setq end (max end end1))))
+
+  `(jit-lock-bounds ,beg . ,end))
+
+(defun comint--intersect-regions (fun-output fun-input beg end)
+  "Iterate over comint output and input regions between BEG and END.
+Divide the region specified by BEG and END into smaller regions
+that cover either process output (its `field' property is `output')
+or input (all remaining text).  Interchangeably call FUN-OUTPUT
+on each output region, and FUN-INPUT on each input region.
+
+FUN-OUTPUT and FUN-INPUT are passed two arguments, the beginning
+and end of the smaller region.  Before calling each function,
+narrow the buffer to the surrounding process output or input.  You
+can also pass nil as either function to skip its corresponding
+regions.
+
+Return a cons cell of return values of the first and last
+function called, or nil, if no function was called (if BEG = END)."
+  (let ((beg1 beg)
+        (end1 (copy-marker nil t))
+        (return-beg nil) (return-end nil)
+        (is-output (eq (get-text-property beg 'field) 'output)))
+    (setq end (copy-marker end t))
+
+    (while (< beg1 end)
+      (set-marker
+       end1 (or (if is-output
+                    (text-property-not-all beg1 end 'field 'output)
+                  (text-property-any beg1 end 'field 'output))
+                end))
+      (when-let ((fun (if is-output fun-output fun-input)))
+        (save-restriction
+          (let ((beg2 beg1)
+                (end2 end1))
+            (when (= beg2 beg)
+              (setq beg2 (field-beginning beg2)))
+            (when (= end2 end)
+              (setq end2 (field-end end2)))
+            ;; Narrow to the whole field surrounding the region
+            (narrow-to-region beg2 end2))
+          (setq return-end (list (funcall fun beg1
+                                          (marker-position end1)))))
+        (unless return-beg
+          (setq return-beg return-end)))
+      (setq beg1 (marker-position end1))
+      (setq is-output (not is-output)))
+
+    (set-marker end nil)
+    (set-marker end1 nil)
+    (when return-beg
+      (cons (car return-beg) (car return-end)))))
+
+(defun comint-indent-input-line (fun)
+  "Indent current line from comint process output or input.
+If point is on output, call FUN, otherwise indent the current
+line in the indirect buffer created by `comint-indirect-buffer',
+which see."
+  (if (or comint-use-prompt-regexp
+          (eq (get-text-property (point) 'field) 'output))
+      (funcall fun)
+    (let ((point (point))
+          (min (point-min))
+          (max (point-max)))
+      (unwind-protect
+          (with-current-buffer (comint-indirect-buffer)
+            (narrow-to-region min max)
+            (goto-char point)
+            (narrow-to-region (field-beginning) (field-end))
+            (unwind-protect (funcall indent-line-function)
+              (setq point (point))))
+        (goto-char point)))))
+
+(defun comint-indent-input-region (fun start end)
+  "Indent comint process output and input between START and END.
+Output text between START and END is indented with FUN and input
+text is indented in the indirect buffer created by
+`comint-indirect-buffer', which see."
+  (if comint-use-prompt-regexp
+      (funcall fun start end)
+    (let ((opoint (copy-marker (point)))
+          final-point)
+      (unwind-protect
+          (comint--intersect-regions
+           (lambda (start end)
+             (goto-char opoint)
+             (if (= opoint (point))
+                 (unwind-protect (funcall fun start end)
+                   (setq final-point (copy-marker (point))))
+               (funcall fun start end)))
+           (lambda (start end)
+             (let ((min (point-min))
+                   (max (point-max))
+                   (final-point1 nil))
+               (unwind-protect
+                   (with-current-buffer (comint-indirect-buffer)
+                     (narrow-to-region min max)
+                     (goto-char opoint)
+                     (if (= opoint (point))
+                         (unwind-protect
+                             (funcall indent-region-function start end)
+                           (setq final-point1 (point)))
+                       (funcall indent-region-function start end)))
+                 (when final-point1
+                   (setq final-point (copy-marker final-point1))))))
+           start end)
+        (if final-point
+            (progn
+              (goto-char final-point)
+              (set-marker final-point nil))
+          (goto-char opoint))
+        (set-marker opoint nil)))))
+
+(defun comint-indent-input-line-default ()
+  "Indent current line from comint process output or input.
+If point is on output, indent the current line according to the
+default value of `indent-line-function', otherwise indent the
+current line in the indirect buffer created by
+`comint-indirect-buffer', which see."
+  (comint-indent-input-line (default-value 'indent-line-function)))
+
+(defun comint-indent-input-region-default (start end)
+  "Indent comint process output and input between START and END.
+Output text between START and END is indented according to the
+default value of `indent-region-function' and input text is
+indented in the indirect buffer created by
+`comint-indirect-buffer', which see."
+  (comint-indent-input-region (default-value 'indent-line-function)
+                              start end))
+
+(defun comint-indirect-buffer (&optional no-create)
+  "Return an indirect comint fontification buffer.
+If an indirect buffer for the current buffer already exists,
+return it, otherwise create it first and set it up by calling
+`comint-indirect-setup-function' with zero arguments, turning on
+font-lock, and running `comint-indirect-setup-hook'.  This setup
+happens with `delay-mode-hooks' set to t in order to prevent
+possible SETUP-FUN's mode hooks from running.
+
+If an indirect buffer doesn't exist and NO-CREATE is non-nil,
+return nil."
+  (or
+   comint--indirect-buffer
+   (unless no-create
+     (let ((setup-hook
+            (if (local-variable-p 'comint-indirect-setup-hook)
+                (list comint-indirect-setup-hook)))
+           (setup-fun comint-indirect-setup-function))
+
+       (add-hook 'change-major-mode-hook #'comint--indirect-cleanup
+                 nil t)
+
+       (with-current-buffer
+           (setq comint--indirect-buffer
+                 (make-indirect-buffer
+                  (current-buffer)
+                  (generate-new-buffer-name
+                   (concat " " (buffer-name) "-comint-indirect"))))
+         (setq-local delay-mode-hooks t)
+         (when setup-fun
+           (let ((change-major-mode-hook nil)
+                 (after-change-major-mode-hook nil))
+             (funcall setup-fun)))
+         (setq-local font-lock-dont-widen t)
+         (setq-local font-lock-support-mode nil)
+         (font-lock-mode)
+         (when setup-hook
+           (setq-local comint-indirect-setup-hook
+                       (car setup-hook)))
+         (run-hooks 'comint-indirect-setup-hook))
+       comint--indirect-buffer))))
+
+(defun comint--indirect-cleanup ()
+  (when comint--indirect-buffer
+    (kill-buffer comint--indirect-buffer)
+    (setq comint--indirect-buffer nil)))
 
 
+
 ;;; Converting process modes to use comint mode
 ;;============================================================================
 ;; The code in the Emacs 19 distribution has all been modified to use comint
diff --git a/lisp/cus-dep.el b/lisp/cus-dep.el
index bb07a0694a..3f18202aff 100644
--- a/lisp/cus-dep.el
+++ b/lisp/cus-dep.el
@@ -175,7 +175,7 @@ Usage: emacs -batch -l ./cus-dep.el -f 
custom-make-dependencies DIRS"
                                    (prin1 (sort found #'string<))))
                            alist))))))
     (dolist (e (sort alist (lambda (e1 e2) (string< (car e1) (car e2)))))
-      (insert "(put '" (car e) " 'custom-loads '" (cdr e) ")\n")))
+      (insert "(custom--add-custom-loads '" (car e) " '" (cdr e) ")\n")))
   (insert "\
 
 ;; The remainder of this file is for handling :version.
diff --git a/lisp/cus-edit.el b/lisp/cus-edit.el
index d3768766be..ee32c9c945 100644
--- a/lisp/cus-edit.el
+++ b/lisp/cus-edit.el
@@ -4298,7 +4298,12 @@ restoring it to the state of a face that has never been 
customized."
   "A Lisp fringe bitmap name."
   :format "%v"
   :tag "Fringe bitmap"
-  :match (lambda (_widget value) (fringe-bitmap-p value))
+  :match (lambda (_widget value)
+           ;; In no-X builds (where `fringe-bitmaps' is undefined),
+           ;; allow anything.  This ensures that customizations set on
+           ;; a with-X build aren't considered invalid under no-X.
+           (or (not (boundp 'fringe-bitmaps))
+               (fringe-bitmap-p value)))
   :completions (apply-partially #'completion-table-with-predicate
                                 obarray #'fringe-bitmap-p 'strict)
   :prompt-match 'fringe-bitmap-p
diff --git a/lisp/cus-start.el b/lisp/cus-start.el
index 0e1cb4589d..d7fb56c985 100644
--- a/lisp/cus-start.el
+++ b/lisp/cus-start.el
@@ -251,7 +251,6 @@ Leaving \"Default\" unchecked is equivalent with specifying 
a default of
             ;; emacs.c
             (report-emacs-bug-address emacsbug string)
             ;; eval.c
-            (max-specpdl-size limits integer)
             (max-lisp-eval-depth limits integer)
             (max-mini-window-height limits
                                     (choice (const :tag "quarter screen" nil)
diff --git a/lisp/cus-theme.el b/lisp/cus-theme.el
index 69ec837db8..90680ff68f 100644
--- a/lisp/cus-theme.el
+++ b/lisp/cus-theme.el
@@ -496,7 +496,7 @@ It includes all faces in list FACES."
       (princ (substitute-command-keys " in `"))
       (help-insert-xref-button (file-name-nondirectory fn)
                               'help-theme-def fn)
-      (princ (substitute-command-keys "'")))
+      (princ (substitute-quotes "'")))
     (princ ".\n")
     (if (custom-theme-p theme)
        (progn
diff --git a/lisp/custom.el b/lisp/custom.el
index 96dfb37d86..604b1a3ff4 100644
--- a/lisp/custom.el
+++ b/lisp/custom.el
@@ -674,8 +674,6 @@ property, or (ii) an alias for another customizable 
variable."
   "Return the standard value of VARIABLE."
   (eval (car (get variable 'standard-value)) t))
 
-(define-obsolete-function-alias 'user-variable-p 'custom-variable-p "24.3")
-
 (defun custom-note-var-changed (variable)
   "Inform Custom that VARIABLE has been set (changed).
 VARIABLE is a symbol that names a user option.
@@ -1709,6 +1707,13 @@ If a choice with the same tag already exists, no action 
is taken."
       (put variable 'custom-type
            (append choices (list choice))))))
 
+(defun custom--add-custom-loads (symbol loads)
+  ;; Don't overwrite existing `custom-loads'.
+  (dolist (load (get symbol 'custom-loads))
+    (unless (memq load loads)
+      (push load loads)))
+  (put symbol 'custom-loads loads))
+
 (provide 'custom)
 
 ;;; custom.el ends here
diff --git a/lisp/dired-aux.el b/lisp/dired-aux.el
index 06f0b86fc4..9add96c260 100644
--- a/lisp/dired-aux.el
+++ b/lisp/dired-aux.el
@@ -987,7 +987,7 @@ Also see the `dired-confirm-shell-command' variable."
                ;; Add 'wait' to force those POSIX shells to wait until
                ;; all commands finish.
                (or (and parallel-in-background (not w32-shell)
-                        "&wait")
+                        " &wait")
                    "")))
       (t
        (let ((files (mapconcat #'shell-quote-argument
@@ -999,7 +999,7 @@ Also see the `dired-confirm-shell-command' variable."
           ;; Be consistent in how we treat inputs to commands -- do
           ;; the same here as in the `on-each' case.
           (if (and in-background (not w32-shell))
-              "&wait"
+              " &wait"
             "")))))
      (or (and in-background "&")
          ""))))
@@ -1169,6 +1169,10 @@ Return the result of `process-file' - zero for success."
          ;; Optional decompression.
          "unxz")
 
+   ;; zstandard archives
+   `(,(rx (or ".tar.zst" ".tzst") eos) "unzstd -c %i | tar -xf -")
+   `(,(rx ".zst" eos)                  "unzstd --rm")
+
    '("\\.shar\\.Z\\'" "zcat * | unshar")
    '("\\.shar\\.g?z\\'" "gunzip -qc * | unshar")
 
@@ -2876,6 +2880,10 @@ of `dired-dwim-target', which see.
 
 Also see `dired-do-revert-buffer'."
   (interactive "P")
+  (when (seq-find (lambda (file)
+                    (member (file-name-nondirectory file) '("." "..")))
+                  (dired-get-marked-files nil arg))
+    (user-error "Can't rename \".\" or \"..\" files"))
   (dired-do-create-files 'move #'dired-rename-file
                         "Move" arg dired-keep-marker-rename "Rename"))
 
diff --git a/lisp/dired.el b/lisp/dired.el
index fa06c8fd44..96b580d576 100644
--- a/lisp/dired.el
+++ b/lisp/dired.el
@@ -508,15 +508,6 @@ This is what the do-commands look for, and what the 
mark-commands store.")
 (defvar dired-del-marker ?D
   "Character used to flag files for deletion.")
 
-(defvar dired-shrink-to-fit t
-  ;; I see no reason ever to make this nil -- rms.
-  ;;  (> baud-rate search-slow-speed)
-  "Non-nil means Dired shrinks the display buffer to fit the marked files.")
-(make-obsolete-variable 'dired-shrink-to-fit
-                       "use the Customization interface to add a new rule
-to `display-buffer-alist' where condition regexp is \"^ \\*Marked Files\\*$\",
-action argument symbol is `window-height' and its value is nil." "24.3")
-
 (defvar dired-file-version-alist)
 
 ;;;###autoload
@@ -2259,8 +2250,6 @@ Do so according to the former subdir alist 
OLD-SUBDIR-ALIST."
   "M-s f C-M-s" #'dired-isearch-filenames-regexp
   ;; misc
   "<remap> <read-only-mode>"   #'dired-toggle-read-only
-  ;; `toggle-read-only' is an obsolete alias for `read-only-mode'
-  "<remap> <toggle-read-only>" #'dired-toggle-read-only
   "?"       #'dired-summary
   "DEL"     #'dired-unmark-backward
   "<remap> <undo>"             #'dired-undo
@@ -3675,16 +3664,16 @@ non-empty directories is allowed."
         case-fold-search markers)
     (if (save-excursion (goto-char (point-min))
                        (re-search-forward regexp nil t))
-       (dired-internal-do-deletions
-         (nreverse
-         ;; this can't move point since ARG is nil
-         (dired-map-over-marks (cons (dired-get-filename)
-                                      (let ((m (point-marker)))
-                                        (push m markers)
-                                        m))
-                               nil))
-        nil t)
-      (dolist (m markers) (set-marker m nil))
+        (progn
+          (dired-internal-do-deletions
+           (nreverse
+            (dired-map-over-marks (cons (dired-get-filename)
+                                   (let ((m (point-marker)))
+                                     (push m markers)
+                                     m))
+                             nil))
+           nil t)
+          (dolist (m markers) (set-marker m nil)))
       (or nomessage
          (message "(No deletions requested)")))))
 
@@ -3746,7 +3735,10 @@ non-empty directories is allowed."
                      (progress-reporter-update progress-reporter succ)
                      (dired-fun-in-all-buffers
                       (file-name-directory fn) (file-name-nondirectory fn)
-                      #'dired-delete-entry fn))
+                      #'dired-delete-entry fn)
+                      ;; For when FN's directory name is different
+                      ;; from the current buffer's dired-directory.
+                      (dired-delete-entry fn))
                   (quit (throw '--delete-cancel (message "OK, canceled")))
                  (error ;; catch errors from failed deletions
                   (dired-log "%s: %s\n" (car err) (error-message-string err))
@@ -3876,28 +3868,6 @@ or \"* [3 files]\"."
          (format "[next %d files]" arg)
        (format "%c [%d files]" dired-marker-char count)))))
 
-(defun dired-pop-to-buffer (buf)
-  "Pop up buffer BUF in a way suitable for Dired."
-  (declare (obsolete pop-to-buffer "24.3"))
-  (let ((split-window-preferred-function
-        (lambda (window)
-          (or (and (let ((split-height-threshold 0))
-                     (window-splittable-p (selected-window)))
-                   ;; Try to split the selected window vertically if
-                   ;; that's possible.  (Bug#1806)
-                   (split-window-below))
-              ;; Otherwise, try to split WINDOW sensibly.
-              (split-window-sensibly window))))
-       pop-up-frames)
-    (pop-to-buffer (get-buffer-create buf)))
-  ;; See Bug#12281.
-  (set-window-start nil (point-min))
-  ;; If dired-shrink-to-fit is t, make its window fit its contents.
-  (when dired-shrink-to-fit
-    ;; Try to not delete window when we want to display less than
-    ;; `window-min-height' lines.
-    (fit-window-to-buffer (get-buffer-window buf) nil 1 nil nil t)))
-
 (defcustom dired-no-confirm nil
   "A list of symbols for commands Dired should not confirm, or t.
 Command symbols are `byte-compile', `chgrp', `chmod', `chown', `compress',
@@ -4587,9 +4557,6 @@ Possible values:
                     (t (concat "Dired " dired-actual-switches))))))
     (force-mode-line-update)))
 
-(define-obsolete-function-alias 'dired-sort-set-modeline
-  #'dired-sort-set-mode-line "24.3")
-
 (defun dired-sort-toggle-or-edit (&optional arg)
   "Toggle sorting by date, and refresh the Dired buffer.
 With a prefix argument, edit the current listing switches instead."
diff --git a/lisp/disp-table.el b/lisp/disp-table.el
index 422728c61c..f0ee3d1d78 100644
--- a/lisp/disp-table.el
+++ b/lisp/disp-table.el
@@ -296,6 +296,78 @@ in `.emacs'."
         (if (coding-system-p c) c 'latin-1))))
     (standard-display-european-internal)))
 
+
+;;;###autoload
+(defun standard-display-by-replacement-char (&optional repl from to)
+  "Produce code to display characters between FROM and TO using REPL.
+This function produces a buffer with code to set up `standard-display-table'
+such that characters that cannot be displayed by the terminal, and
+don't already have their display set up in `standard-display-table', will
+be represented by a replacement character.  You can evaluate the produced
+code to use the setup for the current Emacs session, or copy the code
+into your init file, to make Emacs use it for subsequent sessions.
+
+Interactively, the produced code arranges for any character in
+the range [#x100..#x10FFFF] that the terminal cannot display to
+be represented by the #xFFFD Unicode replacement character.
+
+When called from Lisp, FROM and TO define the range of characters for
+which to produce the setup code for `standard-display-table'.  If they
+are omitted, they default to #x100 and #x10FFFF respectively, covering
+the entire non-ASCII range of Unicode characters.
+REPL is the replacement character to use.  If it's omitted, it defaults
+to #xFFFD, the Unicode replacement character, usually displayed as a
+black diamond with a question mark inside.
+The produced code sets up `standard-display-table' to show REPL with
+the `homoglyph' face, making the replacements stand out on display.
+
+This command is most useful with text-mode terminals, such as the
+Linux console, for which Emacs has a reliable way of determining
+which characters can be displayed and which cannot."
+  (interactive)
+  (or repl
+      (setq repl #xfffd))
+  (or (and from to (<= from to))
+      (setq from #x100
+           to (max-char 'unicode)))
+  (let ((buf (get-buffer-create "*Display replacements*"))
+       (ch from)
+        (tbl standard-display-table)
+       first)
+    (with-current-buffer buf
+      (erase-buffer)
+      (insert "\
+;; This code was produced by `standard-display-by-replacement-char'.
+;; Evaluate the Lisp code below to make Emacs show the standard
+;; replacement character as a substitute for each undisplayable character.
+;; One way to do that is with \"C-x h M-x eval-region RET\".
+;; Normally you would put this code in your Emacs initialization file,
+;; perhaps conditionally based on the type of terminal, so that
+;; this setup happens automatically on each startup.
+(let ((tbl (or standard-display-table
+               (setq standard-display-table (make-display-table)))))\n")
+      (while (<= ch to)
+       (cond
+        ((or (char-displayable-p ch)
+             (aref tbl ch))
+         (setq ch (1+ ch)))
+        (t
+         (setq first ch)
+         (while (and (<= ch to)
+                     (not (or (char-displayable-p ch)
+                              (aref tbl ch))))
+           (setq ch (1+ ch)))
+         (insert
+          "  (set-char-table-range tbl '("
+          (format "#x%x" first)
+          " . "
+          (format "#x%x" (1- ch))
+          ")\n\                        (vconcat (list (make-glyph-code "
+          (format "#x%x" repl) " 'homoglyph))))\n"))))
+      (insert ")\n"))
+    (pop-to-buffer buf)))
+
+
 (provide 'disp-table)
 
 ;;; disp-table.el ends here
diff --git a/lisp/dnd.el b/lisp/dnd.el
index 70852885a8..b2e93a63de 100644
--- a/lisp/dnd.el
+++ b/lisp/dnd.el
@@ -370,8 +370,8 @@ currently being held down.  It should only be called upon a
                          ;; the standard (i.e. Qt programs).
                          "text/plain" "text/plain;charset=utf-8")
                        (cl-ecase action
-                         ('copy 'XdndActionCopy)
-                         ('move 'XdndActionMove))
+                         (copy 'XdndActionCopy)
+                         (move 'XdndActionMove))
                        frame nil allow-same-frame)))
     (cond
      ((eq return-value 'XdndActionCopy) 'copy)
@@ -457,9 +457,9 @@ currently being held down.  It should only be called upon a
                              ;; programs.
                              "_DT_NETFILE")
                            (cl-ecase action
-                             ('copy 'XdndActionCopy)
-                             ('move 'XdndActionMove)
-                             ('link 'XdndActionLink))
+                             (copy 'XdndActionCopy)
+                             (move 'XdndActionMove)
+                             (link 'XdndActionLink))
                            frame nil allow-same-frame)))
         (cond
          ((eq return-value 'XdndActionCopy) 'copy)
@@ -527,9 +527,9 @@ FILES will be dragged."
                            ;; and Haiku.
                            "FILE_NAME" "HOST_NAME")
                          (cl-ecase action
-                           ('copy 'XdndActionCopy)
-                           ('move 'XdndActionMove)
-                           ('link 'XdndActionLink))
+                           (copy 'XdndActionCopy)
+                           (move 'XdndActionMove)
+                           (link 'XdndActionLink))
                          frame nil allow-same-frame)))
       (cond
        ((eq return-value 'XdndActionCopy) 'copy)
diff --git a/lisp/doc-view.el b/lisp/doc-view.el
index 29da3b4297..b1ea90c212 100644
--- a/lisp/doc-view.el
+++ b/lisp/doc-view.el
@@ -153,7 +153,7 @@
   "In-buffer document viewer.
 The viewer handles PDF, PostScript, DVI, DJVU, ODF, EPUB, CBZ,
 FB2, XPS and OXPS files, if the appropriate converter programs
-are available (see Info node `(emacs)Document View')"
+are available (see Info node `(emacs)Document View')."
   :link '(function-link doc-view)
   :version "22.2"
   :group 'applications
@@ -209,6 +209,45 @@ are available (see Info node `(emacs)Document View')"
           function)
   :version "24.4")
 
+(defcustom doc-view-mupdf-use-svg (image-type-available-p 'svg)
+  "Whether to use svg images for PDF files."
+  :type 'boolean
+  :version "29.1")
+
+(defcustom doc-view-imenu-enabled (and (executable-find "mutool") t)
+  "Whether to generate an imenu outline when \"mutool\" is available."
+  :type 'boolean
+  :version "29.1")
+
+(defcustom doc-view-imenu-title-format "%t (%p)"
+  "Format spec for imenu's display of section titles from docview documents.
+
+The special markers '%t' and '%p' are replaced by the section
+title and page number in this format string, which uses
+`format-spec'.
+
+For instance, setting this variable to \"%t\" will produce items
+showing only titles and no page number."
+  :type 'string
+  :version "29.1")
+
+(defcustom doc-view-imenu-flatten nil
+  "Whether to flatten the list of sections in an imenu or show it nested."
+  :type 'boolean
+  :version "29.1")
+
+(defcustom doc-view-svg-background "white"
+  "Background color for svg images.
+See `doc-view-mupdf-use-svg'."
+  :type 'color
+  :version "29.1")
+
+(defcustom doc-view-svg-foreground "black"
+  "Foreground color for svg images.
+See `doc-view-mupdf-use-svg'."
+  :type 'color
+  :version "29.1")
+
 (defcustom doc-view-ghostscript-options
   '("-dSAFER" ;; Avoid security problems when rendering files from untrusted
     ;; sources.
@@ -1562,6 +1601,9 @@ ARGS is a list of image descriptors."
                            (setq args `(,@args :width ,doc-view-image-width)))
                           (unless (member :transform-smoothing args)
                             (setq args `(,@args :transform-smoothing t)))
+                          (when (eq doc-view--image-type 'svg)
+                            (setq args `(,@args :background 
,doc-view-svg-background
+                                               :foreground 
,doc-view-svg-foreground)))
                          (apply #'create-image file doc-view--image-type nil 
args))))
             (slice (doc-view-current-slice))
             (img-width (and image (car (image-size image))))
@@ -1854,6 +1896,81 @@ If BACKWARD is non-nil, jump to the previous match."
             (y-or-n-p "No more matches before current page.  Wrap to last 
match? "))
        (doc-view-goto-page (caar (last doc-view--current-search-matches)))))))
 
+;;;; Imenu support
+(defconst doc-view--outline-rx
+  "[^\t]+\\(\t+\\)\"\\(.+\\)\"\t#\\(?:page=\\)?\\([0-9]+\\)")
+
+(defvar-local doc-view--outline nil
+  "Cached PDF outline, so that it is only computed once per document.")
+
+(defun doc-view--pdf-outline (&optional file-name)
+  "Return a list describing the outline of FILE-NAME.
+Return a list describing the current file if FILE-NAME is nil.
+
+Each element in the returned list contains information about a section's
+title, nesting level and page number.  The list is flat: its tree
+structure is extracted by `doc-view--imenu-subtree'."
+  (let ((fn (or file-name (buffer-file-name))))
+    (when fn
+      (let ((outline nil)
+            (fn (shell-quote-argument (expand-file-name fn))))
+        (with-temp-buffer
+          (insert (shell-command-to-string (format "mutool show %s outline" 
fn)))
+          (goto-char (point-min))
+          (while (re-search-forward doc-view--outline-rx nil t)
+            (push `((level . ,(length (match-string 1)))
+                    (title . ,(replace-regexp-in-string "\\\\[rt]" " "
+                                                        (match-string 2)))
+                    (page . ,(string-to-number (match-string 3))))
+                  outline)))
+        (nreverse outline)))))
+
+(defun doc-view--imenu-subtree (outline act)
+  "Construct a tree of imenu items for the given outline list and action.
+
+This auxliary function constructs recursively all the items for
+the first node in the outline and all its siblings at the same
+level.  Returns that imenu alist together with any other pending outline
+entries at an upper level."
+  (let ((level (alist-get 'level (car outline)))
+        (nested (not doc-view-imenu-flatten))
+        (index nil))
+    (while (and (car outline)
+                (or (not nested)
+                    (<= level (alist-get 'level (car outline)))))
+      (let-alist (car outline)
+        (let ((title (format-spec doc-view-imenu-title-format
+                                  `((?t . ,.title) (?p . ,.page)))))
+          (if (and nested (> .level level))
+              (let ((sub (doc-view--imenu-subtree outline act))
+                    (fst (car index)))
+                (setq index (cdr index))
+                (push (cons (car fst) (cons fst (car sub))) index)
+                (setq outline (cdr sub)))
+            (push `(,title 0 ,act ,.page) index)
+            (setq outline (cdr outline))))))
+    (cons (nreverse index) outline)))
+
+(defun doc-view-imenu-index (&optional file-name goto-page-fn)
+  "Create an imenu index using \"mutool\" to extract its outline.
+
+For extensibility, callers can specify a FILE-NAME to indicate
+the buffer other than the current buffer, and a jumping function
+GOTO-PAGE-FN other than `doc-view-goto-page'."
+  (let* ((goto (or goto-page-fn 'doc-view-goto-page))
+         (act (lambda (_name _pos page) (funcall goto page)))
+         (outline (or doc-view--outline (doc-view--pdf-outline file-name))))
+    (car (doc-view--imenu-subtree outline act))))
+
+(defun doc-view-imenu-setup ()
+  "Set up local state in the current buffer for imenu, if needed."
+  (when (and doc-view-imenu-enabled (executable-find "mutool"))
+    (setq-local imenu-create-index-function #'doc-view-imenu-index
+                imenu-submenus-on-top nil
+                imenu-sort-function nil
+                doc-view--outline (doc-view--pdf-outline))
+    (when doc-view--outline (imenu-add-to-menubar "Outline"))))
+
 ;;;; User interface commands and the mode
 
 (put 'doc-view-mode 'mode-class 'special)
@@ -1983,7 +2100,11 @@ If BACKWARD is non-nil, jump to the previous match."
   (pcase-let ((`(,conv-function ,type ,extension)
                (pcase doc-view-doc-type
                  ('djvu (list #'doc-view-djvu->tiff-converter-ddjvu 'tiff 
"tif"))
-                 (_     (list doc-view-pdf->png-converter-function  'png  
"png")))))
+                 (_ (if (and (eq doc-view-pdf->png-converter-function
+                                 #'doc-view-pdf->png-converter-mupdf)
+                             doc-view-mupdf-use-svg)
+                        (list doc-view-pdf->png-converter-function 'svg "svg")
+                      (list doc-view-pdf->png-converter-function 'png 
"png"))))))
     (setq-local doc-view-single-page-converter-function conv-function)
     (setq-local doc-view--image-type type)
     (setq-local doc-view--image-file-pattern (concat "page-%s." extension))))
@@ -2023,7 +2144,7 @@ If BACKWARD is non-nil, jump to the previous match."
   "Major mode in DocView buffers.
 
 DocView Mode is an Emacs document viewer.  It displays PDF, PS
-and DVI files (as PNG images) in Emacs buffers.
+and DVI files (as PNG or SVG images) in Emacs buffers.
 
 You can use \\<doc-view-mode-map>\\[doc-view-toggle-display] to
 toggle between displaying the document or editing it as text.
@@ -2118,6 +2239,7 @@ toggle between displaying the document or editing it as 
text.
     (setq mode-name "DocView"
          buffer-read-only t
          major-mode 'doc-view-mode)
+    (doc-view-imenu-setup)
     (doc-view-initiate-display)
     ;; Switch off view-mode explicitly, because doc-view-mode is the
     ;; canonical view mode for PDF/PS/DVI files.  This could be
diff --git a/lisp/ebuff-menu.el b/lisp/ebuff-menu.el
index 2b1fc916d9..809a31d457 100644
--- a/lisp/ebuff-menu.el
+++ b/lisp/ebuff-menu.el
@@ -203,9 +203,6 @@ See the documentation of `electric-buffer-list' for 
details."
   (setq mode-line-buffer-identification "Electric Buffer List")
   (setq-local Helper-return-blurb "return to buffer editing"))
 
-(define-obsolete-function-alias 'Electric-buffer-menu-mode
-  #'electric-buffer-menu-mode "24.3")
-
 ;; generally the same as Buffer-menu-mode-map
 ;;  (except we don't indirect to global-map)
 (put 'Electric-buffer-menu-undefined 'suppress-keymap t)
diff --git a/lisp/ecomplete.el b/lisp/ecomplete.el
index aa415a3e9e..6ff67d46d2 100644
--- a/lisp/ecomplete.el
+++ b/lisp/ecomplete.el
@@ -81,6 +81,11 @@ string that was matched."
                (function-item :tag "Sort by newness" ecomplete-newness)
                (function :tag "Other")))
 
+(defcustom ecomplete-auto-select nil
+  "Whether `ecomplete-display-matches' should automatically select a sole 
option."
+  :type 'boolean
+  :version "29.1")
+
 ;;; Internal variables.
 
 (defvar ecomplete-database nil)
@@ -94,8 +99,10 @@ string that was matched."
        (insert-file-contents ecomplete-database-file)
        (setq ecomplete-database (read (current-buffer)))))))
 
-(defun ecomplete-add-item (type key text)
-  "Add item TEXT of TYPE to the database, using KEY as the identifier."
+(defun ecomplete-add-item (type key text &optional force)
+  "Add item TEXT of TYPE to the database, using KEY as the identifier.
+By default, the longest version of TEXT will be preserved, but if
+FORCE is non-nil, use TEXT exactly as is."
   (unless ecomplete-database (ecomplete-setup))
   (let ((elems (assq type ecomplete-database))
        (now (time-convert nil 'integer))
@@ -106,10 +113,24 @@ string that was matched."
        (pcase-let ((`(,_key ,count ,_time ,oldtext) entry))
          (setcdr entry (list (1+ count) now
                              ;; Preserve the "more complete" text.
-                             (if (>= (length text) (length oldtext))
-                                 text oldtext))))
+                             (if (or force
+                                      (>= (length text) (length oldtext)))
+                                 text
+                                oldtext))))
       (nconc elems (list (list key 1 now text))))))
 
+(defun ecomplete--remove-item (type key)
+  "Remove the element of TYPE and KEY from the ecomplete database."
+  (unless ecomplete-database
+    (ecomplete-setup))
+  (let ((elems (assq type ecomplete-database)))
+    (unless elems
+      (user-error "No elements of type %s" type))
+    (let ((entry (assoc key elems)))
+      (unless entry
+        (user-error "No entry with key %s" key))
+      (setcdr elems (delq entry (cdr elems))))))
+
 (defun ecomplete-get-item (type key)
   "Return the text for the item identified by KEY of the required TYPE."
   (assoc key (cdr (assq type ecomplete-database))))
@@ -159,10 +180,14 @@ string that was matched."
 (defun ecomplete-display-matches (type word &optional choose)
   "Display the top-rated elements TYPE that match WORD.
 If CHOOSE, allow the user to choose interactively between the
-matches."
+matches.
+
+Auto-select when `ecomplete-message-display-abbrev-auto-select' is
+non-nil and there is only a single completion option available."
   (let* ((matches (ecomplete-get-matches type word))
+         (match-list (and matches (split-string matches "\n")))
+         (max-lines (and matches (- (length match-list) 2)))
         (line 0)
-        (max-lines (when matches (- (length (split-string matches "\n")) 2)))
         (message-log-max nil)
         command highlight)
     (if (not matches)
@@ -173,25 +198,31 @@ matches."
          (progn
            (message "%s" matches)
            nil)
-       (setq highlight (ecomplete-highlight-match-line matches line))
-       (let ((local-map (make-sparse-keymap))
-              (prev-func (lambda () (setq line (max (1- line) 0))))
-              (next-func (lambda () (setq line (min (1+ line) max-lines))))
-             selected)
-         (define-key local-map (kbd "RET")
-           (lambda () (setq selected (nth line (split-string matches "\n")))))
-         (define-key local-map (kbd "M-n") next-func)
-         (define-key local-map (kbd "<down>") next-func)
-         (define-key local-map (kbd "M-p") prev-func)
-         (define-key local-map (kbd "<up>") prev-func)
-         (let ((overriding-local-map local-map))
-           (while (and (null selected)
-                       (setq command (read-key-sequence highlight))
-                       (lookup-key local-map command))
-             (apply (key-binding command) nil)
-             (setq highlight (ecomplete-highlight-match-line matches line))))
-         (message (or selected "Abort"))
-         selected)))))
+        (if (and ecomplete-auto-select
+                 max-lines
+                 (zerop max-lines))
+            ;; Auto-select when only one option is available.
+            (nth 0 match-list)
+          ;; Interactively choose from the filtered completions.
+         (let ((local-map (make-sparse-keymap))
+                (prev-func (lambda () (setq line (max (1- line) 0))))
+                (next-func (lambda () (setq line (min (1+ line) max-lines))))
+               selected)
+           (define-key local-map (kbd "RET")
+                        (lambda () (setq selected (nth line match-list))))
+           (define-key local-map (kbd "M-n") next-func)
+           (define-key local-map (kbd "<down>") next-func)
+           (define-key local-map (kbd "M-p") prev-func)
+           (define-key local-map (kbd "<up>") prev-func)
+           (let ((overriding-local-map local-map))
+              (setq highlight (ecomplete-highlight-match-line matches line))
+             (while (and (null selected)
+                         (setq command (read-key-sequence highlight))
+                         (lookup-key local-map command))
+               (apply (key-binding command) nil)
+               (setq highlight (ecomplete-highlight-match-line matches line))))
+           (message (or selected "Abort"))
+            selected))))))
 
 (defun ecomplete-highlight-match-line (matches line)
   (with-temp-buffer
@@ -248,6 +279,53 @@ matches."
                         ecomplete-sort-predicate))))
          (complete-with-action action candidates string pred))))))
 
+(defun ecomplete--prompt-type ()
+  (unless ecomplete-database
+    (ecomplete-setup))
+  (if (length= ecomplete-database 1)
+      (caar ecomplete-database)
+    (completing-read "Item type to edit: "
+                     (mapcar #'car ecomplete-database)
+                     nil t)))
+
+(defun ecomplete-edit ()
+  "Prompt for an item and allow editing it."
+  (interactive)
+  (let* ((type (ecomplete--prompt-type))
+         (data (cdr (assq type ecomplete-database)))
+         (key (completing-read "Key to edit: " data nil t))
+         (new (read-string "New value (empty to remove): "
+                           (nth 3 (assoc key data)))))
+    (if (zerop (length new))
+        (progn
+          (ecomplete--remove-item type key)
+          (message "Removed %s" key))
+      (ecomplete-add-item type key new t)
+      (message "Updated %s to %s" key new))
+    (ecomplete-save)))
+
+(defun ecomplete-remove ()
+  "Remove entries matching a regexp from the ecomplete database."
+  (interactive)
+  (let* ((type (ecomplete--prompt-type))
+         (data (cdr (assq type ecomplete-database)))
+         (match (read-regexp (format "Remove %s keys matching (regexp): "
+                                     type)))
+         (elems (seq-filter (lambda (elem)
+                              (string-match-p match (car elem)))
+                            data)))
+    (if (length= elems 0)
+        (message "No matching entries for %s" match)
+      (when (yes-or-no-p (format "Delete %s matching ecomplete %s? "
+                                 (length elems)
+                                 (if (length= elems 1)
+                                     "entry"
+                                   "entries")))
+        (dolist (elem elems)
+          (ecomplete--remove-item type (car elem)))
+        (ecomplete-save)
+        (message "Deleted entries")))))
+
 (provide 'ecomplete)
 
 ;;; ecomplete.el ends here
diff --git a/lisp/emacs-lisp/backtrace.el b/lisp/emacs-lisp/backtrace.el
index 70473770d1..4ffe6f573c 100644
--- a/lisp/emacs-lisp/backtrace.el
+++ b/lisp/emacs-lisp/backtrace.el
@@ -209,7 +209,6 @@ frames where the source code location is known.")
   "v"   #'backtrace-toggle-locals
   "#"   #'backtrace-toggle-print-circle
   ":"   #'backtrace-toggle-print-gensym
-  "s"   #'backtrace-goto-source
   "RET" #'backtrace-help-follow-symbol
   "+"   #'backtrace-multi-line
   "-"   #'backtrace-single-line
diff --git a/lisp/emacs-lisp/benchmark.el b/lisp/emacs-lisp/benchmark.el
index 882b1d68c4..47bc3a4524 100644
--- a/lisp/emacs-lisp/benchmark.el
+++ b/lisp/emacs-lisp/benchmark.el
@@ -70,7 +70,7 @@ number of repetitions actually used."
 
 (defun benchmark--adaptive (func time)
   "Measure the run time of FUNC, calling it enough times to last TIME seconds.
-Result is (REPETITIONS . DATA) where DATA is as returned by `branchmark-call'."
+Result is (REPETITIONS . DATA) where DATA is as returned by `benchmark-call'."
   (named-let loop ((repetitions 1)
                    (data (let ((x (list 0))) (setcdr x x) x)))
     ;; (message "Running %d iteration" repetitions)
diff --git a/lisp/emacs-lisp/byte-opt.el b/lisp/emacs-lisp/byte-opt.el
index 27b0d33d3e..5ef2d7fe82 100644
--- a/lisp/emacs-lisp/byte-opt.el
+++ b/lisp/emacs-lisp/byte-opt.el
@@ -737,7 +737,7 @@ for speeding up processing.")
                                 reverse nreverse sort))
                   (setq form (nth 1 form))
                   t)
-                 ((memq head '(mapc setq setcar setcdr puthash))
+                 ((memq head '(mapc setq setcar setcdr puthash set))
                   (setq form (nth 2 form))
                   t)
                  ((memq head '(aset put function-put))
@@ -793,6 +793,7 @@ for speeding up processing.")
                            sxhash sxhash-equal sxhash-eq sxhash-eql
                            sxhash-equal-including-properties
                            make-marker copy-marker point-marker mark-marker
+                           set-marker
                            kbd key-description
                            always))
                   t)
@@ -811,7 +812,7 @@ for speeding up processing.")
 (defun byte-compile-nilconstp (form)
   "Return non-nil if FORM always evaluates to a nil value."
   (setq form (byte-opt--bool-value-form form))
-  (or (not form)   ; assume (quote nil) always being normalised to nil
+  (or (not form)   ; assume (quote nil) always being normalized to nil
       (and (consp form)
            (let ((head (car form)))
              ;; FIXME: There are many other expressions that are statically 
nil.
@@ -1183,7 +1184,7 @@ See Info node `(elisp) Integer Basics'."
     (if (equal new-args (cdr form))
         ;; Input is unchanged: keep original form, and don't represent
         ;; a nil result explicitly because that would lead to infinite
-        ;; growth when the optimiser is iterated.
+        ;; growth when the optimizer is iterated.
         (setq nil-result nil)
       (setq form (cons (car form) new-args)))
 
@@ -1531,15 +1532,16 @@ See Info node `(elisp) Integer Basics'."
 
 (put 'set 'byte-optimizer #'byte-optimize-set)
 (defun byte-optimize-set (form)
-  (let ((var (car-safe (cdr-safe form))))
-    (cond
-     ((and (eq (car-safe var) 'quote) (consp (cdr var)))
-      `(setq ,(cadr var) ,@(cddr form)))
-     ((and (eq (car-safe var) 'make-local-variable)
-          (eq (car-safe (setq var (car-safe (cdr var)))) 'quote)
-          (consp (cdr var)))
-      `(progn ,(cadr form) (setq ,(cadr var) ,@(cddr form))))
-     (t form))))
+  (pcase (cdr form)
+    ;; Make sure we only turn `set' into `setq' for dynamic variables.
+    (`((quote ,(and var (guard (and (symbolp var)
+                                    (not (macroexp--const-symbol-p var))
+                                    (not (assq var byte-optimize--lexvars))))))
+       ,newval)
+     `(setq ,var ,newval))
+    (`(,(and ml `(make-local-variable ,(and v `(quote ,_)))) ,newval)
+     `(progn ,ml (,(car form) ,v ,newval)))
+    (_ form)))
 
 ;; enumerating those functions which need not be called if the returned
 ;; value is not used.  That is, something like
@@ -1999,20 +2001,20 @@ If FOR-EFFECT is non-nil, the return value is assumed 
to be of no importance."
          (setq keep-going t)
          (setq tmp (aref byte-stack+-info (symbol-value (car lap0))))
          (setq rest (cdr rest))
-         (cond ((= tmp 1)
+         (cond ((eql tmp 1)
                 (byte-compile-log-lap
                  "  %s discard\t-->\t<deleted>" lap0)
                 (setq lap (delq lap0 (delq lap1 lap))))
-               ((= tmp 0)
+               ((eql tmp 0)
                 (byte-compile-log-lap
                  "  %s discard\t-->\t<deleted> discard" lap0)
                 (setq lap (delq lap0 lap)))
-               ((= tmp -1)
+               ((eql tmp -1)
                 (byte-compile-log-lap
                  "  %s discard\t-->\tdiscard discard" lap0)
                 (setcar lap0 'byte-discard)
                 (setcdr lap0 0))
-               ((error "Optimizer error: too much on the stack"))))
+               (t (error "Optimizer error: too much on the stack"))))
         ;;
         ;; goto*-X X:  -->  X:
         ;;
diff --git a/lisp/emacs-lisp/byte-run.el b/lisp/emacs-lisp/byte-run.el
index 9a56ba0f7a..9db84c31b8 100644
--- a/lisp/emacs-lisp/byte-run.el
+++ b/lisp/emacs-lisp/byte-run.el
@@ -112,44 +112,6 @@ So far, FUNCTION can only be a symbol, not a lambda 
expression."
 (function-put 'defmacro 'doc-string-elt 3)
 (function-put 'defmacro 'lisp-indent-function 2)
 
-;; `macro-declaration-function' are both obsolete (as marked at the end of this
-;; file) but used in many .elc files.
-
-;; We don't use #' here, because it's an obsolete function, and we
-;; can't use `with-suppressed-warnings' here due to how this file is
-;; used in the bootstrapping process.
-(defvar macro-declaration-function 'macro-declaration-function
-  "Function to process declarations in a macro definition.
-The function will be called with two args MACRO and DECL.
-MACRO is the name of the macro being defined.
-DECL is a list `(declare ...)' containing the declarations.
-The value the function returns is not used.")
-
-(defalias 'macro-declaration-function
-  #'(lambda (macro decl)
-      "Process a declaration found in a macro definition.
-This is set as the value of the variable `macro-declaration-function'.
-MACRO is the name of the macro being defined.
-DECL is a list `(declare ...)' containing the declarations.
-The return value of this function is not used."
-      ;; We can't use `dolist' or `cadr' yet for bootstrapping reasons.
-      (let (d)
-        ;; Ignore the first element of `decl' (it's always `declare').
-        (while (setq decl (cdr decl))
-          (setq d (car decl))
-          (if (and (consp d)
-                   (listp (cdr d))
-                   (null (cdr (cdr d))))
-              (cond ((eq (car d) 'indent)
-                     (put macro 'lisp-indent-function (car (cdr d))))
-                    ((eq (car d) 'debug)
-                     (put macro 'edebug-form-spec (car (cdr d))))
-                    ((eq (car d) 'doc-string)
-                     (put macro 'doc-string-elt (car (cdr d))))
-                    (t
-                     (message "Unknown declaration %s" d)))
-            (message "Invalid declaration %s" d))))))
-
 ;; We define macro-declaration-alist here because it is needed to
 ;; handle declarations in macro definitions and this is the first file
 ;; loaded by loadup.el that uses declarations in macros.  We specify
@@ -771,9 +733,4 @@ type is.  This defaults to \"INFO\"."
 ;;       (file-format emacs19))"
 ;;   nil)
 
-(make-obsolete-variable 'macro-declaration-function
-                        'macro-declarations-alist "24.3")
-(make-obsolete 'macro-declaration-function
-               'macro-declarations-alist "24.3")
-
 ;;; byte-run.el ends here
diff --git a/lisp/emacs-lisp/bytecomp.el b/lisp/emacs-lisp/bytecomp.el
index a16486dc31..03c45e44a5 100644
--- a/lisp/emacs-lisp/bytecomp.el
+++ b/lisp/emacs-lisp/bytecomp.el
@@ -1705,12 +1705,12 @@ URLs."
               (+ " " (or
                       ;; Arguments.
                       (+ (or (syntax symbol)
-                             (any word "-/:[]&=().?^\\#'")))
+                             (any word "-/:[]&=()<>.,?^\\#*'\"")))
                       ;; Argument that is a list.
                       (seq "(" (* (not ")")) ")")))
               ")")))
     ""
-    ;; Heuristic: We can't reliably do `subsititute-command-keys'
+    ;; Heuristic: We can't reliably do `substitute-command-keys'
     ;; substitutions, since the value of a keymap in general can't be
     ;; known at compile time.  So instead, we assume that these
     ;; substitutions are of some length N.
@@ -3104,8 +3104,8 @@ lambda-expression."
              ;; Check that the bit after the `interactive' spec is
              ;; just a list of symbols (i.e., modes).
             (unless (seq-every-p #'symbolp (cdr (cdr int)))
-              (byte-compile-warn-x int "malformed interactive specc: %s"
-                                   int))
+              (byte-compile-warn-x
+                int "malformed `interactive' specification: %s" int))
              (setq command-modes (cdr (cdr int)))
             ;; If the interactive spec is a call to `list', don't
             ;; compile it, because `call-interactively' looks at the
diff --git a/lisp/emacs-lisp/cconv.el b/lisp/emacs-lisp/cconv.el
index 7f95fa94fa..23d0f12194 100644
--- a/lisp/emacs-lisp/cconv.el
+++ b/lisp/emacs-lisp/cconv.el
@@ -137,6 +137,11 @@ is less than this number.")
   ;; Alist associating to each function body the list of its free variables.
   )
 
+(defvar cconv--interactive-form-funs
+  ;; Table used to hold the functions we create internally for
+  ;; interactive forms.
+  (make-hash-table :test #'eq :weakness 'key))
+
 ;;;###autoload
 (defun cconv-closure-convert (form)
   "Main entry point for closure conversion.
@@ -503,9 +508,23 @@ places where they originally did not directly appear."
                               cond-forms)))
 
     (`(function (lambda ,args . ,body) . ,_)
-     (let ((docstring (if (eq :documentation (car-safe (car body)))
-                          (cconv-convert (cadr (pop body)) env extend))))
-       (cconv--convert-function args body env form docstring)))
+     (let* ((docstring (if (eq :documentation (car-safe (car body)))
+                           (cconv-convert (cadr (pop body)) env extend)))
+            (bf (if (stringp (car body)) (cdr body) body))
+            (if (when (eq 'interactive (car-safe (car bf)))
+                  (gethash form cconv--interactive-form-funs)))
+            (cif (when if (cconv-convert if env extend)))
+            (_ (pcase cif
+                 (`#'(lambda () ,form) (setf (cadr (car bf)) form) (setq cif 
nil))
+                 ('nil nil)
+                 ;; The interactive form needs special treatment, so the form
+                 ;; inside the `interactive' won't be used any further.
+                 (_ (setf (cadr (car bf)) nil))))
+            (cf (cconv--convert-function args body env form docstring)))
+       (if (not cif)
+           ;; Normal case, the interactive form needs no special treatment.
+           cf
+         `(cconv--interactive-helper ,cf ,cif))))
 
     (`(internal-make-closure . ,_)
      (byte-compile-report-error
@@ -589,12 +608,12 @@ places where they originally did not directly appear."
                                    (cconv-convert arg env extend))
                                  (cons fun args)))))))
 
-    (`(interactive . ,forms)
-     `(,(car form) . ,(mapcar (lambda (form)
-                                (cconv-convert form nil nil))
-                              forms)))
+    ;; The form (if any) is converted beforehand as part of the `lambda' case.
+    (`(interactive . ,_) form)
 
-    (`(declare . ,_) form)              ;The args don't contain code.
+    ;; `declare' should now be macro-expanded away (and if they're not, we're
+    ;; in trouble because they *can* contain code nowadays).
+    ;; (`(declare . ,_) form)              ;The args don't contain code.
 
     (`(oclosure--fix-type (ignore . ,vars) ,exp)
      (dolist (var vars)
@@ -739,6 +758,13 @@ This function does not return anything but instead fills 
the
     (`(function (lambda ,vrs . ,body-forms))
      (when (eq :documentation (car-safe (car body-forms)))
        (cconv-analyze-form (cadr (pop body-forms)) env))
+     (let ((bf (if (stringp (car body-forms)) (cdr body-forms) body-forms)))
+       (when (eq 'interactive (car-safe (car bf)))
+         (let ((if (cadr (car bf))))
+           (unless (macroexp-const-p if) ;Optimize this common case.
+             (let ((f `#'(lambda () ,if)))
+               (setf (gethash form cconv--interactive-form-funs) f)
+               (cconv-analyze-form f env))))))
      (cconv--analyze-function vrs body-forms env form))
 
     (`(setq ,var ,expr)
@@ -803,13 +829,8 @@ This function does not return anything but instead fills 
the
          (cconv-analyze-form fun env)))
      (dolist (form args) (cconv-analyze-form form env)))
 
-    (`(interactive . ,forms)
-     ;; These appear within the function body but they don't have access
-     ;; to the function's arguments.
-     ;; We could extend this to allow interactive specs to refer to
-     ;; variables in the function's enclosing environment, but it doesn't
-     ;; seem worth the trouble.
-     (dolist (form forms) (cconv-analyze-form form nil)))
+    ;; The form (if any) is converted beforehand as part of the `lambda' case.
+    (`(interactive . ,_) nil)
 
     ;; `declare' should now be macro-expanded away (and if they're not, we're
     ;; in trouble because they *can* contain code nowadays).
diff --git a/lisp/emacs-lisp/checkdoc.el b/lisp/emacs-lisp/checkdoc.el
index a5ab3a50ff..3f9bc28e0b 100644
--- a/lisp/emacs-lisp/checkdoc.el
+++ b/lisp/emacs-lisp/checkdoc.el
@@ -250,7 +250,7 @@ with these words enabled."
 (defvar checkdoc-ispell-lisp-words
   '("alist" "emacs" "etags" "keymap" "paren" "regexp" "sexp")
   "List of words that are correct when spell-checking Lisp documentation.")
-;;;###autoload(put 'checkdoc-ispell-list-words 'safe-local-variable 
#'checkdoc-list-of-strings-p)
+;;;###autoload(put 'checkdoc-ispell-list-words 'safe-local-variable 
#'list-of-strings-p)
 
 (defcustom checkdoc-max-keyref-before-warn nil
   "If non-nil, number of \\\\=[command-to-keystroke] tokens allowed in a doc 
string.
@@ -281,8 +281,6 @@ Currently, all recognized keywords must be on 
`finder-known-keywords'."
   :version "25.1"
   :type 'boolean)
 
-(define-obsolete-variable-alias 'checkdoc-style-hooks
-  'checkdoc-style-functions "24.3")
 (defvar checkdoc-style-functions nil
   "Hook run after the standard style check is completed.
 All functions must return nil or a string representing the error found.
@@ -292,8 +290,6 @@ Each hook is called with two parameters, (DEFUNINFO 
ENDPOINT).
 DEFUNINFO is the return value of `checkdoc-defun-info'.  ENDPOINT is the
 location of end of the documentation string.")
 
-(define-obsolete-variable-alias 'checkdoc-comment-style-hooks
-  'checkdoc-comment-style-functions "24.3")
 (defvar checkdoc-comment-style-functions nil
   "Hook run after the standard comment style check is completed.
 Must return nil if no errors are found, or a string describing the
@@ -324,7 +320,7 @@ These words are ignored when unquoted symbols are searched 
for.
 This should be set in an Emacs Lisp file's local variables."
   :type '(repeat (string :tag "Word"))
   :version "28.1")
-;;;###autoload(put 'checkdoc-symbol-words 'safe-local-variable 
#'checkdoc-list-of-strings-p)
+;;;###autoload(put 'checkdoc-symbol-words 'safe-local-variable 
#'list-of-strings-p)
 
 (defcustom checkdoc-column-zero-backslash-before-paren t
   "Non-nil means to warn if there is no \"\\\" before \"(\" in column zero.
@@ -364,9 +360,9 @@ large number of libraries means it is impractical to fix all
 of these warnings masse.  In almost any other case, setting
 this to anything but t is likely to be counter-productive.")
 
-;;;###autoload
 (defun checkdoc-list-of-strings-p (obj)
   "Return t when OBJ is a list of strings."
+  (declare (obsolete list-of-strings-p "29.1"))
   ;; this is a function so it might be shared by checkdoc-proper-noun-list
   ;; and/or checkdoc-ispell-lisp-words in the future
   (and (listp obj)
diff --git a/lisp/emacs-lisp/cl-extra.el b/lisp/emacs-lisp/cl-extra.el
index 607810ee14..7c7f027d77 100644
--- a/lisp/emacs-lisp/cl-extra.el
+++ b/lisp/emacs-lisp/cl-extra.el
@@ -772,7 +772,7 @@ PROPLIST is a list of the sort returned by `symbol-plist'.
       (help-insert-xref-button
        (help-fns-short-filename location)
        'cl-type-definition type location 'define-type)
-      (insert (substitute-command-keys "'")))
+      (insert (substitute-quotes "'")))
     (insert ".\n")
 
     ;; Parents.
@@ -782,7 +782,7 @@ PROPLIST is a list of the sort returned by `symbol-plist'.
         (insert " Inherits from ")
         (while (setq cur (pop pl))
           (setq cur (cl--class-name cur))
-          (insert (substitute-command-keys "`"))
+          (insert (substitute-quotes "`"))
           (help-insert-xref-button (symbol-name cur)
                                    'cl-help-type cur)
           (insert (substitute-command-keys (if pl "', " "'"))))
@@ -796,7 +796,7 @@ PROPLIST is a list of the sort returned by `symbol-plist'.
       (when ch
         (insert " Children ")
         (while (setq cur (pop ch))
-          (insert (substitute-command-keys "`"))
+          (insert (substitute-quotes "`"))
           (help-insert-xref-button (symbol-name cur)
                                    'cl-help-type cur)
           (insert (substitute-command-keys (if ch "', " "'"))))
@@ -815,10 +815,10 @@ PROPLIST is a list of the sort returned by `symbol-plist'.
       (when generics
         (insert (propertize "Specialized Methods:\n\n" 'face 'bold))
         (dolist (generic generics)
-          (insert (substitute-command-keys "`"))
+          (insert (substitute-quotes "`"))
           (help-insert-xref-button (symbol-name generic)
                                    'help-function generic)
-          (insert (substitute-command-keys "'"))
+          (insert (substitute-quotes "'"))
           (pcase-dolist (`(,qualifiers ,args ,doc)
                          (cl--generic-method-documentation generic type))
             (insert (format " %s%S\n" qualifiers args)
diff --git a/lisp/emacs-lisp/cl-generic.el b/lisp/emacs-lisp/cl-generic.el
index 0560ddda26..b3ade3b894 100644
--- a/lisp/emacs-lisp/cl-generic.el
+++ b/lisp/emacs-lisp/cl-generic.el
@@ -94,11 +94,6 @@
 ;; This second one is closely related to what we do here (and that's
 ;; the name "generalizer" comes from).
 
-;; The autoloads.el mechanism which adds package--builtin-versions
-;; maintenance to loaddefs.el doesn't work for preloaded packages (such
-;; as this one), so we have to do it by hand!
-(push (purecopy '(cl-generic 1 0)) package--builtin-versions)
-
 ;; Note: For generic functions that dispatch on several arguments (i.e. those
 ;; which use the multiple-dispatch feature), we always use the same "tagcodes"
 ;; and the same set of arguments on which to dispatch.  This works, but is
@@ -425,11 +420,13 @@ the specializer used will be the one returned by BODY."
                 ;; only called with explicit arguments.
                 (uses-cnm (macroexp--fgrep `((,cnm) (,nmp)) nbody))
                 (λ-lift (mapcar #'car uses-cnm)))
-           (if (not uses-cnm)
-               (cons nil
-                     `#'(lambda (,@args)
-                          ,@(car parsed-body)
-                          ,nbody))
+           (cond
+            ((not uses-cnm)
+             (cons nil
+                   `#'(lambda (,@args)
+                        ,@(car parsed-body)
+                        ,nbody)))
+            (lexical-binding
              (cons 'curried
                    `#'(lambda (,nm) ;Called when constructing the effective 
method.
                         (let ((,nmp (if (cl--generic-isnot-nnm-p ,nm)
@@ -465,7 +462,20 @@ the specializer used will be the one returned by BODY."
                               ;; A destructuring-bind would do the trick
                               ;; as well when/if it's more efficient.
                               (apply (lambda (,@λ-lift ,@args) ,nbody)
-                                     ,@λ-lift ,arglist)))))))))
+                                     ,@λ-lift ,arglist)))))))
+            (t
+             (cons t
+                 `#'(lambda (,cnm ,@args)
+                      ,@(car parsed-body)
+                      ,(macroexp-warn-and-return
+                        "cl-defmethod used without lexical-binding"
+                        (if (not (assq nmp uses-cnm))
+                            nbody
+                          `(let ((,nmp (lambda ()
+                                         (cl--generic-isnot-nnm-p ,cnm))))
+                             ,nbody))
+                        'lexical t)))))
+           ))
         (f (error "Unexpected macroexpansion result: %S" f))))))
 
 (put 'cl-defmethod 'function-documentation
diff --git a/lisp/emacs-lisp/cl-lib.el b/lisp/emacs-lisp/cl-lib.el
index a54fa21fa9..b83b44974d 100644
--- a/lisp/emacs-lisp/cl-lib.el
+++ b/lisp/emacs-lisp/cl-lib.el
@@ -89,12 +89,6 @@
 (defvar cl--optimize-speed 1)
 (defvar cl--optimize-safety 1)
 
-;;;###autoload
-(define-obsolete-variable-alias
-  ;; This alias is needed for compatibility with .elc files that use defstruct
-  ;; and were compiled with Emacs<24.3.
-  'custom-print-functions 'cl-custom-print-functions "24.3")
-
 ;;;###autoload
 (defvar cl-custom-print-functions nil
   "This is a list of functions that format user objects for printing.
diff --git a/lisp/emacs-lisp/cl-macs.el b/lisp/emacs-lisp/cl-macs.el
index 80ca43c902..beafee1d63 100644
--- a/lisp/emacs-lisp/cl-macs.el
+++ b/lisp/emacs-lisp/cl-macs.el
@@ -775,14 +775,34 @@ compared by `eql'.
 \(fn EXPR (KEYLIST BODY...)...)"
   (declare (indent 1) (debug (form &rest (sexp body))))
   (macroexp-let2 macroexp-copyable-p temp expr
-    (let* ((head-list nil))
+    (let* ((head-list nil)
+           (has-otherwise nil))
       `(cond
         ,@(mapcar
            (lambda (c)
-             (cons (cond ((memq (car c) '(t otherwise)) t)
+             (cons (cond (has-otherwise
+                          (error "Misplaced t or `otherwise' clause"))
+                         ((memq (car c) '(t otherwise))
+                          (setq has-otherwise t)
+                          t)
                          ((eq (car c) 'cl--ecase-error-flag)
                           `(error "cl-ecase failed: %s, %s"
                                   ,temp ',(reverse head-list)))
+                         ((null (car c))
+                          (macroexp-warn-and-return
+                           "Case nil will never match"
+                           nil 'suspicious))
+                         ((and (consp (car c)) (cdar c) (not (cddar c))
+                               (memq (caar c) '(quote function)))
+                          (macroexp-warn-and-return
+                           (format-message
+                            (concat "Case %s will match `%s'.  If "
+                                    "that's intended, write %s "
+                                    "instead.  Otherwise, don't "
+                                    "quote `%s'.")
+                            (car c) (caar c) (list (cadar c) (caar c))
+                            (cadar c))
+                           `(cl-member ,temp ',(car c)) 'suspicious))
                          ((listp (car c))
                           (setq head-list (append (car c) head-list))
                           `(cl-member ,temp ',(car c)))
@@ -2261,139 +2281,131 @@ This is like `cl-flet', but for macros instead of 
functions.
                                      (eval `(function (lambda ,@res)) t))
                               macroexpand-all-environment))))))
 
-(defun cl--sm-macroexpand (orig-fun exp &optional env)
+(defun cl--sm-macroexpand (exp &optional env)
+  "Special macro expander used inside `cl-symbol-macrolet'."
+  ;; FIXME: Arguably, this should be the official definition of `macroexpand'.
+  (while (not (eq exp (setq exp (macroexpand-1 exp env)))))
+  exp)
+
+(defun cl--sm-macroexpand-1 (orig-fun exp &optional env)
   "Special macro expander advice used inside `cl-symbol-macrolet'.
-This function extends `macroexpand' during macro expansion
+This function extends `macroexpand-1' during macro expansion
 of `cl-symbol-macrolet' to additionally expand symbol macros."
-  (let ((macroexpand-all-environment env)
+  (let ((exp (funcall orig-fun exp env))
         (venv (alist-get :cl-symbol-macros env)))
-    (while
-        (progn
-          (setq exp (funcall orig-fun exp env))
-          (pcase exp
-            ((pred symbolp)
-             ;; Perform symbol-macro expansion.
-             (let ((symval (assq exp venv)))
-               (when symval
-                 (setq exp (cadr symval)))))
-            (`(setq . ,args)
-             ;; Convert setq to setf if required by symbol-macro expansion.
-             (let ((convert nil)
-                   (rargs nil))
-               (while args
-                 (let ((place (pop args)))
-                   ;; Here, we know `place' should be a symbol.
-                   (while
-                       (let ((symval (assq place venv)))
-                         (when symval
-                           (setq place (cadr symval))
-                           (if (symbolp place)
-                               t        ;Repeat.
-                             (setq convert t)
-                             nil))))
-                   (push place rargs)
-                   (push (pop args) rargs)))
-               (setq exp (cons (if convert 'setf 'setq)
-                               (nreverse rargs)))
-               convert))
-            ;; CL's symbol-macrolet used to treat re-bindings as candidates for
-            ;; expansion (turning the let into a letf if needed), contrary to
-            ;; Common-Lisp where such re-bindings hide the symbol-macro.
-            ;; Not sure if there actually is code out there which depends
-            ;; on this behavior (haven't found any yet).
-            ;; Such code should explicitly use `cl-letf' instead, I think.
-            ;;
-            ;; (`(,(or `let `let*) . ,(or `(,bindings . ,body) 
pcase--dontcare))
-            ;;  (let ((letf nil) (found nil) (nbs ()))
-            ;;    (dolist (binding bindings)
-            ;;      (let* ((var (if (symbolp binding) binding (car binding)))
-            ;;             (sm (assq var venv)))
-            ;;        (push (if (not (cdr sm))
-            ;;                  binding
-            ;;                (let ((nexp (cadr sm)))
-            ;;                  (setq found t)
-            ;;                  (unless (symbolp nexp) (setq letf t))
-            ;;                  (cons nexp (cdr-safe binding))))
-            ;;              nbs)))
-            ;;    (when found
-            ;;      (setq exp `(,(if letf
-            ;;                       (if (eq (car exp) 'let) 'cl-letf 
'cl-letf*)
-            ;;                     (car exp))
-            ;;                  ,(nreverse nbs)
-            ;;                  ,@body)))))
-            ;;
-            ;; We implement the Common-Lisp behavior, instead (see bug#26073):
-            ;; The behavior of CL made sense in a dynamically scoped
-            ;; language, but nowadays, lexical scoping semantics is more often
-            ;; expected.
-            (`(,(or 'let 'let*) . ,(or `(,bindings . ,body) pcase--dontcare))
-             (let ((nbs ()) (found nil))
-               (dolist (binding bindings)
-                 (let* ((var (if (symbolp binding) binding (car binding)))
-                        (val (and found (consp binding) (eq 'let* (car exp))
-                                  (list (macroexpand-all (cadr binding)
-                                                         env)))))
-                   (push (if (assq var venv)
-                             ;; This binding should hide "its" surrounding
-                             ;; symbol-macro, but given the way macroexpand-all
-                             ;; works (i.e. the `env' we receive as input will
-                             ;; be (re)applied to the code we return), we can't
-                             ;; prevent application of `env' to the
-                             ;; sub-expressions, so we need to α-rename this
-                             ;; variable instead.
-                             (let ((nvar (make-symbol (symbol-name var))))
-                               (setq found t)
-                               (push (list var nvar) venv)
-                               (push (cons :cl-symbol-macros venv) env)
-                               (cons nvar (or val (cdr-safe binding))))
-                           (if val (cons var val) binding))
-                         nbs)))
-               (when found
-                 (setq exp `(,(car exp)
-                             ,(nreverse nbs)
-                             ,@(macroexp-unprogn
-                                (macroexpand-all (macroexp-progn body)
-                                                 env)))))
-               nil))
-            ;; Do the same as for `let' but for variables introduced
-            ;; via other means, such as `lambda' and `condition-case'.
-            (`(function (lambda ,args . ,body))
-             (let ((nargs ()) (found nil))
-               (dolist (var args)
-                 (push (cond
-                        ((memq var '(&optional &rest)) var)
-                        ((assq var venv)
-                         (let ((nvar (make-symbol (symbol-name var))))
-                           (setq found t)
-                           (push (list var nvar) venv)
-                           (push (cons :cl-symbol-macros venv) env)
-                           nvar))
-                        (t var))
-                       nargs))
-               (when found
-                 (setq exp `(function
-                             (lambda ,(nreverse nargs)
-                               . ,(mapcar (lambda (exp)
-                                            (macroexpand-all exp env))
-                                          body)))))
-               nil))
-            ((and `(condition-case ,var ,exp . ,clauses)
-                  (guard (assq var venv)))
-             (let ((nvar (make-symbol (symbol-name var))))
-               (push (list var nvar) venv)
-               (push (cons :cl-symbol-macros venv) env)
-               (setq exp
-                     `(condition-case ,nvar ,(macroexpand-all exp env)
-                        . ,(mapcar
-                            (lambda (clause)
-                              `(,(car clause)
-                                . ,(mapcar (lambda (exp)
-                                             (macroexpand-all exp env))
-                                           (cdr clause))))
-                            clauses)))
-               nil))
-            )))
-    exp))
+    (pcase exp
+      ((pred symbolp)
+       ;; Try symbol-macro expansion.
+       (let ((symval (assq exp venv)))
+         (if symval (cadr symval) exp)))
+      (`(setq . ,args)
+       ;; Convert setq to setf if required by symbol-macro expansion.
+       (let ((convert nil))
+         (while args
+           (let* ((place (pop args))
+                  ;; Here, we know `place' should be a symbol.
+                  (symval (assq place venv)))
+             (pop args)
+             (when symval
+               (setq convert t))))
+         (if convert
+             (cons 'setf (cdr exp))
+           exp)))
+      ;; CL's symbol-macrolet used to treat re-bindings as candidates for
+      ;; expansion (turning the let into a letf if needed), contrary to
+      ;; Common-Lisp where such re-bindings hide the symbol-macro.
+      ;; Not sure if there actually is code out there which depends
+      ;; on this behavior (haven't found any yet).
+      ;; Such code should explicitly use `cl-letf' instead, I think.
+      ;;
+      ;; (`(,(or `let `let*) . ,(or `(,bindings . ,body) pcase--dontcare))
+      ;;  (let ((letf nil) (found nil) (nbs ()))
+      ;;    (dolist (binding bindings)
+      ;;      (let* ((var (if (symbolp binding) binding (car binding)))
+      ;;             (sm (assq var venv)))
+      ;;        (push (if (not (cdr sm))
+      ;;                  binding
+      ;;                (let ((nexp (cadr sm)))
+      ;;                  (setq found t)
+      ;;                  (unless (symbolp nexp) (setq letf t))
+      ;;                  (cons nexp (cdr-safe binding))))
+      ;;              nbs)))
+      ;;    (when found
+      ;;      (setq exp `(,(if letf
+      ;;                       (if (eq (car exp) 'let) 'cl-letf 'cl-letf*)
+      ;;                     (car exp))
+      ;;                  ,(nreverse nbs)
+      ;;                  ,@body)))))
+      ;;
+      ;; We implement the Common-Lisp behavior, instead (see bug#26073):
+      ;; The behavior of CL made sense in a dynamically scoped
+      ;; language, but nowadays, lexical scoping semantics is more often
+      ;; expected.
+      (`(,(or 'let 'let*) . ,(or `(,bindings . ,body) pcase--dontcare))
+       (let ((nbs ()) (found nil))
+         (dolist (binding bindings)
+           (let* ((var (if (symbolp binding) binding (car binding)))
+                  (val (and found (consp binding) (eq 'let* (car exp))
+                            (list (macroexpand-all (cadr binding)
+                                                   env)))))
+             (push (if (assq var venv)
+                       ;; This binding should hide "its" surrounding
+                       ;; symbol-macro, but given the way macroexpand-all
+                       ;; works (i.e. the `env' we receive as input will
+                       ;; be (re)applied to the code we return), we can't
+                       ;; prevent application of `env' to the
+                       ;; sub-expressions, so we need to α-rename this
+                       ;; variable instead.
+                       (let ((nvar (make-symbol (symbol-name var))))
+                         (setq found t)
+                         (push (list var nvar) venv)
+                         (push (cons :cl-symbol-macros venv) env)
+                         (cons nvar (or val (cdr-safe binding))))
+                     (if val (cons var val) binding))
+                   nbs)))
+         (if found
+             `(,(car exp)
+               ,(nreverse nbs)
+               ,@(macroexp-unprogn
+                  (macroexpand-all (macroexp-progn body)
+                                   env)))
+           exp)))
+      ;; Do the same as for `let' but for variables introduced
+      ;; via other means, such as `lambda' and `condition-case'.
+      (`(function (lambda ,args . ,body))
+       (let ((nargs ()) (found nil))
+         (dolist (var args)
+           (push (cond
+                  ((memq var '(&optional &rest)) var)
+                  ((assq var venv)
+                   (let ((nvar (make-symbol (symbol-name var))))
+                     (setq found t)
+                     (push (list var nvar) venv)
+                     (push (cons :cl-symbol-macros venv) env)
+                     nvar))
+                  (t var))
+                 nargs))
+         (if found
+             `(function
+               (lambda ,(nreverse nargs)
+                 . ,(mapcar (lambda (exp)
+                              (macroexpand-all exp env))
+                            body)))
+           exp)))
+      ((and `(condition-case ,var ,exp . ,clauses)
+            (guard (assq var venv)))
+       (let ((nvar (make-symbol (symbol-name var))))
+         (push (list var nvar) venv)
+         (push (cons :cl-symbol-macros venv) env)
+         `(condition-case ,nvar ,(macroexpand-all exp env)
+            . ,(mapcar
+                (lambda (clause)
+                  `(,(car clause)
+                    . ,(mapcar (lambda (exp)
+                                 (macroexpand-all exp env))
+                               (cdr clause))))
+                clauses))))
+      (_ exp))))
 
 ;;;###autoload
 (defmacro cl-symbol-macrolet (bindings &rest body)
@@ -2412,7 +2424,8 @@ by EXPANSION, and (setq NAME ...) will act like (setf 
EXPANSION ...).
     (unwind-protect
         (progn
           (unless advised
-            (advice-add 'macroexpand :around #'cl--sm-macroexpand))
+            (advice-add 'macroexpand :override #'cl--sm-macroexpand)
+            (advice-add 'macroexpand-1 :around #'cl--sm-macroexpand-1))
           (let* ((venv (cdr (assq :cl-symbol-macros
                                   macroexpand-all-environment)))
                  (expansion
@@ -2428,7 +2441,8 @@ by EXPANSION, and (setq NAME ...) will act like (setf 
EXPANSION ...).
                    expansion nil nil rev-malformed-bindings))
               expansion)))
       (unless advised
-        (advice-remove 'macroexpand #'cl--sm-macroexpand)))))
+        (advice-remove 'macroexpand   #'cl--sm-macroexpand)
+        (advice-remove 'macroexpand-1 #'cl--sm-macroexpand-1)))))
 
 ;;;###autoload
 (defmacro cl-with-gensyms (names &rest body)
@@ -2762,11 +2776,17 @@ Each PLACE may be a symbol, or any generalized variable 
allowed by `setf'.
                            (funcall setter vold)))
                        binds))))
     (let* ((binding (car bindings))
-           (place (macroexpand (car binding) macroexpand-all-environment)))
+           (place (car binding)))
       (gv-letplace (getter setter) place
         (macroexp-let2 nil vnew (cadr binding)
-          (if (symbolp place)
+          (if (and (symbolp place)
+                   ;; `place' could be some symbol-macro.
+                   (eq place getter))
               ;; Special-case for simple variables.
+              ;; FIXME: We currently only use this special case when `place'
+              ;; is a simple var.  Should we also use it when the
+              ;; macroexpansion of `place' is a simple var (i.e. when
+              ;; getter+setter is the same as that of a simple var)?
               (cl--letf (cdr bindings)
                         (cons `(,getter ,(if (cdr binding) vnew getter))
                               simplebinds)
@@ -3105,7 +3125,7 @@ To see the documentation for a defined struct type, use
                   `(and ,pred-form t)))
             forms)
       (push `(eval-and-compile
-               (put ',name 'cl-deftype-satisfies ',predicate))
+               (define-symbol-prop ',name 'cl-deftype-satisfies ',predicate))
             forms))
     (let ((pos 0) (descp descs))
       (while descp
@@ -3570,7 +3590,7 @@ and then returning foo."
        (cl-defun ,fname ,(if (memq '&whole args) (delq '&whole args)
                            (cons '_cl-whole-arg args))
          ,@body)
-       (put ',func 'compiler-macro #',fname))))
+       (define-symbol-prop ',func 'compiler-macro #',fname))))
 
 ;;;###autoload
 (defun cl-compiler-macroexpand (form)
@@ -3679,8 +3699,8 @@ macro that returns its `&whole' argument."
 The type name can then be used in `cl-typecase', `cl-check-type', etc."
   (declare (debug cl-defmacro) (doc-string 3) (indent 2))
   `(cl-eval-when (compile load eval)
-     (put ',name 'cl-deftype-handler
-          (cl-function (lambda (&cl-defs ('*) ,@arglist) ,@body)))))
+     (define-symbol-prop ',name 'cl-deftype-handler
+                         (cl-function (lambda (&cl-defs ('*) ,@arglist) 
,@body)))))
 
 (cl-deftype extended-char () '(and character (not base-char)))
 ;; Define fixnum so `cl-typep' recognize it and the type check emitted
diff --git a/lisp/emacs-lisp/comp.el b/lisp/emacs-lisp/comp.el
index e10443588e..6656b7e57c 100644
--- a/lisp/emacs-lisp/comp.el
+++ b/lisp/emacs-lisp/comp.el
@@ -178,14 +178,15 @@ and above."
   :type '(repeat string)
   :version "28.1")
 
-(defcustom native-comp-driver-options nil
+(defcustom native-comp-driver-options (when (eq system-type 'darwin)
+                                        '("-Wl,-w"))
   "Options passed verbatim to the native compiler's back-end driver.
 Note that not all options are meaningful; typically only the options
 affecting the assembler and linker are likely to be useful.
 
 Passing these options is only available in libgccjit version 9
 and above."
-  :type '(repeat string)                ; FIXME is this right?
+  :type '(repeat string)
   :version "28.1")
 
 (defcustom comp-libgccjit-reproducer nil
@@ -462,7 +463,7 @@ Useful to hook into pass checkers.")
     (marker-buffer (function (marker) (or buffer null)))
     (markerp (function (t) boolean))
     (max (function ((or number marker) &rest (or number marker)) number))
-    (max-char (function () fixnum))
+    (max-char (function (&optional t) fixnum))
     (member (function (t list) list))
     (memory-limit (function () integer))
     (memq (function (t list) list))
@@ -3800,22 +3801,25 @@ Return the trampoline if found or nil otherwise."
          (lexical-binding t))
     (comp--native-compile
      form nil
-     (cl-loop
-      for dir in (if native-compile-target-directory
-                     (list (expand-file-name comp-native-version-dir
-                                             native-compile-target-directory))
-                   (comp-eln-load-path-eff))
-      for f = (expand-file-name
-               (comp-trampoline-filename subr-name)
-               dir)
-      unless (file-exists-p dir)
-        do (ignore-errors
-             (make-directory dir t)
-             (cl-return f))
-      when (file-writable-p f)
-        do (cl-return f)
-      finally (error "Cannot find suitable directory for output in \
-`native-comp-eln-load-path'")))))
+     ;; If we've disabled nativecomp, don't write the trampolines to
+     ;; the eln cache (but create them).
+     (and (not inhibit-automatic-native-compilation)
+          (cl-loop
+           for dir in (if native-compile-target-directory
+                          (list (expand-file-name comp-native-version-dir
+                                                  
native-compile-target-directory))
+                        (comp-eln-load-path-eff))
+           for f = (expand-file-name
+                    (comp-trampoline-filename subr-name)
+                    dir)
+           unless (file-exists-p dir)
+           do (ignore-errors
+                (make-directory dir t)
+                (cl-return f))
+           when (file-writable-p f)
+           do (cl-return f)
+           finally (error "Cannot find suitable directory for output in \
+`native-comp-eln-load-path'"))))))
 
 
 ;; Some entry point support code.
@@ -3935,8 +3939,11 @@ display a message."
          when (or native-comp-always-compile
                   load ; Always compile when the compilation is
                        ; commanded for late load.
-                  (file-newer-than-file-p
-                   source-file (comp-el-to-eln-filename source-file)))
+                  ;; Skip compilation if `comp-el-to-eln-filename' fails
+                  ;; to find a writable directory.
+                  (with-demoted-errors "Async compilation :%S"
+                    (file-newer-than-file-p
+                     source-file (comp-el-to-eln-filename source-file))))
          do (let* ((expr `((require 'comp)
                            (setq comp-async-compilation t)
                            (setq warning-fill-column most-positive-fixnum)
@@ -4041,7 +4048,6 @@ the deferred compilation mechanism."
             (list "Not a function symbol or file" function-or-file)))
   (catch 'no-native-compile
     (let* ((print-symbols-bare t)
-           (max-specpdl-size (max max-specpdl-size 5000))
            (data function-or-file)
            (comp-native-compiling t)
            (byte-native-qualities nil)
@@ -4104,6 +4110,7 @@ the deferred compilation mechanism."
                    comp-ctxt
                    (comp-ctxt-output comp-ctxt)
                    (file-exists-p (comp-ctxt-output comp-ctxt)))
+          (message "Deleting %s" (comp-ctxt-output comp-ctxt))
           (delete-file (comp-ctxt-output comp-ctxt)))))))
 
 (defun native-compile-async-skip-p (file load selector)
diff --git a/lisp/emacs-lisp/debug.el b/lisp/emacs-lisp/debug.el
index 460057b3af..f78d44cf98 100644
--- a/lisp/emacs-lisp/debug.el
+++ b/lisp/emacs-lisp/debug.el
@@ -110,10 +110,6 @@ The value used here is passed to `quit-restore-window'."
 (defvar debugger-previous-window-height nil
   "The last recorded height of `debugger-previous-window'.")
 
-(defvar debugger-previous-backtrace nil
-  "The contents of the previous backtrace (including text properties).
-This is to optimize `debugger-make-xrefs'.")
-
 (defvar debugger-outer-match-data)
 (defvar debugger-will-be-back nil
   "Non-nil if we expect to get back in the debugger soon.")
@@ -836,6 +832,10 @@ To specify a nil argument interactively, exit with an 
empty minibuffer."
 ;;;###autoload
 (defalias 'cancel-debug-watch #'cancel-debug-on-variable-change)
 
+(make-obsolete-variable 'debugger-previous-backtrace
+                        "no longer used." "29.1")
+(defvar debugger-previous-backtrace nil)
+
 (provide 'debug)
 
 ;;; debug.el ends here
diff --git a/lisp/emacs-lisp/easy-mmode.el b/lisp/emacs-lisp/easy-mmode.el
index c3a4e9fc7a..7d54a84687 100644
--- a/lisp/emacs-lisp/easy-mmode.el
+++ b/lisp/emacs-lisp/easy-mmode.el
@@ -417,7 +417,12 @@ No problems result if this variable is not bound.
          `(defvar ,keymap-sym
             (let ((m ,keymap))
               (cond ((keymapp m) m)
-                    ((listp m) (easy-mmode-define-keymap m))
+                     ;; FIXME: `easy-mmode-define-keymap' is obsolete,
+                     ;; so this form should also be obsolete somehow.
+                    ((listp m)
+                      (with-suppressed-warnings ((obsolete
+                                                  easy-mmode-define-keymap))
+                        (easy-mmode-define-keymap m)))
                     (t (error "Invalid keymap %S" m))))
             ,(format "Keymap for `%s'." mode-name)))
 
@@ -679,6 +684,7 @@ Valid keywords and arguments are:
   :group     Ignored.
   :suppress  Non-nil to call `suppress-keymap' on keymap,
              `nodigits' to suppress digits as prefix arguments."
+  (declare (obsolete define-keymap "29.1"))
   (let (inherit dense suppress)
     (while args
       (let ((key (pop args))
@@ -719,9 +725,7 @@ The M, BS, and ARGS arguments are as per that function.  
DOC is
 the constant's documentation.
 
 This macro is deprecated; use `defvar-keymap' instead."
-  ;; FIXME: Declare obsolete in favor of `defvar-keymap'.  It is still
-  ;; used for `gud-menu-map' and `gud-minor-mode-map', so fix that first.
-  (declare (doc-string 3) (indent 1))
+  (declare (doc-string 3) (indent 1) (obsolete defvar-keymap "29.1"))
   `(defconst ,m
      (easy-mmode-define-keymap ,bs nil (if (boundp ',m) ,m) ,(cons 'list args))
      ,doc))
diff --git a/lisp/emacs-lisp/edebug.el b/lisp/emacs-lisp/edebug.el
index 9de8999fdf..67704bdb51 100644
--- a/lisp/emacs-lisp/edebug.el
+++ b/lisp/emacs-lisp/edebug.el
@@ -129,7 +129,7 @@ contains an infinite loop.  When Edebug is instrumenting 
code
 containing very large quoted lists, it may reach this limit and give
 the error message \"Too deep - perhaps infinite loop in spec?\".
 Make this limit larger to countermand that, but you may also need to
-increase `max-lisp-eval-depth' and `max-specpdl-size'."
+increase `max-lisp-eval-depth'."
   :type 'integer
   :version "26.1")
 
@@ -1107,8 +1107,7 @@ purpose by adding an entry to this alist, and setting
        edebug-best-error
        edebug-error-point
        ;; Do this once here instead of several times.
-       (max-lisp-eval-depth (+ 800 max-lisp-eval-depth))
-       (max-specpdl-size (+ 2000 max-specpdl-size)))
+       (max-lisp-eval-depth (+ 800 max-lisp-eval-depth)))
     (let ((no-match
            (catch 'no-match
              (setq result (edebug-read-and-maybe-wrap-form1))
@@ -2317,7 +2316,6 @@ and run its entry function, and set up `edebug-before' and
               ;; but not inside an unwind-protect.
               ;; Doing it here also keeps it from growing too large.
               (max-lisp-eval-depth (+ 100 max-lisp-eval-depth)) ; too much??
-              (max-specpdl-size (+ 200 max-specpdl-size))
 
               (debugger edebug-debugger) ; only while edebug is active.
               (edebug-outside-debug-on-error debug-on-error)
@@ -3791,9 +3789,6 @@ limited by `edebug-print-length' or `edebug-print-level'."
 
 ;;; Edebug Minor Mode
 
-(define-obsolete-variable-alias 'gud-inhibit-global-bindings
-  'edebug-inhibit-emacs-lisp-mode-bindings "24.3")
-
 (defvar edebug-inhibit-emacs-lisp-mode-bindings nil
   "If non-nil, inhibit Edebug bindings on the C-x C-a key.
 By default, loading the `edebug' library causes these bindings to
@@ -4182,6 +4177,7 @@ from Edebug instrumentation found in the backtrace."
     (backtrace-mode)
     (add-hook 'backtrace-goto-source-functions
               #'edebug--backtrace-goto-source nil t))
+  (edebug-backtrace-mode)
   (setq edebug-instrumented-backtrace-frames
         (backtrace-get-frames 'edebug-debugger
                               :constructor #'edebug--make-frame)
@@ -4258,6 +4254,14 @@ Save DEF-NAME, BEFORE-INDEX and AFTER-INDEX in FRAME."
   (setf (edebug--frame-before-index frame) before-index)
   (setf (edebug--frame-after-index frame) after-index))
 
+(defvar-keymap edebug-backtrace-mode-map
+  "s" #'backtrace-goto-source)
+
+(define-minor-mode edebug-backtrace-mode
+  "Minor mode for showing backtraces from edebug."
+  :lighter nil
+  :interactive nil)
+
 (defun edebug--backtrace-goto-source ()
   (let* ((index (backtrace-get-index))
          (frame (nth index backtrace-frames)))
@@ -4567,6 +4571,12 @@ With prefix argument, make it a temporary breakpoint."
         (was-macro               `(macro . ,unwrapped))
         (t                       unwrapped))))))
 
+(defun edebug--strip-plist (symbol)
+  "Remove edebug related properties from plist for SYMBOL."
+  (dolist (prop '( edebug edebug-behavior edebug-coverage
+                   edebug-freq-count ghost-edebug))
+    (cl-remprop symbol prop)))
+
 (defun edebug-remove-instrumentation (functions)
   "Remove Edebug instrumentation from FUNCTIONS.
 Interactively, the user is prompted for the function to remove
@@ -4598,6 +4608,7 @@ instrumentation for, defaulting to all functions."
   (dolist (symbol functions)
     (when-let ((unwrapped
                 (edebug--unwrap*-symbol-function symbol)))
+      (edebug--strip-plist symbol)
       (defalias symbol unwrapped)))
   (message "Removed edebug instrumentation from %s"
            (mapconcat #'symbol-name functions ", ")))
diff --git a/lisp/emacs-lisp/eieio-core.el b/lisp/emacs-lisp/eieio-core.el
index 5e7b5cbfb2..65aa6aa6df 100644
--- a/lisp/emacs-lisp/eieio-core.el
+++ b/lisp/emacs-lisp/eieio-core.el
@@ -249,16 +249,22 @@ use '%s or turn off `eieio-backward-compatibility' 
instead" cname)
 (defun eieio-make-class-predicate (class)
   (lambda (obj)
     (:documentation
-     (format "Return non-nil if OBJ is an object of type `%S'.\n\n(fn OBJ)"
-             class))
+     (concat
+      (internal--format-docstring-line
+       "Return non-nil if OBJ is an object of type `%S'."
+       class)
+      "\n\n(fn OBJ)"))
     (and (eieio-object-p obj)
          (same-class-p obj class))))
 
 (defun eieio-make-child-predicate (class)
   (lambda (obj)
     (:documentation
-     (format "Return non-nil if OBJ is an object of type `%S' or a subclass.
-\n(fn OBJ)" class))
+     (concat
+      (internal--format-docstring-line
+       "Return non-nil if OBJ is an object of type `%S' or a subclass."
+       class)
+      "\n\n(fn OBJ)"))
     (and (eieio-object-p obj)
          (object-of-class-p obj class))))
 
@@ -353,8 +359,8 @@ See `defclass' for more information."
         (defalias csym
           (lambda (obj)
             (:documentation
-             (format
-              "Test OBJ to see if it a list of objects which are a child of 
type %s"
+             (internal--format-docstring-line
+              "Test OBJ to see if it a list of objects which are a child of 
type `%s'."
               cname))
             (when (listp obj)
               (let ((ans t)) ;; nil is valid
diff --git a/lisp/emacs-lisp/eieio-opt.el b/lisp/emacs-lisp/eieio-opt.el
index 5f67263f17..b599aabb7f 100644
--- a/lisp/emacs-lisp/eieio-opt.el
+++ b/lisp/emacs-lisp/eieio-opt.el
@@ -153,7 +153,7 @@ are not abstract."
         (help-insert-xref-button
         (help-fns-short-filename location)
         'cl-type-definition ctr location 'define-type)
-       (insert (substitute-command-keys "'")))
+        (insert (substitute-quotes "'")))
       (insert ".\nCreates an object of class " (symbol-name ctr) ".")
       (goto-char (point-max))
       (if (autoloadp def)
diff --git a/lisp/emacs-lisp/eieio.el b/lisp/emacs-lisp/eieio.el
index 984166b593..8351d97b13 100644
--- a/lisp/emacs-lisp/eieio.el
+++ b/lisp/emacs-lisp/eieio.el
@@ -136,6 +136,7 @@ and reference them using the function `class-option'."
         (accessors ()))
 
     ;; Collect the accessors we need to define.
+    (setq slots (mapcar (lambda (x) (if (consp x) x (list x))) slots))
     (pcase-dolist (`(,sname . ,soptions) slots)
       (let* ((acces   (plist-get soptions :accessor))
             (initarg (plist-get soptions :initarg))
@@ -217,10 +218,11 @@ and reference them using the function `class-option'."
           (when (and eieio-backward-compatibility (eq alloc :class))
             ;; FIXME: How could I declare this *method* as obsolete.
             (push `(cl-defmethod ,acces ((this (subclass ,name)))
-                     ,(format
-                       "Retrieve the class slot `%S' from a class `%S'.
-This method is obsolete."
-                       sname name)
+                     ,(concat
+                       (internal--format-docstring-line
+                        "Retrieve the class slot `%S' from a class `%S'."
+                        sname name)
+                       "\nThis method is obsolete.")
                      (if (slot-boundp this ',sname)
                          (eieio-oref-default this ',sname)))
                   accessors)))
@@ -229,16 +231,18 @@ This method is obsolete."
        ;; name whose purpose is to set the value of the slot.
        (if writer
             (push `(cl-defmethod ,writer ((this ,name) value)
-                     ,(format "Set the slot `%S' of an object of class `%S'."
-                              sname name)
+                     ,(internal--format-docstring-line
+                       "Set the slot `%S' of an object of class `%S'."
+                       sname name)
                      (setf (slot-value this ',sname) value))
                   accessors))
        ;; If a reader is defined, then create a generic method
        ;; of that name whose purpose is to access this slot value.
        (if reader
             (push `(cl-defmethod ,reader ((this ,name))
-                     ,(format "Access the slot `%S' from object of class `%S'."
-                              sname name)
+                     ,(internal--format-docstring-line
+                       "Access the slot `%S' from object of class `%S'."
+                       sname name)
                      (slot-value this ',sname))
                   accessors))
        ))
diff --git a/lisp/emacs-lisp/ert-x.el b/lisp/emacs-lisp/ert-x.el
index 4436d0a4b1..a891f068a7 100644
--- a/lisp/emacs-lisp/ert-x.el
+++ b/lisp/emacs-lisp/ert-x.el
@@ -102,6 +102,43 @@ the name of the test and the result of NAME-FORM."
            (indent 1))
   `(ert--call-with-test-buffer ,name-form (lambda () ,@body)))
 
+(cl-defmacro ert-with-test-buffer-selected ((&key name)
+                                            &body body)
+  "Create a test buffer, switch to it, and run BODY.
+
+This extends `ert-with-test-buffer' by displaying the test
+buffer (whose name is derived from NAME) in a temporary window.
+The temporary window becomes the `selected-window' before BODY is
+evaluated.  The modification hooks `before-change-functions' and
+`after-change-functions' are not inhibited during the evaluation
+of BODY, which makes it easier to use `execute-kbd-macro' to
+simulate user interaction.  The window configuration is restored
+before returning, even if BODY exits nonlocally.  The return
+value is the last form in BODY."
+  (declare (debug ((":name" form) def-body))
+           (indent 1))
+  (let ((ret (make-symbol "ert--with-test-buffer-selected-ret")))
+    `(save-window-excursion
+       (let (,ret)
+         (ert-with-test-buffer (:name ,name)
+           (with-current-buffer-window (current-buffer)
+               `(display-buffer-below-selected
+                 (body-function
+                  . ,(lambda (window)
+                       (select-window window t)
+                       ;; body-function is intended to initialize the
+                       ;; contents of a temporary read-only buffer, so
+                       ;; it is executed with some convenience
+                       ;; changes.  Undo those changes so that the
+                       ;; test buffer behaves more like an ordinary
+                       ;; buffer while the body executes.
+                       (let ((inhibit-modification-hooks nil)
+                             (inhibit-read-only nil)
+                             (buffer-read-only nil))
+                         (setq ,ret (progn ,@body))))))
+             nil))
+         ,ret))))
+
 ;;;###autoload
 (defun ert-kill-all-test-buffers ()
   "Kill all test buffers that are still live."
@@ -422,6 +459,10 @@ The following keyword arguments are supported:
 :text STRING    If non-nil, pass STRING to `make-temp-file' as
                 the TEXT argument.
 
+:buffer SYMBOL  Open the temporary file using `find-file-noselect'
+                and bind SYMBOL to the buffer.  Kill the buffer
+                after BODY exits normally or non-locally.
+
 :coding CODING  If non-nil, bind `coding-system-for-write' to CODING
                 when executing BODY.  This is handy when STRING includes
                 non-ASCII characters or the temporary file must have a
@@ -430,14 +471,17 @@ The following keyword arguments are supported:
 See also `ert-with-temp-directory'."
   (declare (indent 1) (debug (symbolp body)))
   (cl-check-type name symbol)
-  (let (keyw prefix suffix directory text extra-keywords coding)
+  (let (keyw prefix suffix directory text extra-keywords buffer coding)
     (while (keywordp (setq keyw (car body)))
       (setq body (cdr body))
       (pcase keyw
         (:prefix (setq prefix (pop body)))
         (:suffix (setq suffix (pop body)))
+        ;; This is only for internal use by `ert-with-temp-directory'
+        ;; and is therefore not documented.
         (:directory (setq directory (pop body)))
         (:text (setq text (pop body)))
+        (:buffer (setq buffer (pop body)))
         (:coding (setq coding (pop body)))
         (_ (push keyw extra-keywords) (pop body))))
     (when extra-keywords
@@ -452,9 +496,16 @@ See also `ert-with-temp-directory'."
                            (make-temp-file ,prefix ,directory ,suffix ,text)))
               (,name ,(if directory
                           `(file-name-as-directory ,temp-file)
-                        temp-file)))
+                        temp-file))
+              ,@(when buffer
+                  (list `(,buffer (find-file-literally ,temp-file)))))
          (unwind-protect
              (progn ,@body)
+           (ignore-errors
+             ,@(when buffer
+                 (list `(with-current-buffer buf
+                          (set-buffer-modified-p nil))
+                       `(kill-buffer ,buffer))))
            (ignore-errors
              ,(if directory
                   `(delete-directory ,temp-file :recursive)
@@ -517,7 +568,7 @@ The same keyword arguments are supported as in
          `("\\`mock\\'" nil ,(system-name)))
         ;; Emacs's Makefile sets $HOME to a nonexistent value.  Needed
         ;; in batch mode only, therefore.
-        (unless (and (null noninteractive) (file-directory-p "~/"))
+        (when (and noninteractive (not (file-directory-p "~/")))
           (setenv "HOME" temporary-file-directory))
         (format "/mock::%s" temporary-file-directory))))
     "Temporary directory for remote file tests.")
diff --git a/lisp/emacs-lisp/generate-lisp-file.el 
b/lisp/emacs-lisp/generate-lisp-file.el
index 8896a3f701..7b087a4ecb 100644
--- a/lisp/emacs-lisp/generate-lisp-file.el
+++ b/lisp/emacs-lisp/generate-lisp-file.el
@@ -63,12 +63,12 @@ inserted."
 
 (cl-defun generate-lisp-file-trailer (file &key version inhibit-provide
                                       (coding 'utf-8-emacs-unix) autoloads
-                                      compile provide)
+                                      compile provide inhibit-native-compile)
   "Insert a standard trailer for FILE.
 By default, this trailer inhibits version control, byte
 compilation, updating autoloads, and uses a `utf-8-emacs-unix'
 coding system.  These can be inhibited by providing non-nil
-values to the VERSION, NO-PROVIDE, AUTOLOADS and COMPILE
+values to the VERSION, AUTOLOADS, COMPILE and NATIVE-COMPILE
 keyword arguments.
 
 CODING defaults to `utf-8-emacs-unix'.  Use a nil value to
@@ -79,7 +79,11 @@ If PROVIDE is non-nil, use that in the `provide' statement
 instead of using FILE as the basis.
 
 If `standard-output' is bound to a buffer, insert in that buffer.
-If no, insert at point in the current buffer."
+If no, insert at point in the current buffer.
+
+If INHITBIT-NATIVE-COMPILE is non-nil, add a cookie to inhibit
+native compilation.  (By default, a file will be native-compiled
+if it's also byte-compiled)."
   (with-current-buffer (if (bufferp standard-output)
                            standard-output
                          (current-buffer))
@@ -96,9 +100,11 @@ If no, insert at point in the current buffer."
     (unless version
       (insert ";; version-control: never\n"))
     (unless compile
-      (insert ";; no-byte-" "compile: t\n")) ;; #$ is byte-compiled into nil.
+      (insert ";; no-byte-" "compile: t\n"))
     (unless autoloads
       (insert ";; no-update-autoloads: t\n"))
+    (when inhibit-native-compile
+      (insert ";; no-native-" "compile: t\n"))
     (when coding
       (insert (format ";; coding: %s\n"
                       (if (eq coding t)
diff --git a/lisp/emacs-lisp/gv.el b/lisp/emacs-lisp/gv.el
index eaab6439ad..a96fa19a3f 100644
--- a/lisp/emacs-lisp/gv.el
+++ b/lisp/emacs-lisp/gv.el
@@ -87,7 +87,11 @@ with a (not necessarily copyable) Elisp expression that 
returns the value to
 set it to.
 DO must return an Elisp expression."
   (cond
-   ((symbolp place) (funcall do place (lambda (v) `(setq ,place ,v))))
+   ((symbolp place)
+    (let ((me (macroexpand-1 place macroexpand-all-environment)))
+      (if (eq me place)
+          (funcall do place (lambda (v) `(setq ,place ,v)))
+        (gv-get me do))))
    ((not (consp place)) (signal 'gv-invalid-place (list place)))
    (t
     (let* ((head (car place))
@@ -532,13 +536,15 @@ The return value is the last VAL in the list.
        (funcall do `(error . ,args)
                 (lambda (v) `(progn ,v (error . ,args))))))
 
-(defmacro gv-synthetic-place (getter setter)
+(defun gv-synthetic-place (getter setter)
   "Special place described by its setter and getter.
 GETTER and SETTER (typically obtained via `gv-letplace') get and
-set that place.  I.e. This macro allows you to do the \"reverse\" of what
-`gv-letplace' does.
-This macro only makes sense when used in a place."
-  (declare (gv-expander funcall))
+set that place.  I.e. this function allows you to do the
+\"reverse\" of what `gv-letplace' does.
+
+This function is only useful when used in conjunction with
+generalized variables in place forms."
+  (declare (gv-expander funcall) (compiler-macro (lambda (_) getter)))
   (ignore setter)
   getter)
 
@@ -806,6 +812,7 @@ REF must have been previously obtained with `gv-ref'."
                    `(cond
                      (,v ,(funcall setter val))
                      ((eq ,getter ,val) ,(funcall setter `(not ,val))))))))))
+(make-obsolete-generalized-variable 'eq nil "29.1")
 
 (gv-define-expander substring
   (lambda (do place from &optional to)
diff --git a/lisp/emacs-lisp/icons.el b/lisp/emacs-lisp/icons.el
index 93749a3451..a08ac7463c 100644
--- a/lisp/emacs-lisp/icons.el
+++ b/lisp/emacs-lisp/icons.el
@@ -202,7 +202,11 @@ present if the icon is represented by an image."
                             :height (if (eq height 'line)
                                         (window-default-line-height)
                                       height)
-                            :scale 1)
+                            :scale 1
+                            :rotation (or (plist-get keywords :rotation) 0)
+                            :ascent (if (plist-member keywords :ascent)
+                                        (plist-get keywords :ascent)
+                                      'center))
             (create-image file))))))
 
 (cl-defmethod icons--create ((_type (eql 'emoji)) icon _keywords)
diff --git a/lisp/emacs-lisp/lisp-mode.el b/lisp/emacs-lisp/lisp-mode.el
index c906ee6e31..7e39a77aed 100644
--- a/lisp/emacs-lisp/lisp-mode.el
+++ b/lisp/emacs-lisp/lisp-mode.el
@@ -325,6 +325,20 @@ This will generate compile-time constants from BINDINGS."
               (throw 'matched t)))
         (throw 'matched nil)))))
 
+(defun lisp-mode--search-key (char bound)
+  (catch 'found
+    (while (re-search-forward
+            (concat "\\_<" char (rx lisp-mode-symbol) "\\_>")
+            bound t)
+      (when (or (< (match-beginning 0) (+ (point-min) 2))
+                ;; A quoted white space before the &/: means that this
+                ;; is not the start of a :keyword or an &option.
+                (not (eql (char-after (- (match-beginning 0) 2))
+                          ?\\))
+                (not (memq (char-after (- (match-beginning 0) 1))
+                           '(?\s ?\n ?\t))))
+        (throw 'found t)))))
+
 (let-when-compile
     ((lisp-fdefs '("defmacro" "defun"))
      (lisp-vdefs '("defvar"))
@@ -496,11 +510,11 @@ This will generate compile-time constants from BINDINGS."
          (,(rx "\\\\=")
           (0 font-lock-builtin-face prepend))
          ;; Constant values.
-         (,(concat "\\_<:" (rx lisp-mode-symbol) "\\_>")
+         (,(lambda (bound) (lisp-mode--search-key ":" bound))
           (0 font-lock-builtin-face))
          ;; ELisp and CLisp `&' keywords as types.
-         (,(concat "\\_<&" (rx lisp-mode-symbol) "\\_>")
-          . font-lock-type-face)
+         (,(lambda (bound) (lisp-mode--search-key "&" bound))
+          (0 font-lock-builtin-face))
          ;; ELisp regexp grouping constructs
          (,(lambda (bound)
              (catch 'found
@@ -549,11 +563,12 @@ This will generate compile-time constants from BINDINGS."
          ;; must come before keywords below to have effect
          (,(concat "#:" (rx lisp-mode-symbol) "") 0 font-lock-builtin-face)
          ;; Constant values.
-         (,(concat "\\_<:" (rx lisp-mode-symbol) "\\_>")
+         (,(lambda (bound) (lisp-mode--search-key ":" bound))
           (0 font-lock-builtin-face))
          ;; ELisp and CLisp `&' keywords as types.
-         (,(concat "\\_<&" (rx lisp-mode-symbol) "\\_>")
-          . font-lock-type-face)
+         (,(lambda (bound) (lisp-mode--search-key "&" bound))
+          (0 font-lock-builtin-face))
+         ;; ELisp regexp grouping constructs
          ;; This is too general -- rms.
          ;; A user complained that he has functions whose names start with `do'
          ;; and that they get the wrong color.
diff --git a/lisp/emacs-lisp/loaddefs-gen.el b/lisp/emacs-lisp/loaddefs-gen.el
index e13b92bab8..964d23c770 100644
--- a/lisp/emacs-lisp/loaddefs-gen.el
+++ b/lisp/emacs-lisp/loaddefs-gen.el
@@ -287,10 +287,14 @@ expression, in which case we want to handle forms 
differently."
       ;; In Emacs this is normally handled separately by cus-dep.el, but for
       ;; third party packages, it can be convenient to explicitly autoload
       ;; a group.
-      (let ((groupname (nth 1 form)))
+      (let ((groupname (nth 1 form))
+            (parent (eval (plist-get form :group) t)))
         `(let ((loads (get ',groupname 'custom-loads)))
            (if (member ',file loads) nil
-             (put ',groupname 'custom-loads (cons ',file loads))))))
+             (put ',groupname 'custom-loads (cons ',file loads))
+             ,@(when parent
+               `((put ',parent 'custom-loads
+                      (cons ',groupname (get ',parent 'custom-loads)))))))))
 
      ;; When processing a macro expansion, any expression
      ;; before a :autoload-end should be included.  These are typically (put
@@ -504,6 +508,7 @@ If COMPILE, don't include a \"don't compile\" cookie."
       (generate-lisp-file-trailer
        file :provide (and (stringp feature) feature)
        :compile compile
+       :inhibit-native-compile t
        :inhibit-provide (not feature))
       (buffer-string))))
 
@@ -511,7 +516,7 @@ If COMPILE, don't include a \"don't compile\" cookie."
 (defun loaddefs-generate (dir output-file &optional excluded-files
                               extra-data include-package-version
                               generate-full)
-  "Generate loaddefs files for Lisp files in the directories DIRS.
+  "Generate loaddefs files for Lisp files in one or more directories given by 
DIR.
 DIR can be either a single directory or a list of directories.
 
 The autoloads will be written to OUTPUT-FILE.  If any Lisp file
@@ -519,7 +524,7 @@ binds `generated-autoload-file' as a file-local variable, 
write
 its autoloads into the specified file instead.
 
 The function does NOT recursively descend into subdirectories of the
-directory or directories specified by DIRS.
+directories specified by DIR.
 
 Optional argument EXCLUDED-FILES, if non-nil, should be a list of
 files, such as preloaded files, whose autoloads should not be written
@@ -627,7 +632,7 @@ instead of just updating them with the new/changed 
autoloads."
                       ;; It's a new file; put the data at the end.
                       (progn
                         (goto-char (point-max))
-                        (search-backward "\f\n"))
+                        (search-backward "\f\n" nil t))
                     ;; Delete the old version of the section.
                     (delete-region (match-beginning 0)
                                    (and (search-forward "\n\f\n;;;")
diff --git a/lisp/emacs-lisp/macroexp.el b/lisp/emacs-lisp/macroexp.el
index c3ba1b36d4..f4df40249d 100644
--- a/lisp/emacs-lisp/macroexp.el
+++ b/lisp/emacs-lisp/macroexp.el
@@ -110,7 +110,8 @@ each clause."
       (let ((symbols-with-pos-enabled t))
         (apply handler form (cdr form)))
     (error
-     (message "Compiler-macro error for %S: Handler: %S\n%S" (car form) 
handler err)
+     (message "Warning: Optimization failure for %S: Handler: %S\n%S"
+              (car form) handler err)
      form)))
 
 (defun macroexp--funcall-if-compiled (_form)
diff --git a/lisp/emacs-lisp/nadvice.el b/lisp/emacs-lisp/nadvice.el
index a9a20ab5ab..429052bfdf 100644
--- a/lisp/emacs-lisp/nadvice.el
+++ b/lisp/emacs-lisp/nadvice.el
@@ -4,6 +4,7 @@
 
 ;; Author: Stefan Monnier <monnier@iro.umontreal.ca>
 ;; Keywords: extensions, lisp, tools
+;; Version: 1.0
 
 ;; This file is part of GNU Emacs.
 
@@ -37,11 +38,6 @@
 
 ;;; Code:
 
-;; The autoloads.el mechanism which adds package--builtin-versions
-;; maintenance to loaddefs.el doesn't work for preloaded packages (such
-;; as this one), so we have to do it by hand!
-(push (purecopy '(nadvice 1 0)) package--builtin-versions)
-
 (oclosure-define (advice
                   (:predicate advice--p)
                   (:copier advice--cons (cdr))
@@ -108,19 +104,26 @@ DOC is a string where \"FUNCTION\" and \"OLDFUN\" are 
expected.")
                    (format "%s\n%s" name doc)
                  (format "%s" name))
              (or doc "No documentation")))))
-     "\n")))
+     "\n"
+     (and
+      (eq how :override)
+      (concat
+       (format-message
+        "\nThis is an :override advice, which means that `%s' isn't\n" 
function)
+       "run at all, and the documentation below may be irrelevant.\n")))))
 
 (defun advice--make-docstring (function)
   "Build the raw docstring for FUNCTION, presumably advised."
   (let* ((flist (indirect-function function))
          (docfun nil)
          (macrop (eq 'macro (car-safe flist)))
-         (docstring nil))
+         (before nil)
+         (after nil))
     (when macrop
       (setq flist (cdr flist)))
     (if (and (autoloadp flist)
              (get function 'advice--pending))
-        (setq docstring
+        (setq after
               (advice--make-single-doc (get function 'advice--pending)
                                        function macrop))
       (while (advice--p flist)
@@ -130,9 +133,13 @@ DOC is a string where \"FUNCTION\" and \"OLDFUN\" are 
expected.")
         ;; object instead!  So here we try to undo the damage.
         (when (integerp (aref flist 4))
           (setq docfun flist))
-        (setq docstring (concat docstring (advice--make-single-doc
-                                           flist function macrop))
-              flist (advice--cdr flist))))
+        (let ((doc-bit (advice--make-single-doc flist function macrop)))
+          ;; We want :overrides to go to the front, because they mean
+          ;; that the doc string may be irrelevant.
+          (if (eq (advice--how flist) :override)
+              (setq before (concat before doc-bit))
+            (setq after (concat after doc-bit))))
+        (setq flist (advice--cdr flist))))
     (unless docfun
       (setq docfun flist))
     (let* ((origdoc (unless (eq function docfun) ;Avoid inf-loops.
@@ -145,12 +152,18 @@ DOC is a string where \"FUNCTION\" and \"OLDFUN\" are 
expected.")
                         (if (stringp arglist) t
                           (help--make-usage-docstring function arglist)))
                     (setq origdoc (cdr usage)) (car usage)))
-      (help-add-fundoc-usage (concat origdoc
-                                     (if (string-suffix-p "\n" origdoc)
-                                         "\n"
-                                       "\n\n")
-                                     docstring)
-                             usage))))
+      (help-add-fundoc-usage
+       (with-temp-buffer
+         (when before
+           (insert before)
+           (ensure-empty-lines 1))
+         (when origdoc
+           (insert origdoc))
+         (when after
+           (ensure-empty-lines 1)
+           (insert after))
+         (buffer-string))
+       usage))))
 
 (defun advice-eval-interactive-spec (spec)
   "Evaluate the interactive spec SPEC."
diff --git a/lisp/emacs-lisp/oclosure.el b/lisp/emacs-lisp/oclosure.el
index 9775e8cc65..c77ac151d7 100644
--- a/lisp/emacs-lisp/oclosure.el
+++ b/lisp/emacs-lisp/oclosure.el
@@ -557,6 +557,21 @@ This has 2 uses:
 (oclosure-define (save-some-buffers-function
                   (:predicate save-some-buffers-function--p)))
 
+;; This OClosure type is used internally by `cconv.el' to handle
+;; the case where we need to build a closure whose `interactive' spec
+;; captures variables from the context.
+;; It arguably belongs with `cconv.el' but is needed at runtime,
+;; so we placed it here.
+(oclosure-define (cconv--interactive-helper) fun if)
+(defun cconv--interactive-helper (fun if)
+  "Add interactive \"form\" IF to FUN.
+Returns a new command that otherwise behaves like FUN.
+IF should actually not be a form but a function of no arguments."
+  (oclosure-lambda (cconv--interactive-helper (fun fun) (if if))
+      (&rest args)
+    (apply (if (called-interactively-p 'any)
+               #'funcall-interactively #'funcall)
+           fun args)))
 
 (provide 'oclosure)
 ;;; oclosure.el ends here
diff --git a/lisp/emacs-lisp/package.el b/lisp/emacs-lisp/package.el
index ed23ee5f22..4abee9d053 100644
--- a/lisp/emacs-lisp/package.el
+++ b/lisp/emacs-lisp/package.el
@@ -2189,8 +2189,8 @@ to install it but still mark it as selected."
              (assq (car elt) package-archive-contents)))
         (and available
              (version-list-<
-              (package-desc-priority-version (cadr elt))
-              (package-desc-priority-version (cadr available))))))
+              (package-desc-version (cadr elt))
+              (package-desc-version (cadr available))))))
     package-alist)))
 
 ;;;###autoload
@@ -2648,7 +2648,7 @@ Helper function for `describe-package'."
                         "',\n             shadowing a ")
                        (propertize "built-in package"
                                    'font-lock-face 'package-status-built-in))
-             (insert (substitute-command-keys "'")))
+             (insert (substitute-quotes "'")))
            (if signed
                (insert ".")
              (insert " (unsigned)."))
@@ -3700,30 +3700,34 @@ objects removed."
     `((delete . ,del) (install . ,ins) (upgrade . ,upg))))
 
 (defun package-menu--perform-transaction (install-list delete-list)
-  "Install packages in INSTALL-LIST and delete DELETE-LIST."
-  (if install-list
-      (let ((status-format (format ":Installing %%d/%d"
-                             (length install-list)))
-            (i 0)
-            (package-menu--transaction-status))
-        (dolist (pkg install-list)
-          (setq package-menu--transaction-status
-                (format status-format (cl-incf i)))
-          (force-mode-line-update)
-          (redisplay 'force)
-          ;; Don't mark as selected, `package-menu-execute' already
-          ;; does that.
-          (package-install pkg 'dont-select))))
-  (let ((package-menu--transaction-status ":Deleting"))
-    (force-mode-line-update)
-    (redisplay 'force)
-    (dolist (elt (package--sort-by-dependence delete-list))
-      (condition-case-unless-debug err
-          (let ((inhibit-message (or inhibit-message package-menu-async)))
-            (package-delete elt nil 'nosave))
-        (error (message "Error trying to delete `%s': %S"
-                 (package-desc-full-name elt)
-                 err))))))
+  "Install packages in INSTALL-LIST and delete DELETE-LIST.
+Return nil if there were no errors; non-nil otherwise."
+  (let ((errors nil))
+    (if install-list
+        (let ((status-format (format ":Installing %%d/%d"
+                                     (length install-list)))
+              (i 0)
+              (package-menu--transaction-status))
+          (dolist (pkg install-list)
+            (setq package-menu--transaction-status
+                  (format status-format (cl-incf i)))
+            (force-mode-line-update)
+            (redisplay 'force)
+            ;; Don't mark as selected, `package-menu-execute' already
+            ;; does that.
+            (package-install pkg 'dont-select))))
+    (let ((package-menu--transaction-status ":Deleting"))
+      (force-mode-line-update)
+      (redisplay 'force)
+      (dolist (elt (package--sort-by-dependence delete-list))
+        (condition-case-unless-debug err
+            (let ((inhibit-message (or inhibit-message package-menu-async)))
+              (package-delete elt nil 'nosave))
+          (error
+           (push (package-desc-full-name elt) errors)
+           (message "Error trying to delete `%s': %S"
+                    (package-desc-full-name elt) err)))))
+    errors))
 
 (defun package--update-selected-packages (add remove)
   "Update the `package-selected-packages' list according to ADD and REMOVE.
@@ -3796,8 +3800,8 @@ Optional argument NOQUERY non-nil means do not ask the 
user to confirm."
           (message "Operation %s started" message-template)
           ;; Packages being upgraded are not marked as selected.
           (package--update-selected-packages .install .delete)
-          (package-menu--perform-transaction install-list delete-list)
-          (when package-selected-packages
+          (unless (package-menu--perform-transaction install-list delete-list)
+            ;; If there weren't errors, output data.
             (if-let* ((removable (package--removable-packages)))
                 (message "Operation finished.  Packages that are no longer 
needed: %d.  Type `%s' to remove them"
                          (length removable)
diff --git a/lisp/emacs-lisp/re-builder.el b/lisp/emacs-lisp/re-builder.el
index e6e8bb202d..897c35b5b1 100644
--- a/lisp/emacs-lisp/re-builder.el
+++ b/lisp/emacs-lisp/re-builder.el
@@ -369,7 +369,8 @@ provided in the Commentary section of this library."
             (get-buffer-create reb-buffer)
             `((display-buffer-in-direction)
               (direction . ,dir)
-              (dedicated . t))))))
+              (dedicated . t)
+              (window-height . fit-window-to-buffer))))))
     (font-lock-mode 1)
     (reb-initialize-buffer)))
 
diff --git a/lisp/emacs-lisp/regexp-opt.el b/lisp/emacs-lisp/regexp-opt.el
index cae5dd00d1..4d5a39458d 100644
--- a/lisp/emacs-lisp/regexp-opt.el
+++ b/lisp/emacs-lisp/regexp-opt.el
@@ -133,7 +133,6 @@ usually more efficient than that of a simplified version:
   (save-match-data
     ;; Recurse on the sorted list.
     (let* ((max-lisp-eval-depth 10000)
-          (max-specpdl-size 10000)
           (completion-ignore-case nil)
           (completion-regexp-list nil)
           (open (cond ((stringp paren) paren) (paren "\\(")))
diff --git a/lisp/emacs-lisp/seq.el b/lisp/emacs-lisp/seq.el
index b6f0f66e5b..82ade0ac0c 100644
--- a/lisp/emacs-lisp/seq.el
+++ b/lisp/emacs-lisp/seq.el
@@ -346,6 +346,20 @@ list."
   (seq-filter (lambda (elt) (not (funcall pred elt)))
               sequence))
 
+;;;###autoload
+(cl-defgeneric seq-remove-at-position (sequence n)
+  "Return a copy of SEQUENCE where the element at N got removed.
+
+N is the (zero-based) index of the element that should not be in
+the result.
+
+The result is a sequence of the same type as SEQUENCE."
+  (seq-concatenate
+   (let ((type (type-of sequence)))
+     (if (eq type 'cons) 'list type))
+   (seq-subseq sequence 0 n)
+   (seq-subseq sequence (1+ n))))
+
 ;;;###autoload
 (cl-defgeneric seq-reduce (function sequence initial-value)
   "Reduce the function FUNCTION across SEQUENCE, starting with INITIAL-VALUE.
@@ -409,7 +423,7 @@ found or not."
 
 (cl-defgeneric seq-contains (sequence elt &optional testfn)
   "Return the first element in SEQUENCE that is equal to ELT.
-Equality is defined by TESTFN if non-nil or by `equal' if nil."
+Equality is defined by the function TESTFN, which defaults to `equal'."
   (declare (obsolete seq-contains-p "27.1"))
   (seq-some (lambda (e)
               (when (funcall (or testfn #'equal) elt e)
@@ -418,7 +432,7 @@ Equality is defined by TESTFN if non-nil or by `equal' if 
nil."
 
 (cl-defgeneric seq-contains-p (sequence elt &optional testfn)
   "Return non-nil if SEQUENCE contains an element equal to ELT.
-Equality is defined by TESTFN if non-nil or by `equal' if nil."
+Equality is defined by the function TESTFN, which defaults to `equal'."
     (catch 'seq--break
       (seq-doseq (e sequence)
         (let ((r (funcall (or testfn #'equal) e elt)))
@@ -429,14 +443,14 @@ Equality is defined by TESTFN if non-nil or by `equal' if 
nil."
 (cl-defgeneric seq-set-equal-p (sequence1 sequence2 &optional testfn)
   "Return non-nil if SEQUENCE1 and SEQUENCE2 contain the same elements.
 This does not depend on the order of the elements.
-Equality is defined by TESTFN if non-nil or by `equal' if nil."
+Equality is defined by the function TESTFN, which defaults to `equal'."
   (and (seq-every-p (lambda (item1) (seq-contains-p sequence2 item1 testfn)) 
sequence1)
        (seq-every-p (lambda (item2) (seq-contains-p sequence1 item2 testfn)) 
sequence2)))
 
 ;;;###autoload
 (cl-defgeneric seq-position (sequence elt &optional testfn)
-  "Return the index of the first element in SEQUENCE that is equal to ELT.
-Equality is defined by TESTFN if non-nil or by `equal' if nil."
+  "Return the (zero-based) index of the first element in SEQUENCE equal to ELT.
+Equality is defined by the function TESTFN, which defaults to `equal'."
   (let ((index 0))
     (catch 'seq--break
       (seq-doseq (e sequence)
@@ -445,6 +459,23 @@ Equality is defined by TESTFN if non-nil or by `equal' if 
nil."
         (setq index (1+ index)))
       nil)))
 
+;;;###autoload
+(cl-defgeneric seq-positions (sequence elt &optional testfn)
+  "Return indices for which (TESTFN (seq-elt SEQUENCE index) ELT) is non-nil.
+
+TESTFN is a two-argument function which is passed each element of
+SEQUENCE as first argument and ELT as second. TESTFN defaults to
+`equal'.
+
+The result is a list of (zero-based) indices."
+  (let ((result '()))
+    (seq-do-indexed
+     (lambda (e index)
+       (when (funcall (or testfn #'equal) e elt)
+         (push index result)))
+     sequence)
+    (nreverse result)))
+
 ;;;###autoload
 (cl-defgeneric seq-uniq (sequence &optional testfn)
   "Return a list of the elements of SEQUENCE with duplicates removed.
@@ -502,7 +533,7 @@ negative integer or 0, nil is returned."
 ;;;###autoload
 (cl-defgeneric seq-union (sequence1 sequence2 &optional testfn)
   "Return a list of all elements that appear in either SEQUENCE1 or SEQUENCE2.
-Equality is defined by TESTFN if non-nil or by `equal' if nil."
+Equality is defined by the function TESTFN, which defaults to `equal'."
   (let* ((accum (lambda (acc elt)
                   (if (seq-contains-p acc elt testfn)
                       acc
@@ -514,7 +545,7 @@ Equality is defined by TESTFN if non-nil or by `equal' if 
nil."
 ;;;###autoload
 (cl-defgeneric seq-intersection (sequence1 sequence2 &optional testfn)
   "Return a list of the elements that appear in both SEQUENCE1 and SEQUENCE2.
-Equality is defined by TESTFN if non-nil or by `equal' if nil."
+Equality is defined by the function TESTFN, which defaults to `equal'."
   (seq-reduce (lambda (acc elt)
                 (if (seq-contains-p sequence2 elt testfn)
                     (cons elt acc)
@@ -524,7 +555,7 @@ Equality is defined by TESTFN if non-nil or by `equal' if 
nil."
 
 (cl-defgeneric seq-difference (sequence1 sequence2 &optional testfn)
   "Return a list of the elements that appear in SEQUENCE1 but not in SEQUENCE2.
-Equality is defined by TESTFN if non-nil or by `equal' if nil."
+Equality is defined by the function TESTFN, which defaults to `equal'."
   (seq-reduce (lambda (acc elt)
                 (if (seq-contains-p sequence2 elt testfn)
                     acc
@@ -618,13 +649,7 @@ Signal an error if SEQUENCE is empty."
 
 (cl-defmethod seq-take ((list list) n)
   "Optimized implementation of `seq-take' for lists."
-  (if (eval-when-compile (fboundp 'take))
-      (take n list)
-    (let ((result '()))
-      (while (and list (> n 0))
-        (setq n (1- n))
-        (push (pop list) result))
-      (nreverse result))))
+  (take n list))
 
 (cl-defmethod seq-drop-while (pred (list list))
   "Optimized implementation of `seq-drop-while' for lists."
@@ -655,16 +680,6 @@ Signal an error if SEQUENCE is empty."
       sequence
     (concat sequence)))
 
-(defun seq--activate-font-lock-keywords ()
-  "Activate font-lock keywords for some symbols defined in seq."
-  (font-lock-add-keywords 'emacs-lisp-mode
-                          '("\\<seq-doseq\\>" "\\<seq-let\\>")))
-
-(unless (fboundp 'elisp--font-lock-flush-elisp-buffers)
-  ;; In Emacs≥25, (via elisp--font-lock-flush-elisp-buffers and a few others)
-  ;; we automatically highlight macros.
-  (add-hook 'emacs-lisp-mode-hook #'seq--activate-font-lock-keywords))
-
 (defun seq-split (sequence length)
   "Split SEQUENCE into a list of sub-sequences of at most LENGTH.
 All the sub-sequences will be of LENGTH, except the last one,
@@ -680,5 +695,9 @@ which may be shorter."
             result))
     (nreverse result)))
 
+(defun seq-keep (function sequence)
+  "Apply FUNCTION to SEQUENCE and return all non-nil results."
+  (delq nil (seq-map function sequence)))
+
 (provide 'seq)
 ;;; seq.el ends here
diff --git a/lisp/emacs-lisp/shortdoc.el b/lisp/emacs-lisp/shortdoc.el
index 990dabe351..4cfd658e10 100644
--- a/lisp/emacs-lisp/shortdoc.el
+++ b/lisp/emacs-lisp/shortdoc.el
@@ -22,6 +22,15 @@
 
 ;;; Commentary:
 
+;; This package lists functions based on various groupings.
+;;
+;; For instance, `string-trim' and `mapconcat' are `string' functions,
+;; so `M-x shortdoc RET string RET' will give an overview of functions
+;; that operate on strings.
+;;
+;; The documentation groups are created with the
+;; `define-short-documentation-group' macro.
+
 ;;; Code:
 
 (require 'seq)
@@ -355,13 +364,11 @@ A FUNC form can have any number of `:no-eval' (or 
`:no-value'),
   (abbreviate-file-name
    :no-eval (abbreviate-file-name "/home/some-user")
    :eg-result "~some-user")
-  (file-parent-directory
-   :eval (file-parent-directory "/foo/bar")
-   :eval (file-parent-directory "~")
-   :eval (file-parent-directory "/tmp/")
-   :eval (file-parent-directory "foo/bar")
-   :eval (file-parent-directory "foo")
-   :eval (file-parent-directory "/"))
+  (file-name-parent-directory
+   :eval (file-name-parent-directory "/foo/bar")
+   :eval (file-name-parent-directory "/foo/")
+   :eval (file-name-parent-directory "foo/bar")
+   :eval (file-name-parent-directory "foo"))
   "Quoted File Names"
   (file-name-quote
    :args (name)
@@ -846,6 +853,10 @@ A FUNC form can have any number of `:no-eval' (or 
`:no-value'),
    :eval (seq-find #'numberp '(a b 3 4 f 6)))
   (seq-position
    :eval (seq-position '(a b c) 'c))
+  (seq-positions
+   :eval (seq-positions '(a b c a d) 'a)
+   :eval (seq-positions '(a b c a d) 'z)
+   :eval (seq-positions '(11 5 7 12 9 15) 10 #'>=))
   (seq-length
    :eval (seq-length "abcde"))
   (seq-max
@@ -888,6 +899,9 @@ A FUNC form can have any number of `:no-eval' (or 
`:no-value'),
    :eval (seq-filter #'numberp '(a b 3 4 f 6)))
   (seq-remove
    :eval (seq-remove #'numberp '(1 2 c d 5)))
+  (seq-remove-at-position
+   :eval (seq-remove-at-position '(a b c d e) 3)
+   :eval (seq-remove-at-position [a b c d e] 0))
   (seq-group-by
    :eval (seq-group-by #'cl-plusp '(-1 2 3 -4 -5 6)))
   (seq-union
@@ -1507,8 +1521,11 @@ Example:
   :doc "Keymap for `shortdoc-mode'."
   "n"       #'shortdoc-next
   "p"       #'shortdoc-previous
+  "N"       #'shortdoc-next-section
+  "P"       #'shortdoc-previous-section
   "C-c C-n" #'shortdoc-next-section
-  "C-c C-p" #'shortdoc-previous-section)
+  "C-c C-p" #'shortdoc-previous-section
+  "w"       #'shortdoc-copy-function-as-kill)
 
 (define-derived-mode shortdoc-mode special-mode "shortdoc"
   "Mode for shortdoc."
@@ -1521,35 +1538,49 @@ Example:
     (funcall
      (if reverse 'text-property-search-backward
        'text-property-search-forward)
-     sym nil t t)
+     sym nil t)
     (setq arg (1- arg))))
 
 (defun shortdoc-next (&optional arg)
-  "Move cursor to the next function.
-With ARG, do it that many times."
+  "Move point to the next function.
+With prefix numeric argument ARG, do it that many times."
   (interactive "p" shortdoc-mode)
   (shortdoc--goto-section arg 'shortdoc-function))
 
 (defun shortdoc-previous (&optional arg)
-  "Move cursor to the previous function.
-With ARG, do it that many times."
+  "Move point to the previous function.
+With prefix numeric argument ARG, do it that many times."
   (interactive "p" shortdoc-mode)
   (shortdoc--goto-section arg 'shortdoc-function t)
   (backward-char 1))
 
 (defun shortdoc-next-section (&optional arg)
-  "Move cursor to the next section.
-With ARG, do it that many times."
+  "Move point to the next section.
+With prefix numeric argument ARG, do it that many times."
   (interactive "p" shortdoc-mode)
   (shortdoc--goto-section arg 'shortdoc-section))
 
 (defun shortdoc-previous-section (&optional arg)
-  "Move cursor to the previous section.
-With ARG, do it that many times."
+  "Move point to the previous section.
+With prefix numeric argument ARG, do it that many times."
   (interactive "p" shortdoc-mode)
   (shortdoc--goto-section arg 'shortdoc-section t)
   (forward-line -2))
 
+(defun shortdoc-copy-function-as-kill ()
+  "Copy name of the function near point into the kill ring."
+  (interactive)
+  (save-excursion
+    (goto-char (pos-bol))
+    (when-let* ((re (rx bol "(" (group (+ (not (in " "))))))
+                (string
+                 (and (or (looking-at re)
+                          (re-search-backward re nil t))
+                      (match-string 1))))
+      (set-text-properties 0 (length string) nil string)
+      (kill-new string)
+      (message string))))
+
 (provide 'shortdoc)
 
 ;;; shortdoc.el ends here
diff --git a/lisp/emacs-lisp/subr-x.el b/lisp/emacs-lisp/subr-x.el
index bd7c3c82f9..6e4d88b4df 100644
--- a/lisp/emacs-lisp/subr-x.el
+++ b/lisp/emacs-lisp/subr-x.el
@@ -97,6 +97,7 @@ threading."
     (maphash (lambda (_ v) (push v values)) hash-table)
     values))
 
+;;;###autoload
 (defsubst string-join (strings &optional separator)
   "Join all STRINGS using SEPARATOR.
 Optional argument SEPARATOR must be a string, a vector, or a list of
diff --git a/lisp/emacs-lisp/testcover.el b/lisp/emacs-lisp/testcover.el
index cd2e388ce4..760063d1f9 100644
--- a/lisp/emacs-lisp/testcover.el
+++ b/lisp/emacs-lisp/testcover.el
@@ -637,8 +637,7 @@ argument is maybe, return maybe.  Return 1value only if 
both arguments
 are 1value."
   (cl-case val
     (testcover-1value result)
-    (maybe (and result 'maybe))
-    (nil nil)))
+    (maybe (and result 'maybe))))
 
 (defun testcover-analyze-coverage-compose (forms func)
   "Analyze a list of FORMS for code coverage using FUNC.
diff --git a/lisp/emacs-lisp/vtable.el b/lisp/emacs-lisp/vtable.el
index 61265c97c2..9bdf90bf1d 100644
--- a/lisp/emacs-lisp/vtable.el
+++ b/lisp/emacs-lisp/vtable.el
@@ -770,7 +770,8 @@ If NEXT, do the next column."
    ((string-match "\\([0-9.]+\\)px" spec)
     (string-to-number (match-string 1 spec)))
    ((string-match "\\([0-9.]+\\)%" spec)
-    (* (string-to-number (match-string 1 spec)) (window-width nil t)))
+    (/ (* (string-to-number (match-string 1 spec)) (window-width nil t))
+       100))
    (t
     (error "Invalid spec: %s" spec))))
 
diff --git a/lisp/emulation/viper-macs.el b/lisp/emulation/viper-macs.el
index 06130afa7d..9c2aae1fe9 100644
--- a/lisp/emulation/viper-macs.el
+++ b/lisp/emulation/viper-macs.el
@@ -66,9 +66,8 @@
 The previous command is accessible, as usual, via `.'.  The command before this
 can be invoked as `<this key> 1', and the command before that, and the command
 before that one is accessible as `<this key> 2'.
-The notation for these keys is borrowed from XEmacs.  Basically,
-a key is a symbol, e.g., `a', `\\1', `f2', etc., or a list, e.g.,
-`(meta control f1)'."
+Basically, a key is a symbol, e.g., `a', `\\1', `f2', etc., or a
+list, e.g., `(meta control f1)'."
   :type 'sexp
   :group 'viper)
 
diff --git a/lisp/epa-hook.el b/lisp/epa-hook.el
index 18e47c682e..386bd73964 100644
--- a/lisp/epa-hook.el
+++ b/lisp/epa-hook.el
@@ -88,6 +88,14 @@ interface, update `file-name-handler-alist'."
           epa-file-inhibit-auto-save)
       (auto-save-mode 0)))
 
+(defun epa-file-name-p (file)
+  "Say whether FILE is handled by `epa-file'."
+  (and auto-encryption-mode (string-match-p epa-file-name-regexp file)))
+
+(with-eval-after-load 'bookmark
+  (add-hook 'bookmark-inhibit-context-functions
+           #'epa-file-name-p))
+
 (define-minor-mode auto-encryption-mode
   "Toggle automatic file encryption/decryption (Auto Encryption mode)."
   :global t :init-value t :group 'epa-file :version "23.1"
diff --git a/lisp/epa-ks.el b/lisp/epa-ks.el
index 7c60b659f0..4c539b56a3 100644
--- a/lisp/epa-ks.el
+++ b/lisp/epa-ks.el
@@ -41,7 +41,7 @@
 (defcustom epa-keyserver "pgp.mit.edu"
   "Domain of keyserver.
 
-This is used by `epa-ks-lookup-key', for looking up public keys."
+This is used by `epa-search-keys', for looking up public keys."
   :type '(choice :tag "Keyserver"
                  (repeat :tag "Random pool"
                          (string :tag "Keyserver address"))
@@ -66,14 +66,12 @@ This is used by `epa-ks-lookup-key', for looking up public 
keys."
   "List of arguments to pass to `epa-search-keys'.
 This is used when reverting a buffer to restart search.")
 
-(defvar epa-ks-search-mode-map
-  (let ((map (make-sparse-keymap)))
-    (suppress-keymap map)
-    (define-key map (kbd "f") #'epa-ks-mark-key-to-fetch)
-    (define-key map (kbd "i") #'epa-ks-inspect-key-to-fetch)
-    (define-key map (kbd "u") #'epa-ks-unmark-key-to-fetch)
-    (define-key map (kbd "x") #'epa-ks-do-key-to-fetch)
-    map))
+(defvar-keymap epa-ks-search-mode-map
+  :suppress t
+  "f" #'epa-ks-mark-key-to-fetch
+  "i" #'epa-ks-inspect-key-to-fetch
+  "u" #'epa-ks-unmark-key-to-fetch
+  "x" #'epa-ks-do-key-to-fetch)
 
 (define-derived-mode epa-ks-search-mode tabulated-list-mode "Keyserver"
   "Major mode for listing public key search results."
@@ -182,7 +180,7 @@ If EXACT is non-nil, don't accept approximate matches."
   "Prepare KEYS for `tabulated-list-mode', for buffer BUF.
 
 KEYS is a list of `epa-ks-key' structures, as parsed by
-`epa-ks-parse-result'."
+`epa-ks--parse-buffer'."
   (when (buffer-live-p buf)
     (let (entries)
       (dolist (key keys)
diff --git a/lisp/epa.el b/lisp/epa.el
index 63bc0941d6..cb87d80885 100644
--- a/lisp/epa.el
+++ b/lisp/epa.el
@@ -183,28 +183,26 @@ You should bind this variable with `let', but do not set 
it globally.")
 (defvar epa-suppress-error-buffer nil)
 (defvar epa-last-coding-system-specified nil)
 
-(defvar epa-key-list-mode-map
-  (let ((keymap (make-sparse-keymap)))
-    (define-key keymap "\C-m" 'epa-show-key)
-    (define-key keymap [?\t] 'forward-button)
-    (define-key keymap [backtab] 'backward-button)
-    (define-key keymap "m" 'epa-mark-key)
-    (define-key keymap "u" 'epa-unmark-key)
-    (define-key keymap "d" 'epa-decrypt-file)
-    (define-key keymap "v" 'epa-verify-file)
-    (define-key keymap "s" 'epa-sign-file)
-    (define-key keymap "e" 'epa-encrypt-file)
-    (define-key keymap "r" 'epa-delete-keys)
-    (define-key keymap "i" 'epa-import-keys)
-    (define-key keymap "o" 'epa-export-keys)
-    (define-key keymap "g" 'revert-buffer)
-    (define-key keymap "n" 'next-line)
-    (define-key keymap "p" 'previous-line)
-    (define-key keymap " " 'scroll-up-command)
-    (define-key keymap [?\S-\ ] 'scroll-down-command)
-    (define-key keymap [delete] 'scroll-down-command)
-    (define-key keymap "q" 'epa-exit-buffer)
-    keymap))
+(defvar-keymap epa-key-list-mode-map
+  "RET"       #'epa-show-key
+  "TAB"       #'forward-button
+  "<backtab>" #'backward-button
+  "m"         #'epa-mark-key
+  "u"         #'epa-unmark-key
+  "d"         #'epa-decrypt-file
+  "v"         #'epa-verify-file
+  "s"         #'epa-sign-file
+  "e"         #'epa-encrypt-file
+  "r"         #'epa-delete-keys
+  "i"         #'epa-import-keys
+  "o"         #'epa-export-keys
+  "g"         #'revert-buffer
+  "n"         #'next-line
+  "p"         #'previous-line
+  "SPC"       #'scroll-up-command
+  "S-SPC"     #'scroll-down-command
+  "<delete>"  #'scroll-down-command
+  "q"         #'epa-exit-buffer)
 
 (easy-menu-define epa-key-list-mode-menu epa-key-list-mode-map
   "Menu for `epa-key-list-mode'."
@@ -230,10 +228,8 @@ You should bind this variable with `let', but do not set 
it globally.")
     ["Unmark Key" epa-unmark-key
      :help "Unmark a key"]))
 
-(defvar epa-key-mode-map
-  (let ((keymap (make-sparse-keymap)))
-    (define-key keymap "q" 'epa-exit-buffer)
-    keymap))
+(defvar-keymap epa-key-mode-map
+  "q" #'epa-exit-buffer)
 
 (defvar epa-exit-buffer-function #'quit-window)
 
diff --git a/lisp/erc/erc-dcc.el b/lisp/erc/erc-dcc.el
index dd70bfb7b7..90a10766c4 100644
--- a/lisp/erc/erc-dcc.el
+++ b/lisp/erc/erc-dcc.el
@@ -1108,9 +1108,6 @@ Possible values are: ask, auto, ignore."
   (pcomplete-here '("auto" "ask" "ignore")))
 (defalias 'pcomplete/erc-mode/SREQ #'pcomplete/erc-mode/CREQ)
 
-(define-obsolete-variable-alias 'erc-dcc-chat-filter-hook
-  'erc-dcc-chat-filter-functions "24.3")
-
 (defvar erc-dcc-chat-filter-functions '(erc-dcc-chat-parse-output)
   "Abnormal hook run after parsing (and maybe inserting) a DCC message.
 Each function is called with two arguments: the ERC process and
diff --git a/lisp/erc/erc-match.el b/lisp/erc/erc-match.el
index 7c9174ff66..6b9aa47d86 100644
--- a/lisp/erc/erc-match.el
+++ b/lisp/erc/erc-match.el
@@ -240,6 +240,15 @@ server and other miscellaneous functions."
   :version "24.3"
   :type 'boolean)
 
+(defcustom erc-match-quote-when-adding 'ask
+  "Whether to `regexp-quote' when adding to a match list interactively.
+When the value is a boolean, the opposite behavior will be made
+available via universal argument."
+  :package-version '(ERC . "5.4.1") ; FIXME increment on next release
+  :type '(choice (const ask)
+                 (const t)
+                 (const nil)))
+
 ;; Internal variables:
 
 ;; This is exactly the same as erc-button-syntax-table.  Should we
@@ -290,7 +299,7 @@ Note that this is the default face to use if
 
 ;; Functions:
 
-(defun erc-add-entry-to-list (list prompt &optional completions)
+(defun erc-add-entry-to-list (list prompt &optional completions alt)
   "Add an entry interactively to a list.
 LIST must be passed as a symbol
 The query happens using PROMPT.
@@ -299,7 +308,16 @@ Completion is performed on the optional alist COMPLETIONS."
                prompt
                completions
                (lambda (x)
-                 (not (erc-member-ignore-case (car x) (symbol-value list)))))))
+                  (not (erc-member-ignore-case (car x) (symbol-value list))))))
+        quoted)
+    (setq quoted (regexp-quote entry))
+    (when (pcase erc-match-quote-when-adding
+            ('ask (unless (string= quoted entry)
+                    (y-or-n-p
+                     (format "Use regexp-quoted form (%s) instead? " quoted))))
+            ('t (not alt))
+            ('nil alt))
+      (setq entry quoted))
     (if (erc-member-ignore-case entry (symbol-value list))
        (error "\"%s\" is already on the list" entry)
       (set list (cons entry (symbol-value list))))))
@@ -327,10 +345,11 @@ car is the string."
                        (symbol-value list))))))
 
 ;;;###autoload
-(defun erc-add-pal ()
+(defun erc-add-pal (&optional arg)
   "Add pal interactively to `erc-pals'."
-  (interactive)
-  (erc-add-entry-to-list 'erc-pals "Add pal: " 
(erc-get-server-nickname-alist)))
+  (interactive "P")
+  (erc-add-entry-to-list 'erc-pals "Add pal: "
+                         (erc-get-server-nickname-alist) arg))
 
 ;;;###autoload
 (defun erc-delete-pal ()
@@ -339,11 +358,11 @@ car is the string."
   (erc-remove-entry-from-list 'erc-pals "Delete pal: "))
 
 ;;;###autoload
-(defun erc-add-fool ()
+(defun erc-add-fool (&optional arg)
   "Add fool interactively to `erc-fools'."
-  (interactive)
+  (interactive "P")
   (erc-add-entry-to-list 'erc-fools "Add fool: "
-                        (erc-get-server-nickname-alist)))
+                         (erc-get-server-nickname-alist) arg))
 
 ;;;###autoload
 (defun erc-delete-fool ()
@@ -352,10 +371,10 @@ car is the string."
   (erc-remove-entry-from-list 'erc-fools "Delete fool: "))
 
 ;;;###autoload
-(defun erc-add-keyword ()
+(defun erc-add-keyword (&optional arg)
   "Add keyword interactively to `erc-keywords'."
-  (interactive)
-  (erc-add-entry-to-list 'erc-keywords "Add keyword: "))
+  (interactive "P")
+  (erc-add-entry-to-list 'erc-keywords "Add keyword: " nil arg))
 
 ;;;###autoload
 (defun erc-delete-keyword ()
@@ -364,10 +383,10 @@ car is the string."
   (erc-remove-entry-from-list 'erc-keywords "Delete keyword: "))
 
 ;;;###autoload
-(defun erc-add-dangerous-host ()
+(defun erc-add-dangerous-host (&optional arg)
   "Add dangerous-host interactively to `erc-dangerous-hosts'."
-  (interactive)
-  (erc-add-entry-to-list 'erc-dangerous-hosts "Add dangerous-host: "))
+  (interactive "P")
+  (erc-add-entry-to-list 'erc-dangerous-hosts "Add dangerous-host: " nil arg))
 
 ;;;###autoload
 (defun erc-delete-dangerous-host ()
@@ -388,19 +407,19 @@ NICKUSERHOST will be ignored."
 (defun erc-match-pal-p (nickuserhost _msg)
   "Check whether NICKUSERHOST is in `erc-pals'.
 MSG will be ignored."
-  (and nickuserhost
+  (and nickuserhost erc-pals
        (erc-list-match erc-pals nickuserhost)))
 
 (defun erc-match-fool-p (nickuserhost msg)
   "Check whether NICKUSERHOST is in `erc-fools' or MSG is directed at a fool."
-  (and msg nickuserhost
+  (and msg nickuserhost erc-fools
        (or (erc-list-match erc-fools nickuserhost)
           (erc-match-directed-at-fool-p msg))))
 
 (defun erc-match-keyword-p (_nickuserhost msg)
   "Check whether any keyword of `erc-keywords' matches for MSG.
 NICKUSERHOST will be ignored."
-  (and msg
+  (and msg erc-keywords
        (erc-list-match
        (mapcar (lambda (x)
                  (if (listp x)
@@ -412,7 +431,7 @@ NICKUSERHOST will be ignored."
 (defun erc-match-dangerous-host-p (nickuserhost _msg)
   "Check whether NICKUSERHOST is in `erc-dangerous-hosts'.
 MSG will be ignored."
-  (and nickuserhost
+  (and nickuserhost erc-dangerous-hosts
        (erc-list-match erc-dangerous-hosts nickuserhost)))
 
 (defun erc-match-directed-at-fool-p (msg)
diff --git a/lisp/erc/erc-networks.el b/lisp/erc/erc-networks.el
index c54b12fcb0..2c8f8fb72b 100644
--- a/lisp/erc/erc-networks.el
+++ b/lisp/erc/erc-networks.el
@@ -996,8 +996,8 @@ Rename the current buffer if its NID has grown."
   "Return a list of target BUFFERS, newest to oldest."
   (sort buffers
         (lambda (a b)
-          (> (with-current-buffer a (erc-networks--id-ts erc-networks--id))
-             (with-current-buffer b (erc-networks--id-ts erc-networks--id))))))
+          (> (erc-networks--id-ts (buffer-local-value 'erc-networks--id a))
+             (erc-networks--id-ts (buffer-local-value 'erc-networks--id b))))))
 
 
 ;;;; Buffer association
diff --git a/lisp/erc/erc.el b/lisp/erc/erc.el
index 151d75e7ce..20f22c896f 100644
--- a/lisp/erc/erc.el
+++ b/lisp/erc/erc.el
@@ -1651,7 +1651,7 @@ Defaults to the server buffer."
   "IRC port to use for encrypted connections if it cannot be \
 detected otherwise.")
 
-(defcustom erc-join-buffer 'buffer
+(defcustom erc-join-buffer 'bury
   "Determines how to display a newly created IRC buffer.
 
 The available choices are:
@@ -1662,6 +1662,7 @@ The available choices are:
   `bury'            - bury it in a new buffer,
   `buffer'          - in place of the current buffer,
   any other value  - in place of the current buffer."
+  :package-version '(ERC . "5.4.1") ; FIXME increment upon publishing to ELPA
   :group 'erc-buffers
   :type '(choice (const :tag "Split window and select" window)
                  (const :tag "Split window, don't select" window-noselect)
@@ -2148,7 +2149,7 @@ removed from the list will be disabled."
          (display-buffer buffer)
        (switch-to-buffer-other-window buffer)))
     ('window-noselect
-     (display-buffer buffer))
+     (display-buffer buffer '(nil (inhibit-same-window . t))))
     ('bury
      nil)
     ('frame
@@ -2571,7 +2572,8 @@ workaround."
       (with-current-buffer (get-buffer-create "*erc-protocol*")
         (save-excursion
           (goto-char (point-max))
-          (let ((inhibit-read-only t))
+          (let ((buffer-undo-list t)
+                (inhibit-read-only t))
             (insert (if outbound
                         (concat ts esid " >> " string)
                       ;; Cope with multi-line messages
@@ -6283,9 +6285,7 @@ The addressed target is the string before the first colon 
in MSG."
 
 (defun erc-list-match (lst str)
   "Return non-nil if any regexp in LST matches STR."
-  (memq nil (mapcar (lambda (regexp)
-                      (not (string-match regexp str)))
-                    lst)))
+  (and lst (string-match (string-join lst "\\|") str)))
 
 ;; other "toggles"
 
diff --git a/lisp/eshell/em-cmpl.el b/lisp/eshell/em-cmpl.el
index 822cc94149..ac82e3f225 100644
--- a/lisp/eshell/em-cmpl.el
+++ b/lisp/eshell/em-cmpl.el
@@ -378,6 +378,31 @@ to writing a completion function."
           args)
          posns)))
 
+(defun eshell--pcomplete-executables ()
+  "Complete amongst a list of directories and executables.
+
+Wrapper for `pcomplete-executables' or `pcomplete-dirs-or-entries',
+depending on the value of `eshell-force-execution'.
+
+Adds path prefix to candidates independent of `action' value."
+  ;; `pcomplete-entries' returns filenames without path on `action' to
+  ;; use current string directory as done in `completion-file-name-table'
+  ;; when `action' is nil to construct executable candidates.
+  (let ((table (if eshell-force-execution
+                   (pcomplete-dirs-or-entries nil #'file-readable-p)
+                 (pcomplete-executables))))
+    (lambda (string pred action)
+      (let ((cands (funcall table string pred action)))
+        (if (eq action t)
+            (let ((specdir (file-name-directory string)))
+              (mapcar
+               (lambda (cand)
+                 (if (stringp cand)
+                     (file-name-concat specdir cand)
+                   cand))
+               cands))
+          cands)))))
+
 (defun eshell--complete-commands-list ()
   "Generate list of applicable, visible commands."
   ;; Building the commands list can take quite a while, especially over Tramp
@@ -392,9 +417,7 @@ to writing a completion function."
     (completion-table-dynamic
      (lambda (filename)
        (if (file-name-directory filename)
-           (if eshell-force-execution
-               (pcomplete-dirs-or-entries nil #'file-readable-p)
-             (pcomplete-executables))
+           (eshell--pcomplete-executables)
         (let* ((paths (eshell-get-path))
                (cwd (file-name-as-directory
                      (expand-file-name default-directory)))
diff --git a/lisp/eshell/em-glob.el b/lisp/eshell/em-glob.el
index 58b7a83c09..9722aeae18 100644
--- a/lisp/eshell/em-glob.el
+++ b/lisp/eshell/em-glob.el
@@ -202,7 +202,7 @@ The basic syntax is:
   [a-b]  [a-b]   matches a character or range
   [^a]   [^a]    excludes a character or range
 
-If any characters in PATTERN have the text property `eshell-escaped'
+If any characters in PATTERN have the text property `escaped'
 set to true, then these characters will match themselves in the
 resulting regular expression."
   (let ((matched-in-pattern 0)          ; How much of PATTERN handled
diff --git a/lisp/eshell/em-term.el b/lisp/eshell/em-term.el
index a4fa699aa9..6811e70313 100644
--- a/lisp/eshell/em-term.el
+++ b/lisp/eshell/em-term.el
@@ -153,7 +153,7 @@ behavior for short-lived processes, see bug#18108."
 If either COMMAND or a subcommand in ARGS (e.g. git log) is a
 visual command, returns non-nil."
   (let ((command (file-name-nondirectory command)))
-    (and (eshell-interactive-output-p)
+    (and (eshell-interactive-output-p 'all)
          (or (member command eshell-visual-commands)
              (member (car args)
                      (cdr (assoc command eshell-visual-subcommands)))
diff --git a/lisp/eshell/esh-arg.el b/lisp/eshell/esh-arg.el
index 8e44a88459..576d32b8c5 100644
--- a/lisp/eshell/esh-arg.el
+++ b/lisp/eshell/esh-arg.el
@@ -29,6 +29,9 @@
 
 (require 'esh-util)
 
+(eval-when-compile
+  (require 'cl-lib))
+
 (defgroup eshell-arg nil
   "Argument parsing involves transforming the arguments passed on the
 command line into equivalent Lisp forms that, when evaluated, will
@@ -147,6 +150,10 @@ return the result of the parse as a sexp.  It is also 
responsible for
 moving the point forward to reflect the amount of input text that was
 parsed.
 
+If the hook determines that it has reached the end of an argument, it
+should call `eshell-finish-arg' to complete processing of the current
+argument and proceed to the next.
+
 If no function handles the current character at point, it will be
 treated as a literal character."
   :type 'hook
@@ -244,10 +251,16 @@ convert the result to a number as well."
            eshell-current-modifiers (cdr eshell-current-modifiers))))
   (setq eshell-current-modifiers nil))
 
-(defun eshell-finish-arg (&optional argument)
-  "Finish the current ARGUMENT being processed."
-  (if argument
-      (setq eshell-current-argument argument))
+(defun eshell-finish-arg (&rest arguments)
+  "Finish the current argument being processed.
+If any ARGUMENTS are specified, they will be added to the final
+argument list in place of the value of the current argument."
+  (when arguments
+    (if (= (length arguments) 1)
+        (setq eshell-current-argument (car arguments))
+      (cl-assert (and (not eshell-arg-listified)
+                      (not eshell-current-modifiers)))
+      (setq eshell-current-argument (cons 'eshell-flatten-args arguments))))
   (throw 'eshell-arg-done t))
 
 (defun eshell-quote-argument (string)
@@ -287,7 +300,11 @@ Point is left at the end of the arguments."
                      (if (= (point) here)
                          (error "Failed to parse argument `%s'"
                                 (buffer-substring here (point-max))))
-                     (and arg (nconc args (list arg)))))))
+                     (when arg
+                       (nconc args
+                              (if (eq (car-safe arg) 'eshell-flatten-args)
+                                  (cdr arg)
+                                (list arg))))))))
               (throw 'eshell-incomplete (if (listp delim)
                                             delim
                                           (list delim (point) (cdr args)))))
diff --git a/lisp/eshell/esh-cmd.el b/lisp/eshell/esh-cmd.el
index 2f77f3f497..413336e3eb 100644
--- a/lisp/eshell/esh-cmd.el
+++ b/lisp/eshell/esh-cmd.el
@@ -810,8 +810,6 @@ This macro calls itself recursively, with NOTFIRST non-nil."
           `(let ((nextproc
                   (eshell-do-pipelines (quote ,(cdr pipeline)) t)))
               (eshell-set-output-handle ,eshell-output-handle
-                                        'append nextproc)
-              (eshell-set-output-handle ,eshell-error-handle
                                         'append nextproc)))
        ,(let ((head (car pipeline)))
           (if (memq (car head) '(let progn))
@@ -842,8 +840,6 @@ This is used on systems where async subprocesses are not 
supported."
        ,(when (cdr pipeline)
           `(let ((output-marker ,(point-marker)))
              (eshell-set-output-handle ,eshell-output-handle
-                                       'append output-marker)
-             (eshell-set-output-handle ,eshell-error-handle
                                        'append output-marker)))
        ,(let ((head (car pipeline)))
           (if (memq (car head) '(let progn))
@@ -1347,6 +1343,15 @@ case."
                  (apply func-or-form args)))))
         (and result (funcall printer result))
         result)
+    (eshell-pipe-broken
+     ;; If FUNC-OR-FORM tried and failed to write some output to a
+     ;; process, it will raise an `eshell-pipe-broken' signal (this is
+     ;; analogous to SIGPIPE on POSIX systems).  In this case, set the
+     ;; command status to some non-zero value to indicate an error; to
+     ;; match GNU/Linux, we use 141, which the numeric value of
+     ;; SIGPIPE on GNU/Linux (13) with the high bit (2^7) set.
+     (setq eshell-last-command-status 141)
+     nil)
     (error
      (setq eshell-last-command-status 1)
      (let ((msg (error-message-string err)))
diff --git a/lisp/eshell/esh-io.el b/lisp/eshell/esh-io.el
index e5977c9580..4620565f85 100644
--- a/lisp/eshell/esh-io.el
+++ b/lisp/eshell/esh-io.el
@@ -154,6 +154,14 @@ not be added to this variable."
 
 ;;; Internal Variables:
 
+(defconst eshell-redirection-operators-alist
+  '(("<"   . input)                     ; FIXME: Not supported yet.
+    (">"   . overwrite)
+    (">>"  . append)
+    (">>>" . insert))
+  "An association list of redirection operators to symbols
+describing the mode, e.g. for using with `eshell-get-target'.")
+
 (defvar eshell-current-handles nil)
 
 (defvar eshell-last-command-status 0
@@ -173,53 +181,104 @@ not be added to this variable."
 (defun eshell-io-initialize ()      ;Called from `eshell-mode' via intern-soft!
   "Initialize the I/O subsystem code."
   (add-hook 'eshell-parse-argument-hook
-           'eshell-parse-redirection nil t)
+            #'eshell-parse-redirection nil t)
   (make-local-variable 'eshell-current-redirections)
   (add-hook 'eshell-pre-rewrite-command-hook
-           'eshell-strip-redirections nil t)
+            #'eshell-strip-redirections nil t)
   (add-function :filter-return (local 'eshell-post-rewrite-command-function)
                 #'eshell--apply-redirections))
 
 (defun eshell-parse-redirection ()
-  "Parse an output redirection, such as `2>'."
-  (if (and (not eshell-current-quoted)
-          (looking-at "\\([0-9]\\)?\\(<\\|>+\\)&?\\([0-9]\\)?\\s-*"))
+  "Parse an output redirection, such as `2>' or `>&'."
+  (when (not eshell-current-quoted)
+    (cond
+     ;; Copying a handle (e.g. `2>&1').
+     ((looking-at (rx (? (group digit))
+                      (group (or "<" ">"))
+                      "&" (group digit)
+                      (* (syntax whitespace))))
+      (let ((source (string-to-number (or (match-string 1) "1")))
+            (mode (cdr (assoc (match-string 2)
+                              eshell-redirection-operators-alist)))
+            (target (string-to-number (match-string 3))))
+        (when (eq mode 'input)
+          (error "Eshell does not support input redirection"))
+        (goto-char (match-end 0))
+        (eshell-finish-arg (list 'eshell-copy-output-handle
+                                 source target))))
+     ;; Shorthand for redirecting both stdout and stderr (e.g. `&>').
+     ((looking-at (rx (or (seq (group (** 1 3 ">")) "&")
+                          (seq "&" (group-n 1 (** 1 3 ">"))))
+                      (* (syntax whitespace))))
+      (if eshell-current-argument
+          (eshell-finish-arg)
+        (goto-char (match-end 0))
+        (eshell-finish-arg
+         (let ((mode (cdr (assoc (match-string 1)
+                                 eshell-redirection-operators-alist))))
+           (list 'eshell-set-all-output-handles
+                 (list 'quote mode))))))
+     ;; Shorthand for piping both stdout and stderr (i.e. `|&').
+     ((looking-at (rx "|&" (* (syntax whitespace))))
+      (if eshell-current-argument
+          (eshell-finish-arg)
+        (goto-char (match-end 0))
+        (eshell-finish-arg
+         '(eshell-copy-output-handle eshell-error-handle
+                                     eshell-output-handle)
+         '(eshell-operator "|"))))
+     ;; Regular redirecting (e.g. `2>').
+     ((looking-at (rx (? (group digit))
+                      (group (or "<" (** 1 3 ">")))
+                      (* (syntax whitespace))))
       (if eshell-current-argument
-         (eshell-finish-arg)
-       (let ((sh (match-string 1))
-             (oper (match-string 2))
-;            (th (match-string 3))
-             )
-         (if (string= oper "<")
-             (error "Eshell does not support input redirection"))
-         (eshell-finish-arg
-          (prog1
-              (list 'eshell-set-output-handle
-                    (or (and sh (string-to-number sh)) 1)
-                    (list 'quote
-                          (aref [overwrite append insert]
-                                (1- (length oper)))))
-            (goto-char (match-end 0))))))))
+          (eshell-finish-arg)
+        (let ((source (if (match-string 1)
+                          (string-to-number (match-string 1))
+                        eshell-output-handle))
+              (mode (cdr (assoc (match-string 2)
+                                eshell-redirection-operators-alist))))
+          (when (eq mode 'input)
+            (error "Eshell does not support input redirection"))
+          (goto-char (match-end 0))
+          (eshell-finish-arg
+           ;; Note: the target will be set later by
+           ;; `eshell-strip-redirections'.
+           (list 'eshell-set-output-handle
+                 source (list 'quote mode)))))))))
 
 (defun eshell-strip-redirections (terms)
   "Rewrite any output redirections in TERMS."
   (setq eshell-current-redirections (list t))
   (let ((tl terms)
-       (tt (cdr terms)))
+        (tt (cdr terms)))
     (while tt
-      (if (not (and (consp (car tt))
-                   (eq (caar tt) 'eshell-set-output-handle)))
-         (setq tt (cdr tt)
-               tl (cdr tl))
-       (unless (cdr tt)
-         (error "Missing redirection target"))
-       (nconc eshell-current-redirections
-              (list (list 'ignore
-                          (append (car tt) (list (cadr tt))))))
-       (setcdr tl (cddr tt))
-       (setq tt (cddr tt))))
+      (cond
+       ;; Strip `eshell-copy-output-handle'.
+       ((and (consp (car tt))
+             (eq (caar tt) 'eshell-copy-output-handle))
+        (nconc eshell-current-redirections
+               (list (car tt)))
+        (setcdr tl (cddr tt))
+        (setq tt (cdr tt)))
+       ;; Strip `eshell-set-output-handle' or
+       ;; `eshell-set-all-output-handles' and the term immediately
+       ;; after (the redirection target).
+       ((and (consp (car tt))
+             (memq (caar tt) '(eshell-set-output-handle
+                               eshell-set-all-output-handles)))
+        (unless (cdr tt)
+          (error "Missing redirection target"))
+        (nconc eshell-current-redirections
+               (list (list 'ignore
+                           (append (car tt) (list (cadr tt))))))
+        (setcdr tl (cddr tt))
+        (setq tt (cddr tt)))
+       (t
+        (setq tt (cdr tt)
+              tl (cdr tl)))))
     (setq eshell-current-redirections
-         (cdr eshell-current-redirections))))
+          (cdr eshell-current-redirections))))
 
 (defun eshell--apply-redirections (cmd)
   "Apply any redirection which were specified for COMMAND."
@@ -236,22 +295,21 @@ The default location for standard output and standard 
error will go to
 STDOUT and STDERR, respectively.
 OUTPUT-MODE and ERROR-MODE are either `overwrite', `append' or `insert';
 a nil value of mode defaults to `insert'."
-  (let ((handles (make-vector eshell-number-of-handles nil))
-       (output-target (eshell-get-target stdout output-mode))
-        (error-target (eshell-get-target stderr error-mode)))
+  (let* ((handles (make-vector eshell-number-of-handles nil))
+         (output-target (eshell-get-target stdout output-mode))
+         (error-target (if stderr
+                           (eshell-get-target stderr error-mode)
+                         output-target)))
     (aset handles eshell-output-handle (cons output-target 1))
-    (aset handles eshell-error-handle
-          (cons (if stderr error-target output-target) 1))
+    (aset handles eshell-error-handle (cons error-target 1))
     handles))
 
 (defun eshell-protect-handles (handles)
   "Protect the handles in HANDLES from a being closed."
-  (let ((idx 0))
-    (while (< idx eshell-number-of-handles)
-      (if (aref handles idx)
-         (setcdr (aref handles idx)
-                 (1+ (cdr (aref handles idx)))))
-      (setq idx (1+ idx))))
+  (dotimes (idx eshell-number-of-handles)
+    (when (aref handles idx)
+      (setcdr (aref handles idx)
+              (1+ (cdr (aref handles idx))))))
   handles)
 
 (defun eshell-close-handles (&optional exit-code result handles)
@@ -278,6 +336,40 @@ the value already set in `eshell-last-command-result'."
             (eshell-close-target target (= eshell-last-command-status 0)))
           (setcar handle nil))))))
 
+(defun eshell-set-output-handle (index mode &optional target handles)
+  "Set handle INDEX for the current HANDLES to point to TARGET using MODE.
+If HANDLES is nil, use `eshell-current-handles'."
+  (when target
+    (let ((handles (or handles eshell-current-handles)))
+      (if (and (stringp target)
+               (string= target (null-device)))
+          (aset handles index nil)
+        (let ((where (eshell-get-target target mode))
+              (current (car (aref handles index))))
+          (if (listp current)
+              (unless (member where current)
+                (setq current (append current (list where))))
+            (setq current (list where)))
+          (if (not (aref handles index))
+              (aset handles index (cons nil 1)))
+          (setcar (aref handles index) current))))))
+
+(defun eshell-copy-output-handle (index index-to-copy &optional handles)
+  "Copy the handle INDEX-TO-COPY to INDEX for the current HANDLES.
+If HANDLES is nil, use `eshell-current-handles'."
+  (let* ((handles (or handles eshell-current-handles))
+         (handle-to-copy (car (aref handles index-to-copy))))
+    (setcar (aref handles index)
+            (if (listp handle-to-copy)
+                (copy-sequence handle-to-copy)
+              handle-to-copy))))
+
+(defun eshell-set-all-output-handles (mode &optional target handles)
+  "Set output and error HANDLES to point to TARGET using MODE.
+If HANDLES is nil, use `eshell-current-handles'."
+  (eshell-set-output-handle eshell-output-handle mode target handles)
+  (eshell-copy-output-handle eshell-error-handle eshell-output-handle handles))
+
 (defun eshell-close-target (target status)
   "Close an output TARGET, passing STATUS as the result.
 STATUS should be non-nil on successful termination of the output."
@@ -390,28 +482,20 @@ it defaults to `insert'."
     (error "Invalid redirection target: %s"
           (eshell-stringify target)))))
 
-(defun eshell-set-output-handle (index mode &optional target)
-  "Set handle INDEX, using MODE, to point to TARGET."
-  (when target
-    (if (and (stringp target)
-             (string= target (null-device)))
-       (aset eshell-current-handles index nil)
-      (let ((where (eshell-get-target target mode))
-           (current (car (aref eshell-current-handles index))))
-       (if (and (listp current)
-                (not (member where current)))
-           (setq current (append current (list where)))
-         (setq current (list where)))
-       (if (not (aref eshell-current-handles index))
-           (aset eshell-current-handles index (cons nil 1)))
-       (setcar (aref eshell-current-handles index) current)))))
-
-(defun eshell-interactive-output-p ()
-  "Return non-nil if current handles are bound for interactive display."
-  (and (eq (car (aref eshell-current-handles
-                     eshell-output-handle)) t)
-       (eq (car (aref eshell-current-handles
-                     eshell-error-handle)) t)))
+(defun eshell-interactive-output-p (&optional index handles)
+  "Return non-nil if the specified handle is bound for interactive display.
+HANDLES is the set of handles to check; if nil, use
+`eshell-current-handles'.
+
+INDEX is the handle index to check.  If nil, check
+`eshell-output-handle'.  If `all', check both
+`eshell-output-handle' and `eshell-error-handle'."
+  (let ((handles (or handles eshell-current-handles))
+        (index (or index eshell-output-handle)))
+    (if (eq index 'all)
+        (and (eq (car (aref handles eshell-output-handle)) t)
+             (eq (car (aref handles eshell-error-handle)) t))
+      (eq (car (aref handles index)) t))))
 
 (defvar eshell-print-queue nil)
 (defvar eshell-print-queue-count -1)
@@ -498,10 +582,16 @@ Returns what was actually sent, or nil if nothing was 
sent."
    ((eshell-processp target)
     (unless (stringp object)
       (setq object (eshell-stringify object)))
-    (condition-case nil
+    (condition-case err
         (process-send-string target object)
-      ;; If `process-send-string' raises an error, treat it as a broken pipe.
-      (error (signal 'eshell-pipe-broken (list target)))))
+      (error
+       ;; If `process-send-string' raises an error and the process has
+       ;; finished, treat it as a broken pipe.  Otherwise, just
+       ;; re-throw the signal.
+       (if (memq (process-status target)
+                '(run stop open closed))
+           (signal (car err) (cdr err))
+         (signal 'eshell-pipe-broken (list target))))))
 
    ((consp target)
     (apply (car target) object (cdr target))))
diff --git a/lisp/eshell/esh-mode.el b/lisp/eshell/esh-mode.el
index ecbcf88b97..8f11e6f04a 100644
--- a/lisp/eshell/esh-mode.el
+++ b/lisp/eshell/esh-mode.el
@@ -175,9 +175,6 @@ This is used by `eshell-watch-for-password-prompt'."
   "A function called from beginning of line to skip the prompt."
   :type '(choice (const nil) function))
 
-(define-obsolete-variable-alias 'eshell-status-in-modeline
-  'eshell-status-in-mode-line "24.3")
-
 (defcustom eshell-status-in-mode-line t
   "If non-nil, let the user know a command is running in the mode line."
   :type 'boolean)
@@ -334,7 +331,6 @@ and the hook `eshell-exit-hook'."
   (setq-local require-final-newline nil)
 
   (setq-local max-lisp-eval-depth (max 3000 max-lisp-eval-depth))
-  (setq-local max-specpdl-size (max 6000 max-lisp-eval-depth))
 
   (setq-local eshell-last-input-start (point-marker))
   (setq-local eshell-last-input-end (point-marker))
diff --git a/lisp/eshell/esh-proc.el b/lisp/eshell/esh-proc.el
index c367b5cd64..7e005a0fc1 100644
--- a/lisp/eshell/esh-proc.el
+++ b/lisp/eshell/esh-proc.el
@@ -99,7 +99,13 @@ information, for example."
 (defvar eshell-current-subjob-p nil)
 
 (defvar eshell-process-list nil
-  "A list of the current status of subprocesses.")
+  "A list of the current status of subprocesses.
+Each element has the form (PROC . SUBJOB-P), where PROC is the
+process object and SUBJOB-P is non-nil if the process is a
+subjob.
+
+To add or remove elements of this list, see
+`eshell-record-process-object' and `eshell-remove-process-entry'.")
 
 (declare-function eshell-send-eof-to-process "esh-mode")
 (declare-function eshell-tail-process "esh-cmd")
@@ -229,21 +235,30 @@ The prompt will be set to PROMPT."
     (declare-function eshell-interactive-print "esh-mode" (string))
     (eshell-interactive-print
      (format "[%s] %d\n" (process-name object) (process-id object))))
-  (setq eshell-process-list
-       (cons (list object eshell-current-handles
-                   eshell-current-subjob-p nil nil)
-             eshell-process-list)))
+  (push (cons object eshell-current-subjob-p) eshell-process-list))
 
 (defun eshell-remove-process-entry (entry)
   "Record the process ENTRY as fully completed."
   (if (and (eshell-processp (car entry))
-          (nth 2 entry)
+          (cdr entry)
           eshell-done-messages-in-minibuffer)
       (message "[%s]+ Done %s" (process-name (car entry))
               (process-command (car entry))))
   (setq eshell-process-list
        (delq entry eshell-process-list)))
 
+(defun eshell-record-process-properties (process &optional index)
+  "Record Eshell bookkeeping properties for PROCESS.
+`eshell-insertion-filter' and `eshell-sentinel' will use these to
+do their jobs.
+
+INDEX is the index of the output handle to use for writing; if
+nil, write to `eshell-output-handle'."
+  (process-put process :eshell-handles eshell-current-handles)
+  (process-put process :eshell-handle-index (or index eshell-output-handle))
+  (process-put process :eshell-pending nil)
+  (process-put process :eshell-busy nil))
+
 (defvar eshell-scratch-buffer " *eshell-scratch*"
   "Scratch buffer for holding Eshell's input/output.")
 (defvar eshell-last-sync-output-start nil
@@ -262,9 +277,21 @@ Used only on systems which do not support async 
subprocesses.")
              eshell-delete-exited-processes
            delete-exited-processes))
         (process-environment (eshell-environment-variables))
-        proc decoding encoding changed)
+        proc stderr-proc decoding encoding changed)
     (cond
      ((fboundp 'make-process)
+      (unless (equal (car (aref eshell-current-handles eshell-output-handle))
+                     (car (aref eshell-current-handles eshell-error-handle)))
+        (eshell-protect-handles eshell-current-handles)
+        (setq stderr-proc
+              (make-pipe-process
+               :name (concat (file-name-nondirectory command) "-stderr")
+               :buffer (current-buffer)
+               :filter (if (eshell-interactive-output-p eshell-error-handle)
+                           #'eshell-output-filter
+                         #'eshell-insertion-filter)
+               :sentinel #'eshell-sentinel))
+        (eshell-record-process-properties stderr-proc eshell-error-handle))
       (setq proc
             (let ((command (file-local-name (expand-file-name command)))
                   (conn-type (pcase (bound-and-true-p eshell-in-pipeline-p)
@@ -281,8 +308,10 @@ Used only on systems which do not support async 
subprocesses.")
                          #'eshell-insertion-filter)
                :sentinel #'eshell-sentinel
                :connection-type conn-type
+               :stderr stderr-proc
                :file-handler t)))
       (eshell-record-process-object proc)
+      (eshell-record-process-properties proc)
       (run-hook-with-args 'eshell-exec-hook proc)
       (when (fboundp 'process-coding-system)
        (let ((coding-systems (process-coding-system proc)))
@@ -363,36 +392,36 @@ PROC is the process for which we're inserting output.  
STRING is the
 output."
   (when (buffer-live-p (process-buffer proc))
     (with-current-buffer (process-buffer proc)
-      (let ((entry (assq proc eshell-process-list)))
-       (when entry
-         (setcar (nthcdr 3 entry)
-                 (concat (nth 3 entry) string))
-         (unless (nth 4 entry)         ; already being handled?
-           (while (nth 3 entry)
-             (let ((data (nth 3 entry)))
-               (setcar (nthcdr 3 entry) nil)
-               (setcar (nthcdr 4 entry) t)
-                (unwind-protect
-                    (condition-case nil
-                        (eshell-output-object data nil (cadr entry))
-                      ;; FIXME: We want to send SIGPIPE to the process
-                      ;; here.  However, remote processes don't
-                      ;; currently support that, and not all systems
-                      ;; have SIGPIPE in the first place (e.g. MS
-                      ;; Windows).  In these cases, just delete the
-                      ;; process; this is reasonably close to the
-                      ;; right behavior, since the default action for
-                      ;; SIGPIPE is to terminate the process.  For use
-                      ;; cases where SIGPIPE is truly needed, using an
-                      ;; external pipe operator (`*|') may work
-                      ;; instead (e.g. when working with remote
-                      ;; processes).
-                      (eshell-pipe-broken
-                       (if (or (process-get proc 'remote-pid)
-                               (eq system-type 'windows-nt))
-                           (delete-process proc)
-                         (signal-process proc 'SIGPIPE))))
-                  (setcar (nthcdr 4 entry) nil))))))))))
+      (process-put proc :eshell-pending
+                   (concat (process-get proc :eshell-pending)
+                           string))
+      (unless (process-get proc :eshell-busy) ; Already being handled?
+        (while (process-get proc :eshell-pending)
+          (let ((handles (process-get proc :eshell-handles))
+                (index (process-get proc :eshell-handle-index))
+                (data (process-get proc :eshell-pending)))
+            (process-put proc :eshell-pending nil)
+            (process-put proc :eshell-busy t)
+            (unwind-protect
+                (condition-case nil
+                    (eshell-output-object data index handles)
+                  ;; FIXME: We want to send SIGPIPE to the process
+                  ;; here.  However, remote processes don't currently
+                  ;; support that, and not all systems have SIGPIPE in
+                  ;; the first place (e.g. MS Windows).  In these
+                  ;; cases, just delete the process; this is
+                  ;; reasonably close to the right behavior, since the
+                  ;; default action for SIGPIPE is to terminate the
+                  ;; process.  For use cases where SIGPIPE is truly
+                  ;; needed, using an external pipe operator (`*|')
+                  ;; may work instead (e.g. when working with remote
+                  ;; processes).
+                  (eshell-pipe-broken
+                   (if (or (process-get proc 'remote-pid)
+                           (eq system-type 'windows-nt))
+                       (delete-process proc)
+                     (signal-process proc 'SIGPIPE))))
+              (process-put proc :eshell-busy nil))))))))
 
 (defun eshell-sentinel (proc string)
   "Generic sentinel for command processes.  Reports only signals.
@@ -400,37 +429,39 @@ PROC is the process that's exiting.  STRING is the exit 
message."
   (when (buffer-live-p (process-buffer proc))
     (with-current-buffer (process-buffer proc)
       (unwind-protect
-          (when-let ((entry (assq proc eshell-process-list)))
-           (unwind-protect
-               (unless (string= string "run")
-                  ;; Write the exit message if the status is
-                  ;; abnormal and the process is already writing
-                  ;; to the terminal.
-                  (when (and (eq proc (eshell-tail-process))
-                             (not (string-match "^\\(finished\\|exited\\)"
-                                                string)))
-                    (funcall (process-filter proc) proc string))
-                  (let ((handles (nth 1 entry))
-                        (str (prog1 (nth 3 entry)
-                               (setf (nth 3 entry) nil)))
-                        (status (process-exit-status proc)))
-                    ;; If we're in the middle of handling output
-                    ;; from this process then schedule the EOF for
-                    ;; later.
-                    (letrec ((finish-io
-                              (lambda ()
-                                (if (nth 4 entry)
-                                    (run-at-time 0 nil finish-io)
-                                  (when str
-                                    (ignore-error 'eshell-pipe-broken
-                                      (eshell-output-object
-                                       str nil handles)))
-                                  (eshell-close-handles
-                                   status (list 'quote (= status 0))
-                                   handles)))))
-                      (funcall finish-io))))
-             (eshell-remove-process-entry entry)))
-       (eshell-kill-process-function proc string)))))
+          (unless (string= string "run")
+            ;; Write the exit message if the status is abnormal and
+            ;; the process is already writing to the terminal.
+            (when (and (eq proc (eshell-tail-process))
+                       (not (string-match "^\\(finished\\|exited\\)"
+                                          string)))
+              (funcall (process-filter proc) proc string))
+            (let* ((handles (process-get proc :eshell-handles))
+                   (index (process-get proc :eshell-handle-index))
+                   (data (process-get proc :eshell-pending))
+                   ;; Only get the status for the primary subprocess,
+                   ;; not the pipe process (if any).
+                   (status (when (= index eshell-output-handle)
+                            (process-exit-status proc))))
+              (process-put proc :eshell-pending nil)
+              ;; If we're in the middle of handling output from this
+              ;; process then schedule the EOF for later.
+              (letrec ((finish-io
+                        (lambda ()
+                          (if (process-get proc :eshell-busy)
+                              (run-at-time 0 nil finish-io)
+                            (when data
+                              (ignore-error 'eshell-pipe-broken
+                                (eshell-output-object
+                                 data index handles)))
+                            (eshell-close-handles
+                             status
+                             (when status (list 'quote (= status 0)))
+                             handles)))))
+                (funcall finish-io))))
+        (when-let ((entry (assq proc eshell-process-list)))
+          (eshell-remove-process-entry entry))
+        (eshell-kill-process-function proc string)))))
 
 (defun eshell-process-interact (func &optional all query)
   "Interact with a process, using PROMPT if more than one, via FUNC.
@@ -441,7 +472,7 @@ If QUERY is non-nil, query the user with QUERY before 
calling FUNC."
       (if (and (memq (process-status (car entry))
                    '(run stop open closed))
               (or all
-                  (not (nth 2 entry)))
+                  (not (cdr entry)))
               (or (not query)
                   (y-or-n-p (format-message query
                                             (process-name (car entry))))))
diff --git a/lisp/eshell/esh-var.el b/lisp/eshell/esh-var.el
index a9df172e88..36e59cd5a4 100644
--- a/lisp/eshell/esh-var.el
+++ b/lisp/eshell/esh-var.el
@@ -646,23 +646,24 @@ For example, to retrieve the second element of a user's 
record in
   "Reference VALUE using the given INDEX."
   (when (and (stringp index) (get-text-property 0 'number index))
     (setq index (string-to-number index)))
-  (if (stringp index)
-      (cdr (assoc index value))
-    (cond
-     ((ring-p value)
-      (if (> index (ring-length value))
-         (error "Index exceeds length of ring")
-       (ring-ref value index)))
-     ((listp value)
-      (if (> index (length value))
-         (error "Index exceeds length of list")
-       (nth index value)))
-     ((vectorp value)
-      (if (> index (length value))
-         (error "Index exceeds length of vector")
-       (aref value index)))
-     (t
-      (error "Invalid data type for indexing")))))
+  (if (integerp index)
+      (cond
+       ((ring-p value)
+        (if (> index (ring-length value))
+            (error "Index exceeds length of ring")
+          (ring-ref value index)))
+       ((listp value)
+        (if (> index (length value))
+            (error "Index exceeds length of list")
+          (nth index value)))
+       ((vectorp value)
+        (if (> index (length value))
+            (error "Index exceeds length of vector")
+          (aref value index)))
+       (t
+        (error "Invalid data type for indexing")))
+    ;; INDEX is some non-integer value, so treat VALUE as an alist.
+    (cdr (assoc index value))))
 
 ;;;_* Variable name completion
 
diff --git a/lisp/eshell/eshell.el b/lisp/eshell/eshell.el
index 2c472a2afa..e0c927cad4 100644
--- a/lisp/eshell/eshell.el
+++ b/lisp/eshell/eshell.el
@@ -194,17 +194,6 @@ shells such as bash, zsh, rc, 4dos."
 ;; The following user options modify the behavior of Eshell overall.
 (defvar eshell-buffer-name)
 
-(defun eshell-add-to-window-buffer-names ()
-  "Add `eshell-buffer-name' to `same-window-buffer-names'."
-  (declare (obsolete nil "24.3"))
-  (add-to-list 'same-window-buffer-names eshell-buffer-name))
-
-(defun eshell-remove-from-window-buffer-names ()
-  "Remove `eshell-buffer-name' from `same-window-buffer-names'."
-  (declare (obsolete nil "24.3"))
-  (setq same-window-buffer-names
-       (delete eshell-buffer-name same-window-buffer-names)))
-
 (defcustom eshell-load-hook nil
   "A hook run once Eshell has been loaded."
   :type 'hook
diff --git a/lisp/faces.el b/lisp/faces.el
index 336078b040..09e8110449 100644
--- a/lisp/faces.el
+++ b/lisp/faces.el
@@ -666,21 +666,28 @@ If FACE is a face-alias, get the documentation for the 
target face."
 
 (defun set-face-attribute (face frame &rest args)
   "Set attributes of FACE on FRAME from ARGS.
-This function overrides the face attributes specified by FACE's
-face spec.  It is mostly intended for internal use only.
-
-If FRAME is nil, set the attributes for all existing frames, as
-well as the default for new frames.  If FRAME is t, change the
-default for new frames only.  As an exception, to reset the value
-of some attribute to `unspecified' in a way that overrides the
-non-`unspecified' value defined by the face's spec in `defface',
-for new frames, you must explicitly call this function with FRAME
-set to t and the attribute's value set to `unspecified'; just
-using FRAME of nil will not affect new frames in this case.
-
-ARGS must come in pairs ATTRIBUTE VALUE.  ATTRIBUTE must be a
-valid face attribute name.  All attributes can be set to
-`unspecified'; this fact is not further mentioned below.
+This function overrides the face attributes specified by FACE's face spec.
+It is mostly intended for internal use.
+
+If FRAME is a frame, set the FACE's attributes only for that frame.  If
+FRAME is nil, set attribute values for all existing frames, as well as
+the default for new frames.  If FRAME is t, change the default values
+of attributes for new frames.
+
+ARGS must come in pairs ATTRIBUTE VALUE.  ATTRIBUTE must be a valid face
+attribute name and VALUE must be a value that is valid for ATTRIBUTE,
+as described below for each attribute.
+
+In addition to the attribute values listed below, all attributes can
+also be set to the special value `unspecified', which means the face
+doesn't by itself specify a value for the attribute.
+
+When a new frame is created, attribute values in the FACE's `defface'
+spec normally override the `unspecified' values in the FACE's
+default attributes.  To avoid that, i.e. to cause ATTRIBUTE's value
+be reset to `unspecified' when creating new frames, disregarding
+what the FACE's face spec says, call this function with FRAME set to
+t and the ATTRIBUTE's value set to `unspecified'.
 
 The following attributes are recognized:
 
@@ -994,9 +1001,6 @@ Use `set-face-attribute' to \"unspecify\" underlining."
   (interactive (read-face-and-attribute :underline))
   (set-face-attribute face frame :underline underline))
 
-(define-obsolete-function-alias 'set-face-underline-p
-                                'set-face-underline "24.3")
-
 
 (defun set-face-inverse-video (face inverse-video-p &optional frame)
   "Specify whether face FACE is in inverse video.
@@ -2056,7 +2060,7 @@ IF MULTIPLE is non-nil, return a list of faces.
 
 Return nil if there is no face at point.
 
-This function is not meant for handling faces programatically; to
+This function is not meant for handling faces programmatically; to
 do that, use `get-text-property' and `get-char-property'."
   (let (faces)
     (when text
@@ -2544,7 +2548,6 @@ default."
   :version "21.1"
   :group 'basic-faces)
 
-;; Definition stolen from linum.el.
 (defface line-number
   '((t :inherit (shadow default)))
   "Face for displaying line numbers.
@@ -3167,12 +3170,6 @@ also the same size as FACE on FRAME, or fail."
        (car fonts))
     (frame-parameter nil 'font)))
 
-(defcustom font-list-limit 100
-  "This variable is obsolete and has no effect."
-  :type 'integer
-  :group 'display)
-(make-obsolete-variable 'font-list-limit nil "24.3")
-
 (define-obsolete-function-alias 'face-background-pixmap #'face-stipple "29.1")
 (define-obsolete-function-alias 'set-face-background-pixmap #'set-face-stipple 
"29.1")
 
diff --git a/lisp/ffap.el b/lisp/ffap.el
index 88b4bce9fd..482ac3764a 100644
--- a/lisp/ffap.el
+++ b/lisp/ffap.el
@@ -1504,7 +1504,11 @@ which may actually result in an URL rather than a 
filename."
         (progn
           (push elem file-name-handler-alist)
           (if (ffap-url-p guess)
-              (read-file-name prompt guess guess)
+              ;; We're using the default file name prompter here -- it
+              ;; allows you to switch back to reading a file name,
+              ;; while other prompters, like ido, really expect a
+              ;; file, and don't allow you to edit it if it's an URL.
+              (funcall #'read-file-name-default prompt guess guess)
             (unless guess
               (setq guess default-directory))
             (unless (ffap-file-remote-p guess)
@@ -1623,9 +1627,9 @@ and `ffap-url-at-point'."
        ((or (not ffap-newfile-prompt)
            (file-exists-p filename)
            (y-or-n-p "File does not exist, create buffer? "))
-       (find-file
-         ;; expand-file-name fixes "~/~/.emacs" bug
-        (expand-file-name filename)))
+       (funcall ffap-file-finder
+                 ;; expand-file-name fixes "~/~/.emacs" bug
+                (expand-file-name filename)))
        ;; User does not want to find a non-existent file:
        ((signal 'file-missing (list "Opening file buffer"
                                    "No such file or directory"
diff --git a/lisp/filenotify.el b/lisp/filenotify.el
index 94e07289e3..6b13ed0b72 100644
--- a/lisp/filenotify.el
+++ b/lisp/filenotify.el
@@ -339,6 +339,7 @@ DESC is the back-end descriptor.  ACTIONS is a list of:
   "Add a watch for FILE in DIR with FLAGS, using inotify."
   (inotify-add-watch dir
                      (append
+                      '(dont-follow)
                       (and (memq 'change flags)
                            '(create delete delete-self modify move-self move))
                       (and (memq 'attribute-change flags)
diff --git a/lisp/files.el b/lisp/files.el
index 740e09055b..43c5d7d1da 100644
--- a/lisp/files.el
+++ b/lisp/files.el
@@ -2079,12 +2079,6 @@ this function prepends a \"|\" to the final result if 
necessary."
     (uniquify--create-file-buffer-advice buf filename)
     buf))
 
-(defcustom automount-dir-prefix (purecopy "^/tmp_mnt/")
-  "Regexp to match the automounter prefix in a directory name."
-  :group 'files
-  :type 'regexp)
-(make-obsolete-variable 'automount-dir-prefix 'directory-abbrev-alist "24.3")
-
 (defvar abbreviated-home-dir nil
   "Regexp matching the user's homedir at the beginning of file name.
 The value includes abbreviation according to `directory-abbrev-alist'.")
@@ -2092,8 +2086,7 @@ The value includes abbreviation according to 
`directory-abbrev-alist'.")
 (defun abbreviate-file-name (filename)
   "Return a version of FILENAME shortened using `directory-abbrev-alist'.
 This also substitutes \"~\" for the user's home directory (unless the
-home directory is a root directory) and removes automounter prefixes
-\(see the variable `automount-dir-prefix').
+home directory is a root directory).
 
 When this function is first called, it caches the user's home
 directory as a regexp in `abbreviated-home-dir', and reuses it
@@ -2104,11 +2097,6 @@ started Emacs, set `abbreviated-home-dir' to nil so it 
will be recalculated)."
   (save-match-data                      ;FIXME: Why?
     (if-let ((handler (find-file-name-handler filename 'abbreviate-file-name)))
         (funcall handler 'abbreviate-file-name filename)
-      (if (and automount-dir-prefix
-               (string-match automount-dir-prefix filename)
-               (file-exists-p (file-name-directory
-                               (substring filename (1- (match-end 0))))))
-          (setq filename (substring filename (1- (match-end 0)))))
       ;; Avoid treating /home/foo as /home/Foo during `~' substitution.
       (let ((case-fold-search (file-name-case-insensitive-p filename)))
         ;; If any elt of directory-abbrev-alist matches this name,
@@ -2391,16 +2379,15 @@ the various files."
                                          'buffer-file-name buffer)))
                               (and file (file-exists-p file))))))))
        ;; Let user know if there is a buffer with the same truename.
-       (if other
-           (progn
-             (or nowarn
-                 find-file-suppress-same-file-warnings
-                 (string-equal filename (buffer-file-name other))
-                 (files--message "%s and %s are the same file"
-                                  filename (buffer-file-name other)))
-             ;; Optionally also find that buffer.
-             (if (or find-file-existing-other-name find-file-visit-truename)
-                 (setq buf other))))
+        (when other
+          (or nowarn
+              find-file-suppress-same-file-warnings
+              (string-equal filename (buffer-file-name other))
+              (files--message "%s and %s are the same file"
+                         filename (buffer-file-name other)))
+          ;; Optionally also find that buffer.
+          (if (or find-file-existing-other-name find-file-visit-truename)
+              (setq buf other)))
        ;; Check to see if the file looks uncommonly large.
        (when (not (or buf nowarn))
           (when (eq (abort-if-file-too-large
@@ -2723,7 +2710,8 @@ unless NOMODES is non-nil."
                   (file-newer-than-file-p (or buffer-auto-save-file-name
                                               (make-auto-save-file-name))
                                           buffer-file-name))
-                 (format "%s has auto save data; consider M-x 
recover-this-file"
+                  (format (substitute-command-keys
+                           "%s has auto save data; consider \\`M-x 
recover-this-file'")
                          (file-name-nondirectory buffer-file-name))
                (setq not-serious t)
                (if error "(New file)" nil)))
@@ -3028,6 +3016,7 @@ 
ARC\\|ZIP\\|LZH\\|LHA\\|ZOO\\|[JEW]AR\\|XPI\\|RAR\\|CBR\\|7Z\\|SQUASHFS\\)\\'" .
      ("[cC]hange[lL]og[-.][-0-9a-z]+\\'" . change-log-mode)
      ;; either user's dot-files or under /etc or some such
      
("/\\.?\\(?:gitconfig\\|gnokiirc\\|hgrc\\|kde.*rc\\|mime\\.types\\|wgetrc\\)\\'"
 . conf-mode)
+     ("/\\.mailmap\\'" . conf-unix-mode)
      ;; alas not all ~/.*rc files are like this
      
("/\\.\\(?:asound\\|enigma\\|fetchmail\\|gltron\\|gtk\\|hxplayer\\|mairix\\|mbsync\\|msmtp\\|net\\|neverball\\|nvidia-settings-\\|offlineimap\\|qt/.+\\|realplayer\\|reportbug\\|rtorrent\\.\\|screen\\|scummvm\\|sversion\\|sylpheed/.+\\|xmp\\)rc\\'"
 . conf-mode)
      
("/\\.\\(?:gdbtkinit\\|grip\\|mpdconf\\|notmuch-config\\|orbital/.+txt\\|rhosts\\|tuxracer/options\\)\\'"
 . conf-mode)
@@ -3343,6 +3332,7 @@ checks if it uses an interpreter listed in 
`interpreter-mode-alist',
 matches the buffer beginning against `magic-mode-alist',
 compares the file name against the entries in `auto-mode-alist',
 then matches the buffer beginning against `magic-fallback-mode-alist'.
+It also obeys `major-mode-remap-alist'.
 
 If `enable-local-variables' is nil, or if the file name matches
 `inhibit-local-variables-regexps', this function does not check
@@ -3480,6 +3470,17 @@ we don't actually set it to the same mode the buffer 
already has."
     (unless done
       (set-buffer-major-mode (current-buffer)))))
 
+(defvar-local set-auto-mode--last nil
+  "Remember the mode we have set via `set-auto-mode-0'.")
+
+(defcustom major-mode-remap-alist nil
+  "Alist mapping file-specified mode to actual mode.
+Every entry is of the form (MODE . FUNCTION) which means that in order
+to activate the major mode MODE (specified via something like
+`auto-mode-alist', file-local variables, ...) we should actually call
+FUNCTION instead."
+  :type '(alist (symbol) (function)))
+
 ;; When `keep-mode-if-same' is set, we are working on behalf of
 ;; set-visited-file-name.  In that case, if the major mode specified is the
 ;; same one we already have, don't actually reset it.  We don't want to lose
@@ -3490,10 +3491,15 @@ If optional arg KEEP-MODE-IF-SAME is non-nil, MODE is 
chased of
 any aliases and compared to current major mode.  If they are the
 same, do nothing and return nil."
   (unless (and keep-mode-if-same
-              (eq (indirect-function mode)
-                  (indirect-function major-mode)))
+              (or (eq (indirect-function mode)
+                      (indirect-function major-mode))
+                  (and set-auto-mode--last
+                       (eq mode (car set-auto-mode--last))
+                       (eq major-mode (cdr set-auto-mode--last)))))
     (when mode
-      (funcall mode)
+      (funcall (alist-get mode major-mode-remap-alist mode))
+      (unless (eq mode major-mode)
+        (setq set-auto-mode--last (cons mode major-mode)))
       mode)))
 
 (defvar file-auto-mode-skip "^\\(#!\\|'\\\\\"\\)"
@@ -3523,7 +3529,8 @@ have no effect."
                             ;; interpreter invocation.  The same holds
                             ;; for '\" in man pages (preprocessor
                             ;; magic for the `man' program).
-                            (and (looking-at file-auto-mode-skip) 2)) t)
+                            (and (looking-at file-auto-mode-skip) 2))
+                     t)
      (progn
        (skip-chars-forward " \t")
        (setq beg (point))
@@ -3605,7 +3612,6 @@ asking you for confirmation."
        inhibit-quit
        load-path
        max-lisp-eval-depth
-       max-specpdl-size
        minor-mode-map-alist
        minor-mode-overriding-map-alist
        mode-line-format
@@ -4878,6 +4884,14 @@ Interactively, this prompts for NEW-LOCATION."
                            (expand-file-name
                             (file-name-nondirectory (buffer-name))
                             default-directory)))))
+  ;; If the user has given a directory name, the file should be moved
+  ;; there (under the same file name).
+  (when (file-directory-p new-location)
+    (unless buffer-file-name
+      (user-error "Can't rename buffer to a directory file name"))
+    (setq new-location (expand-file-name
+                        (file-name-nondirectory buffer-file-name)
+                        new-location)))
   (when (and buffer-file-name
              (file-exists-p buffer-file-name))
     (rename-file buffer-file-name new-location))
@@ -5186,7 +5200,7 @@ On most systems, this will be true:
           (setq filename nil))))
     components))
 
-(defun file-parent-directory (filename)
+(defun file-name-parent-directory (filename)
   "Return the directory name of the parent directory of FILENAME.
 If FILENAME is at the root of the filesystem, return nil.
 If FILENAME is relative, it is interpreted to be relative
@@ -5196,7 +5210,9 @@ to `default-directory', and the result will also be 
relative."
     (cond
      ;; filename is at top-level, therefore no parent
      ((or (null parent)
-          (file-equal-p parent expanded-filename))
+          ;; `equal' is enough, we don't need to resolve symlinks here
+          ;; with `file-equal-p', also for performance
+          (equal parent expanded-filename))
       nil)
      ;; filename is relative, return relative parent
      ((not (file-name-absolute-p filename))
@@ -6098,14 +6114,6 @@ prints a message in the minibuffer.  Instead, use 
`set-buffer-modified-p'."
                     "Modification-flag cleared"))
   (set-buffer-modified-p arg))
 
-(defun toggle-read-only (&optional arg interactive)
-  "Change whether this buffer is read-only."
-  (declare (obsolete read-only-mode "24.3"))
-  (interactive (list current-prefix-arg t))
-  (if interactive
-      (call-interactively 'read-only-mode)
-    (read-only-mode (or arg 'toggle))))
-
 (defun insert-file (filename)
   "Insert contents of file FILENAME into buffer after point.
 Set mark after the inserted text.
@@ -6146,16 +6154,17 @@ recent files are first."
   (let* ((filename (file-name-sans-versions
                    (make-backup-file-name (expand-file-name filename))))
          (dir (file-name-directory filename)))
-    (sort
-     (seq-filter
-      (lambda (candidate)
-        (and (backup-file-name-p candidate)
-             (string= (file-name-sans-versions candidate) filename)))
-      (mapcar
-       (lambda (file)
-         (concat dir file))
-       (file-name-all-completions (file-name-nondirectory filename) dir)))
-     #'file-newer-than-file-p)))
+    (when (file-directory-p dir)
+      (sort
+       (seq-filter
+        (lambda (candidate)
+          (and (backup-file-name-p candidate)
+               (string= (file-name-sans-versions candidate) filename)))
+        (mapcar
+         (lambda (file)
+           (concat dir file))
+         (file-name-all-completions (file-name-nondirectory filename) dir)))
+       #'file-newer-than-file-p))))
 
 (defun rename-uniquely ()
   "Rename current buffer to a similar name not already taken.
@@ -8289,10 +8298,10 @@ CHAR is in [ugoa] and represents the category of users 
(Owner, Group,
 Others, or All) for whom to produce the mask.
 The bit-mask that is returned extracts from mode bits the access rights
 for the specified category of users."
-  (cond ((= char ?u) #o4700)
-       ((= char ?g) #o2070)
-       ((= char ?o) #o1007)
-       ((= char ?a) #o7777)
+  (cond ((eq char ?u) #o4700)
+       ((eq char ?g) #o2070)
+       ((eq char ?o) #o1007)
+       ((eq char ?a) #o7777)
         (t (error "%c: Bad `who' character" char))))
 
 (defun file-modes-char-to-right (char &optional from)
@@ -8300,22 +8309,22 @@ for the specified category of users."
 CHAR is in [rwxXstugo] and represents symbolic access permissions.
 If CHAR is in [Xugo], the value is taken from FROM (or 0 if omitted)."
   (or from (setq from 0))
-  (cond ((= char ?r) #o0444)
-       ((= char ?w) #o0222)
-       ((= char ?x) #o0111)
-       ((= char ?s) #o6000)
-       ((= char ?t) #o1000)
+  (cond ((eq char ?r) #o0444)
+       ((eq char ?w) #o0222)
+       ((eq char ?x) #o0111)
+       ((eq char ?s) #o6000)
+       ((eq char ?t) #o1000)
        ;; Rights relative to the previous file modes.
-       ((= char ?X) (if (= (logand from #o111) 0) 0 #o0111))
-       ((= char ?u) (let ((uright (logand #o4700 from)))
-                      ;; FIXME: These divisions/shifts seem to be right
-                       ;; for the `7' part of the #o4700 mask, but not
-                       ;; for the `4' part.  Same below for `g' and `o'.
-                      (+ uright (/ uright #o10) (/ uright #o100))))
-       ((= char ?g) (let ((gright (logand #o2070 from)))
-                      (+ gright (/ gright #o10) (* gright #o10))))
-       ((= char ?o) (let ((oright (logand #o1007 from)))
-                      (+ oright (* oright #o10) (* oright #o100))))
+       ((eq char ?X) (if (= (logand from #o111) 0) 0 #o0111))
+       ((eq char ?u) (let ((uright (logand #o4700 from)))
+                       ;; FIXME: These divisions/shifts seem to be right
+                        ;; for the `7' part of the #o4700 mask, but not
+                        ;; for the `4' part.  Same below for `g' and `o'.
+                       (+ uright (/ uright #o10) (/ uright #o100))))
+       ((eq char ?g) (let ((gright (logand #o2070 from)))
+                       (+ gright (/ gright #o10) (* gright #o10))))
+       ((eq char ?o) (let ((oright (logand #o1007 from)))
+                       (+ oright (* oright #o10) (* oright #o100))))
         (t (error "%c: Bad right character" char))))
 
 (defun file-modes-rights-to-number (rights who-mask &optional from)
diff --git a/lisp/filesets.el b/lisp/filesets.el
index 4831bf167d..aeebd907c3 100644
--- a/lisp/filesets.el
+++ b/lisp/filesets.el
@@ -358,8 +358,6 @@ Don't forget to check out 
`filesets-menu-ensure-use-cached'."
                         :value filesets-be-docile-flag)
                  (sexp :tag "Other" :value nil))))
 
-(define-obsolete-variable-alias 'filesets-cache-fill-content-hooks
-  'filesets-cache-fill-content-hook "24.3")
 (defcustom filesets-cache-fill-content-hook nil
   "Hook run when writing the contents of filesets' cache file.
 
diff --git a/lisp/find-file.el b/lisp/find-file.el
index 614ff420f2..646779fc91 100644
--- a/lisp/find-file.el
+++ b/lisp/find-file.el
@@ -193,18 +193,32 @@ The value could be an alist or a symbol whose value is an 
alist.
 Each element of the alist has the form
 
    (REGEXP (EXTENSION...))
-or
-   (REGEXP FUNCTION)
 
 where REGEXP is the regular expression matching a file's extension,
-EXTENSIONs is the list of literal file-name extensions to search for,
-and FUNCTION is a function of one argument, the current file's name,
-that returns the list of extensions to search for.
-The list of extensions should contain the most used extensions before the
-others, since the search algorithm searches sequentially through each
-directory specified in `ff-search-directories'.  If a file is not found,
-a new one is created with the first matching extension (`.cc' yields `.hh').
-This alist should be set by the major mode."
+and EXTENSIONs is the list of literal file-name extensions to search
+for.  The list of extensions should contain the most used extensions
+before the others, since the search algorithm searches sequentially
+through each directory specified in `ff-search-directories'.
+
+Alist elements can also be of the form
+
+   (REGEXP FUNCTION)
+
+where FUNCTION is a function of one argument, the current file's name,
+that returns the list of possible names of the corresponding files, with
+or without leading directories.  Note the difference: FUNCTION returns
+the list of file names, not their extensions.  This is for the case when
+REGEXP is not enough to determine the file name of the other file.
+
+If a file is not found, a new one is created with the first
+matching extension or name (e.g., `.cc' yields `.hh').
+
+This alist should be set by the major mode.
+
+Note: if an element of the alist names a FUNCTION as its cdr, that
+function must return a non-nil list of file-names.  It cannot
+return nil, nor can it signal in any way a failure to find a suitable
+list of file names."
   :type '(choice (repeat (list regexp (choice (repeat string) function)))
                 symbol))
 
@@ -615,7 +629,7 @@ name of the first file found."
             (while (and suffixes (not found))
 
               (setq filename (concat fname-stub this-suffix))
-              (setq file (concat dir "/" filename))
+              (setq file (expand-file-name filename dir))
 
               (if (not ff-quiet-mode)
                   (message "Finding %s..." file))
diff --git a/lisp/find-lisp.el b/lisp/find-lisp.el
index e825d9cba0..62b4ef625d 100644
--- a/lisp/find-lisp.el
+++ b/lisp/find-lisp.el
@@ -166,7 +166,8 @@ It is a function which takes two arguments, the directory 
and its parent."
 
 ;;;###autoload
 (defun find-lisp-find-dired (dir regexp)
-  "Find files in DIR, matching REGEXP."
+  "Find the files within DIR whose names match REGEXP.
+A Dired buffer with the results will be opened."
   (interactive "DFind files in directory: \nsMatching regexp: ")
   (let ((find-lisp-regexp regexp))
     (find-lisp-find-dired-internal
@@ -175,34 +176,54 @@ It is a function which takes two arguments, the directory 
and its parent."
      'find-lisp-default-directory-predicate
      "*Find Lisp Dired*")))
 
+(defun find-lisp-find-dired-other-window (dir regexp)
+  "Same as `find-lisp-find-dired', but use another window."
+  (interactive "DFind files in directory: \nsMatching regexp: ")
+  (let ((find-lisp-regexp regexp))
+    (find-lisp-find-dired-internal
+     dir
+     'find-lisp-default-file-predicate
+     'find-lisp-default-directory-predicate
+     "*Find Lisp Dired*"
+     'OTHER-WINDOW)))
+
 ;; Just the subdirectories
 ;;;###autoload
 (defun find-lisp-find-dired-subdirectories (dir)
   "Find all subdirectories of DIR."
-  (interactive "DFind subdirectories of directory: ")
+  (interactive "DFind dired subdirectories of directory: ")
   (find-lisp-find-dired-internal
    dir
    'find-lisp-file-predicate-is-directory
    'find-lisp-default-directory-predicate
    "*Find Lisp Dired Subdirectories*"))
 
+;;;###autoload
+(defun find-lisp-find-dired-subdirs-other-window (dir)
+  "Same as `find-lisp-find-dired-subdirectories', but use another window."
+  (interactive "DDired descendent dirs of directory: ")
+  (find-lisp-find-dired-internal dir
+                                 'find-lisp-file-predicate-is-directory
+                                 'find-lisp-default-directory-predicate
+                                 "*Find Lisp Dired Subdirectories*"
+                                 'OTHER-WINDOW))
+
 ;; Most of this is lifted from find-dired.el
 ;;
 (defun find-lisp-find-dired-internal (dir file-predicate
-                                         directory-predicate buffer-name)
+                                          directory-predicate buffer-name
+                                          &optional other-window)
   "Run find (Lisp version) and go into Dired mode on a buffer of the output."
-  (let ((dired-buffers dired-buffers)
-       (regexp find-lisp-regexp))
-    ;; Expand DIR ("" means default-directory), and make sure it has a
-    ;; trailing slash.
+  (let ((dired-buffers  dired-buffers)
+        (regexp         find-lisp-regexp))
+    ;; Expand DIR ("" means `default-directory'), ensuring a trailing slash.
     (setq dir (file-name-as-directory (expand-file-name dir)))
     ;; Check that it's really a directory.
     (or (file-directory-p dir)
        (error "find-dired needs a directory: %s" dir))
-    (or
-     (and (buffer-name)
-         (string= buffer-name (buffer-name)))
-       (switch-to-buffer (get-buffer-create buffer-name)))
+    (unless (and (buffer-name)  (string= buffer-name (buffer-name)))
+      (let ((buf  (get-buffer-create buffer-name)))
+        (if other-window (pop-to-buffer buf) (switch-to-buffer buf))))
     (widen)
     (kill-all-local-variables)
     (setq buffer-read-only nil)
@@ -278,10 +299,19 @@ It is a function which takes two arguments, the directory 
and its parent."
   (revert-buffer))
 
 (defun find-lisp-find-dired-insert-file (file buffer)
+  "Insert line for FILE in BUFFER.
+FILE is a file or a directory name.
+
+This function heeds `dired-actual-switches'."
   (set-buffer buffer)
   (insert find-lisp-line-indent
-         (find-lisp-format file (file-attributes file 'string) (list "")
-                           nil)))
+          (find-lisp-format
+           (propertize file 'dired-filename t)
+           (file-attributes file 'string)
+           (or (and dired-actual-switches
+                    (split-string-and-unquote dired-actual-switches))
+               (list ""))
+           nil)))
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;; Lifted from ls-lisp. We don't want to require it, because that
@@ -289,15 +319,14 @@ It is a function which takes two arguments, the directory 
and its parent."
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
 (defun find-lisp-format (file-name file-attr switches now)
-  "Format one line of long ls output for file FILE-NAME.
+  "Format one line of long `ls' output for file or directory FILE-NAME.
 FILE-ATTR and FILE-SIZE give the file's attributes and size.
 SWITCHES and TIME-INDEX give the full switch list and time data."
   (let ((file-type (file-attribute-type file-attr)))
-    (concat (if (memq ?i switches)     ; inode number
-               (format "%6d " (file-attribute-inode-number file-attr)))
-           ;; nil is treated like "" in concat
-           (if (memq ?s switches)      ; size in K
-               (format "%4d " (1+ (/ (file-attribute-size file-attr) 1024))))
+    (concat (and (memq ?i switches)    ; inode number
+                (format "%6d " (file-attribute-inode-number file-attr)))
+           (and (memq ?s switches)     ; size in K
+                (format "%4d " (1+ (/ (file-attribute-size file-attr) 1024))))
            (file-attribute-modes file-attr)
            (format " %3d %-8s %-8s %8d "
                    (file-attribute-link-number file-attr)
@@ -309,14 +338,14 @@ SWITCHES and TIME-INDEX give the full switch list and 
time data."
                      (if (numberp (file-attribute-group-id file-attr))
                          (int-to-string (file-attribute-group-id file-attr))
                        (file-attribute-group-id file-attr)))
-                   (file-attribute-size file-attr)
-                   )
+                   (file-attribute-size file-attr))
            (find-lisp-format-time file-attr switches now)
            " "
            file-name
-           (if (stringp file-type)     ; is a symbolic link
-               (concat " -> " file-type)
-             "")
+            (and (eq t file-type)  (memq ?F switches)
+                 "/")                  ; Add `/' for dir if `F' switch
+           (and (stringp file-type)
+                 (concat " -> " file-type)) ; Add " -> " for symbolic link
            "\n")))
 
 (defun find-lisp-time-index (switches)
diff --git a/lisp/format-spec.el b/lisp/format-spec.el
index 45c19aebc8..60ff9f9086 100644
--- a/lisp/format-spec.el
+++ b/lisp/format-spec.el
@@ -59,6 +59,18 @@ value associated with ?b in SPECIFICATION, either padding it 
with
 leading zeros or truncating leading characters until it's ten
 characters wide\".
 
+the substitution for a specification character can also be a
+function, taking no arguments and returning a string to be used
+for the replacement.  It will only be called if FORMAT uses that
+character.  For example:
+
+  (format-spec \"%n\"
+               \\=`((?n . ,(lambda ()
+                          (read-number \"Number: \")))))
+
+Note that it is best to make sure the function is not quoted,
+like above, so that it is compiled by the byte-compiler.
+
 Any text properties of FORMAT are copied to the result, with any
 text properties of a %-spec itself copied to its substitution.
 
@@ -94,14 +106,15 @@ is returned, where each format spec is its own element."
                  (width (match-string 2))
                  (trunc (match-string 3))
                  (char (string-to-char (match-string 4)))
-                 (text (assq char specification)))
+                 (text (let ((res (cdr (assq char specification))))
+                         (if (functionp res) (funcall res) res))))
             (when (and split
                        (not (= (1- beg) split-start)))
               (push (buffer-substring split-start (1- beg)) split-result))
             (cond (text
                    ;; Handle flags.
                    (setq text (format-spec--do-flags
-                               (format "%s" (cdr text))
+                               (format "%s" text)
                                (format-spec--parse-flags flags)
                                (and width (string-to-number width))
                                (and trunc (car (read-from-string trunc 1)))))
diff --git a/lisp/frame.el b/lisp/frame.el
index 9476cb0ec4..400f8a44ee 100644
--- a/lisp/frame.el
+++ b/lisp/frame.el
@@ -64,7 +64,7 @@ handles the corresponding kind of display.")
 ;; But that's not necessary, because the default is to have one.
 ;; By not specifying it here, we let an X resource specify it.
 (defcustom initial-frame-alist nil
-  "Alist of parameters for the initial X window frame.
+  "Alist of parameters for the initial window-system (a.k.a. \"GUI\") frame.
 You can set this in your init file; for example,
 
  (setq initial-frame-alist
@@ -77,17 +77,20 @@ If the value calls for a frame without a minibuffer, and 
you have
 not created a minibuffer frame on your own, a minibuffer frame is
 created according to `minibuffer-frame-alist'.
 
-You can specify geometry-related options for just the initial
-frame by setting this variable in your init file; however, they
-won't take effect until Emacs reads your init file, which happens
-after creating the initial frame.  If you want the initial frame
-to have the proper geometry as soon as it appears, you need to
-use this three-step process:
+Emacs reads your main init file after creating the initial frame,
+so setting it there won't have the expected effect.  Instead, you
+can set it in `early-init-file'.
+
+If you're using X, and you want (for instance) to have different
+geometries on different displays, you need to use this three-step
+process:
+
 * Specify X resources to give the geometry you want.
 * Set `default-frame-alist' to override these options so that they
   don't affect subsequent frames.
-* Set `initial-frame-alist' in a way that matches the X resources,
-  to override what you put in `default-frame-alist'."
+* Set `initial-frame-alist' in your normal init file in a way
+  that matches the X resources, to override what you put in
+  `default-frame-alist'."
   :type '(repeat (cons :format "%v"
                       (symbol :tag "Parameter")
                       (sexp :tag "Value")))
@@ -1790,7 +1793,7 @@ of frames like calls to map a frame or change its 
visibility."
             (insert (format ", DS=%sx%s" (nth 0 item) (nth 1 item))))
           (insert "\n"))
          ((and (eq (nth 0 item) frame) (= (nth 1 item) 5))
-          ;; Length 5 is an `adjust-frame-size' item.
+          ;; Length 5 is an 'adjust_frame_size' item.
           (insert (format "%s (%s)" (nth 3 item) (nth 2 item)))
           (setq item (nth 0 (cdr entry)))
           (unless (and (= (nth 0 item) (nth 2 item))
@@ -2422,9 +2425,9 @@ It may be less than the total screen size, owing to space 
taken up
 by window manager features (docks, taskbars, etc.).  The precise
 details depend on the platform and environment.
 
-The `source' attribute describes the source from which the information
-was obtained.  On X, this may be one of: \"Gdk\", \"XRandr\", \"Xinerama\",
-or \"fallback\".
+The `source' attribute describes the source from which the
+information was obtained.  On X, this may be one of: \"Gdk\",
+\"XRandR 1.5\", \"XRandr\", \"Xinerama\", or \"fallback\".
 
 A frame is dominated by a physical monitor when either the
 largest area of the frame resides in the monitor, or the monitor
@@ -2513,6 +2516,7 @@ symbols."
           ((eq frame-type 'pgtk)
            (pgtk-device-class name))
           (t (cond
+              ((not name) nil)
               ((string= name "Virtual core pointer")
                'core-pointer)
               ((string= name "Virtual core keyboard")
@@ -3048,16 +3052,8 @@ See also `toggle-frame-maximized'."
 
 ;; Misc.
 
-;; Only marked as obsolete in 24.3.
-(define-obsolete-variable-alias 'automatic-hscrolling
-  'auto-hscroll-mode "22.1")
-
 (make-variable-buffer-local 'show-trailing-whitespace)
 
-;; Defined in dispnew.c.
-(make-obsolete-variable
- 'window-system-version "it does not give useful information." "24.3")
-
 (defun set-frame-property--interactive (prompt number)
   "Get a value for `set-frame-width' or `set-frame-height', prompting with 
PROMPT.
 Offer NUMBER as default value, if it is a natural number."
diff --git a/lisp/generic-x.el b/lisp/generic-x.el
index 2c9d1b316e..bbc90493af 100644
--- a/lisp/generic-x.el
+++ b/lisp/generic-x.el
@@ -193,7 +193,6 @@ This hook will be installed if the variable
     hosts-generic-mode
     java-manifest-generic-mode
     java-properties-generic-mode
-    javascript-generic-mode
     show-tabs-generic-mode
     vrml-generic-mode)
   "List of generic modes that are defined by default.")
@@ -489,12 +488,6 @@ like an INI file.  You can add this hook to 
`find-file-hook'."
   nil
   "Generic mode for Sys V pkginfo files."))
 
-;; Javascript mode
-;; Obsolete; defer to js-mode from js.el.
-(when (memq 'javascript-generic-mode generic-extras-enable-list)
-  (define-obsolete-function-alias 'javascript-generic-mode 'js-mode "24.3")
-  (define-obsolete-variable-alias 'javascript-generic-mode-hook 'js-mode-hook 
"24.3"))
-
 ;; VRML files
 (when (memq 'vrml-generic-mode generic-extras-enable-list)
 
diff --git a/lisp/gnus/gnus-art.el b/lisp/gnus/gnus-art.el
index 83ba72c091..fbcf801313 100644
--- a/lisp/gnus/gnus-art.el
+++ b/lisp/gnus/gnus-art.el
@@ -8550,17 +8550,13 @@ url is put as the `gnus-button-url' overlay property on 
the button."
 (defvar gnus-next-page-line-format "%{%(Next page...%)%}\n")
 (defvar gnus-prev-page-line-format "%{%(Previous page...%)%}\n")
 
-(defvar gnus-prev-page-map
-  (let ((map (make-sparse-keymap)))
-    (define-key map [mouse-2] #'gnus-button-prev-page)
-    (define-key map "\r"      #'gnus-button-prev-page)
-    map))
+(defvar-keymap gnus-prev-page-map
+  "<mouse-2>" #'gnus-button-prev-page
+  "RET"       #'gnus-button-prev-page)
 
-(defvar gnus-next-page-map
-  (let ((map (make-sparse-keymap)))
-    (define-key map [mouse-2] #'gnus-button-next-page)
-    (define-key map "\r"      #'gnus-button-next-page)
-    map))
+(defvar-keymap gnus-next-page-map
+  "<mouse-2>" #'gnus-button-next-page
+  "RET"       #'gnus-button-next-page)
 
 (defun gnus-insert-prev-page-button ()
   (let ((b (point)) e
diff --git a/lisp/gnus/gnus-cloud.el b/lisp/gnus/gnus-cloud.el
index 9bd9f2155f..0e38fc0680 100644
--- a/lisp/gnus/gnus-cloud.el
+++ b/lisp/gnus/gnus-cloud.el
@@ -84,6 +84,7 @@ easy interactive way to set this from the Server buffer."
 
 (defun gnus-cloud-make-chunk (elems)
   (with-temp-buffer
+    (set-buffer-multibyte nil)
     (insert (format "Gnus-Cloud-Version %s\n" gnus-cloud-version))
     (insert (gnus-cloud-insert-data elems))
     (buffer-string)))
diff --git a/lisp/gnus/gnus-cus.el b/lisp/gnus/gnus-cus.el
index ddd939794d..32c475239e 100644
--- a/lisp/gnus/gnus-cus.el
+++ b/lisp/gnus/gnus-cus.el
@@ -36,11 +36,11 @@
 (define-derived-mode gnus-custom-mode fundamental-mode "Gnus Customize"
   "Major mode for editing Gnus customization buffers.
 
-The following commands are available:
+The following commands are available:\\<widget-keymap>
 
 \\[widget-forward]             Move to next button or editable field.
 \\[widget-backward]            Move to previous button or editable field.
-\\[widget-button-click]                Activate button under the mouse pointer.
+\\[widget-button-click]        Activate button under the mouse pointer.
 \\[widget-button-press]                Activate button under point.
 
 Entry to this mode calls the value of `gnus-custom-mode-hook'
diff --git a/lisp/gnus/gnus-group.el b/lisp/gnus/gnus-group.el
index fcad601d0c..35103e9c4f 100644
--- a/lisp/gnus/gnus-group.el
+++ b/lisp/gnus/gnus-group.el
@@ -1717,9 +1717,7 @@ already.  If INFO-UNCHANGED is non-nil, dribble buffer is 
not updated."
          (setq mode-string (substring mode-string 0 (- max-len 4))))
        (prog1
            (setq mode-line-buffer-identification
-                 (gnus-mode-line-buffer-identification
-                  (list (propertize mode-string
-                                    'face 'mode-line-buffer-id))))
+                 (gnus-mode-line-buffer-identification (list mode-string)))
          (set-buffer-modified-p modified))))))
 
 (defun gnus-group-group-name ()
@@ -2423,44 +2421,37 @@ the ephemeral group."
                                    (regexp-quote address)
                                    "\\(?:\\'\\|[ ,>]\\)"))
                (delim (concat "^" message-unix-mail-delimiter)))
-          (let ((coding-system-for-write 'binary)
-                (coding-system-for-read 'binary))
-            (with-temp-file tmpfile
-              (mm-disable-multibyte)
-              (dolist (id ids)
-                (let ((file (expand-file-name id (locate-user-emacs-file
-                                                  "debbugs-cache"))))
-                  (if (and (not gnus-plugged)
-                           (file-exists-p file))
-                      (insert-file-contents file)
-                    ;; Pass non-nil VISIT to avoid errors with non-nil
-                    ;; `url-automatic-caching' (bug#26063, bug#29008)
-                    ;; and immediately unvisit.
-                    ;; FIXME: This masks real errors!
-                    (url-insert-file-contents (format mbox-url id) t)
-                    (setq buffer-file-name nil))))
-             (goto-char (point-min))
-              ;; Throw an informative error early instead of passing nonsense
-              ;; to `gnus-group-read-ephemeral-group' (bug#36433).
-              (unless (save-excursion (re-search-forward delim nil t))
-                (error "Invalid mbox format for bug IDs: %s"
-                       (string-join ids ", ")))
-              (while (re-search-forward delim nil t)
-                (narrow-to-region (point)
-                                  (if (search-forward "\n\n" nil t)
-                                      (1- (point))
-                                    (point-max)))
-                (unless (string-match-p address-re
-                                        (concat (message-fetch-field "to") " "
-                                                (message-fetch-field "cc")))
-                  (goto-char (point-min))
-                  (if (not (re-search-forward "^To:" nil t))
-                      (insert "To: " address "\n")
-                   (message-next-header)
-                   (skip-chars-backward "\t\n ")
-                    (insert ", " address)))
-                (goto-char (point-max))
-                (widen))))
+          (with-temp-file tmpfile
+            (mm-disable-multibyte)
+            (dolist (id ids)
+              (let ((file (expand-file-name id (locate-user-emacs-file
+                                                "debbugs-cache"))))
+                (if (and (not gnus-plugged)
+                         (file-exists-p file))
+                    (insert-file-contents-literally file)
+                  (url-insert-file-contents-literally (format mbox-url id)))))
+           (goto-char (point-min))
+            ;; Throw an informative error early instead of passing nonsense
+            ;; to `gnus-group-read-ephemeral-group' (bug#36433).
+            (unless (save-excursion (re-search-forward delim nil t))
+              (error "Invalid mbox format for bug IDs: %s"
+                     (string-join ids ", ")))
+            (while (re-search-forward delim nil t)
+              (narrow-to-region (point)
+                                (if (search-forward "\n\n" nil t)
+                                    (1- (point))
+                                  (point-max)))
+              (unless (string-match-p address-re
+                                      (concat (message-fetch-field "to") " "
+                                              (message-fetch-field "cc")))
+                (goto-char (point-min))
+                (if (not (re-search-forward "^To:" nil t))
+                    (insert "To: " address "\n")
+                 (message-next-header)
+                 (skip-chars-backward "\t\n ")
+                  (insert ", " address)))
+              (goto-char (point-max))
+              (widen)))
           (gnus-group-read-ephemeral-group
            (concat "nndoc+ephemeral:bug#" (string-join ids ","))
            `(nndoc ,tmpfile
diff --git a/lisp/gnus/gnus-search.el b/lisp/gnus/gnus-search.el
index 327dba95c0..b8f7e7a08f 100644
--- a/lisp/gnus/gnus-search.el
+++ b/lisp/gnus/gnus-search.el
@@ -2247,11 +2247,9 @@ article came from is also searched."
            (forward-line)))))
     groups))
 
-(defvar gnus-search-minibuffer-map
-  (let ((km (make-sparse-keymap)))
-    (set-keymap-parent km minibuffer-local-map)
-    (define-key km (kbd "TAB") #'completion-at-point)
-    km))
+(defvar-keymap gnus-search-minibuffer-map
+  :parent minibuffer-local-map
+  "TAB" #'completion-at-point)
 
 (defun gnus-search--complete-key-data ()
   "Potentially return completion data for a search key or value."
diff --git a/lisp/gnus/gnus-srvr.el b/lisp/gnus/gnus-srvr.el
index e659a648e1..315381a6dd 100644
--- a/lisp/gnus/gnus-srvr.el
+++ b/lisp/gnus/gnus-srvr.el
@@ -829,9 +829,10 @@ claim them."
          (erase-buffer))
        (gnus-browse-mode)
        (setq mode-line-buffer-identification
-             (list
-              (format
-               "Gnus: %%b {%s:%s}" (car method) (cadr method))))
+             (gnus-mode-line-buffer-identification
+               (list
+               (format
+                "Gnus: %%b {%s:%s}" (car method) (cadr method)))))
        (let ((buffer-read-only nil)
              name
              (prefix (let ((gnus-select-method orig-select-method))
diff --git a/lisp/gnus/gnus-start.el b/lisp/gnus/gnus-start.el
index 7700e6bd43..8d9e50059f 100644
--- a/lisp/gnus/gnus-start.el
+++ b/lisp/gnus/gnus-start.el
@@ -294,8 +294,6 @@ claim them."
                function
                (repeat function)))
 
-(define-obsolete-variable-alias 'gnus-subscribe-newsgroup-hooks
-  'gnus-subscribe-newsgroup-functions "24.3")
 (defcustom gnus-subscribe-newsgroup-functions nil
   "Hooks run after you subscribe to a new group.
 The hooks will be called with new group's name as argument."
diff --git a/lisp/gnus/gnus-sum.el b/lisp/gnus/gnus-sum.el
index dde60caee7..107ad8fd4a 100644
--- a/lisp/gnus/gnus-sum.el
+++ b/lisp/gnus/gnus-sum.el
@@ -6207,8 +6207,7 @@ If WHERE is `summary', the summary mode line format will 
be used."
       ;; Update the mode line.
       (setq mode-line-buffer-identification
            (gnus-mode-line-buffer-identification
-            (list (propertize mode-string
-                              'face 'mode-line-buffer-id))))
+            (list mode-string)))
       (set-buffer-modified-p t))))
 
 (defun gnus-create-xref-hashtb (from-newsgroup headers unreads)
diff --git a/lisp/gnus/gnus.el b/lisp/gnus/gnus.el
index 0afd873a5d..778a46dab3 100644
--- a/lisp/gnus/gnus.el
+++ b/lisp/gnus/gnus.el
@@ -310,12 +310,15 @@ be set in `.emacs' instead."
   :type 'boolean)
 
 (defun gnus-mode-line-buffer-identification (line)
-  (let ((str (car-safe line)))
+  (let* ((str (car-safe line))
+         (str (if (stringp str)
+                  (car (propertized-buffer-identification str))
+                str)))
     (if (or (not (fboundp 'find-image))
            (not (display-graphic-p))
            (not (stringp str))
            (not (string-match "^Gnus:" str)))
-       line
+       (list str)
       (let ((load-path (append (mm-image-load-path) load-path)))
        ;; Add the Gnus logo.
        (add-text-properties
@@ -2232,7 +2235,7 @@ Disabling the agent may result in noticeable loss of 
performance."
                       (symbol :tag "Parameter")
                       (sexp :tag "Value"))))
 
-(defcustom gnus-user-agent '(emacs gnus type)
+(defcustom gnus-user-agent '(gnus)
   "Which information should be exposed in the User-Agent header.
 
 Can be a list of symbols or a string.  Valid symbols are `gnus'
@@ -2240,7 +2243,7 @@ Can be a list of symbols or a string.  Valid symbols are 
`gnus'
 addition to the Emacs version, you can add `config' (show system
 configuration) or `type' (show system type).  If you set it to a
 string, be sure to use a valid format, see RFC 2616."
-  :version "22.1"
+  :version "29.1"
   :group 'gnus-message
   :type '(choice (list (set :inline t
                             (const :value gnus  :tag "Gnus version")
@@ -2250,25 +2253,6 @@ string, be sure to use a valid format, see RFC 2616."
                                     (const :value config :tag "system 
configuration"))))
                 (string)))
 
-;; Convert old (< 2005-01-10) symbol type values:
-(when (symbolp gnus-user-agent)
-  (setq gnus-user-agent
-       (cond ((eq gnus-user-agent 'emacs-gnus-config)
-              '(emacs gnus config))
-             ((eq gnus-user-agent 'emacs-gnus-type)
-              '(emacs gnus type))
-             ((eq gnus-user-agent 'emacs-gnus)
-              '(emacs gnus))
-             ((eq gnus-user-agent 'gnus)
-              '(gnus))
-             (t gnus-user-agent)))
-  (gnus-message 1 "Converted `gnus-user-agent' to `%s'." gnus-user-agent)
-  (sit-for 1)
-  (if (get 'gnus-user-agent 'saved-value)
-      (customize-save-variable 'gnus-user-agent gnus-user-agent)
-    (gnus-message 1 "Edit your init file to make this change permanent.")
-    (sit-for 2)))
-
 (defcustom gnus-agent-eagerly-store-articles t
   "If non-nil, cache articles eagerly.
 
diff --git a/lisp/gnus/message.el b/lisp/gnus/message.el
index 49a04f601f..beccef6f5f 100644
--- a/lisp/gnus/message.el
+++ b/lisp/gnus/message.el
@@ -888,9 +888,22 @@ symbol `never', the posting is not allowed.  If it is the 
symbol
   ;; FIXME: This is related to `mail-specify-envelope-from' but works
   ;; differently (bug#36937).
   nil
-  "Non-nil means don't add \"-f username\" to the sendmail command line.
-See `feedmail-sendmail-f-doesnt-sell-me-out' for an explanation
-of what the \"-f\" parameter does."
+  "Non-nil means don't add \"-f username\" to the \"sendmail\" command line.
+The \"sendmail\" program has a useful feature to let you set the
+envelope FROM address via a command line option, \"-f\".
+Unfortunately, it also has a widely disliked default behavior of
+disclosing your actual user name anyway by inserting an
+unattractive warning in the headers.  It looks something like
+this:
+
+  X-Authentication-Warning: u1.example.com: niceguy set
+      sender to niceguy@example.com using -f
+
+It is possible to configure \"sendmail\" to not do this, but such a
+reconfiguration is not an option for some users.
+
+Note that this user option is mostly useful for actual \"sendmail\"
+installations, which are rare these days."
   :group 'message-sending
   :link '(custom-manual "(message)Mail Variables")
   :type 'boolean)
@@ -3195,7 +3208,8 @@ Like `text-mode', but with these additional commands:
   ;;
   (setq-local syntax-propertize-function #'message--syntax-propertize)
   (setq-local parse-sexp-ignore-comments t)
-  (setq-local message-encoded-mail-cache nil))
+  (setq-local message-encoded-mail-cache nil)
+  (setq-local image-crop-buffer-text-function #'message--update-image-crop))
 
 (defun message-setup-fill-variables ()
   "Setup message fill variables."
@@ -3551,7 +3565,12 @@ of lines before the signature intact."
 
 (defun message-newline-and-reformat (&optional arg not-break)
   "Insert four newlines, and then reformat if inside quoted text.
-Prefix arg means justify as well."
+Prefix arg means justify as well.
+
+This function tries to guess what the quote prefix is based on
+the text on the current line before point.  If point is at the
+start of the line, the formatted text (if any) is filled without
+a quote prefix."
   (interactive (list (if current-prefix-arg 'full)) message-mode)
   (unless (message-in-body-p)
     (error "This command only works in the body of the message"))
@@ -8909,19 +8928,26 @@ used to take the screenshot."
                 :max-width (truncate (* (frame-pixel-width) 0.8))
                 :max-height (truncate (* (frame-pixel-height) 0.8))
                 :scale 1)
-   (format "<#part type=\"%s\" disposition=inline data-encoding=base64 
raw=t>\n%s\n<#/part>"
-           type
-          ;; Get a base64 version of the image -- this avoids later
-          ;; complications if we're auto-saving the buffer and
-          ;; restoring from a file.
-          (with-temp-buffer
-            (set-buffer-multibyte nil)
-            (insert image)
-            (base64-encode-region (point-min) (point-max) t)
-            (buffer-string)))
+   (message--image-part-string type image)
    nil nil t)
   (insert "\n\n"))
 
+(defun message--image-part-string (type image)
+  (format "<#part type=\"%s\" disposition=inline data-encoding=base64 
raw=t>\n%s\n<#/part>"
+          type
+         ;; Get a base64 version of the image -- this avoids later
+         ;; complications if we're auto-saving the buffer and
+         ;; restoring from a file.
+         (with-temp-buffer
+           (set-buffer-multibyte nil)
+           (insert image)
+           (base64-encode-region (point-min) (point-max) t)
+           (buffer-string))))
+
+(declare-function image-crop--content-type "image-crop")
+(defun message--update-image-crop (_text image)
+  (message--image-part-string (image-crop--content-type image) image))
+
 (declare-function gnus-url-unhex-string "gnus-util")
 
 (defun message-parse-mailto-url (url)
diff --git a/lisp/gnus/nndiary.el b/lisp/gnus/nndiary.el
index 27204b3618..ab9c6dd74f 100644
--- a/lisp/gnus/nndiary.el
+++ b/lisp/gnus/nndiary.el
@@ -165,22 +165,16 @@ In order to make this clear, here are some examples:
   :type 'boolean)
 
 
-(define-obsolete-variable-alias 'nndiary-request-create-group-hooks
-  'nndiary-request-create-group-functions "24.3")
 (defcustom nndiary-request-create-group-functions nil
   "Hook run after `nndiary-request-create-group' is executed.
 The hook functions will be called with the full group name as argument."
   :type 'hook)
 
-(define-obsolete-variable-alias 'nndiary-request-update-info-hooks
-  'nndiary-request-update-info-functions "24.3")
 (defcustom nndiary-request-update-info-functions nil
   "Hook run after `nndiary-request-update-info' is executed.
 The hook functions will be called with the full group name as argument."
   :type 'hook)
 
-(define-obsolete-variable-alias 'nndiary-request-accept-article-hooks
-  'nndiary-request-accept-article-functions "24.3")
 (defcustom nndiary-request-accept-article-functions nil
   "Hook run before accepting an article.
 Executed near the beginning of `nndiary-request-accept-article'.
diff --git a/lisp/gnus/score-mode.el b/lisp/gnus/score-mode.el
index 8e27e87939..4c9d73a6e5 100644
--- a/lisp/gnus/score-mode.el
+++ b/lisp/gnus/score-mode.el
@@ -45,13 +45,11 @@
 (defvar gnus-score-edit-exit-function nil
   "Function run on exit from the score buffer.")
 
-(defvar gnus-score-mode-map
-  (let ((map (make-sparse-keymap)))
-    (set-keymap-parent map emacs-lisp-mode-map)
-    (define-key map "\C-c\C-c" 'gnus-score-edit-exit)
-    (define-key map "\C-c\C-d" 'gnus-score-edit-insert-date)
-    (define-key map "\C-c\C-p" 'gnus-score-pretty-print)
-    map))
+(defvar-keymap gnus-score-mode-map
+  :parent emacs-lisp-mode-map
+  "C-c C-c" #'gnus-score-edit-exit
+  "C-c C-d" #'gnus-score-edit-insert-date
+  "C-c C-p" #'gnus-score-pretty-print)
 
 (defvar score-mode-syntax-table
   (let ((table (copy-syntax-table lisp-mode-syntax-table)))
diff --git a/lisp/gnus/smime.el b/lisp/gnus/smime.el
index fd2791f5c5..7bb116d0c5 100644
--- a/lisp/gnus/smime.el
+++ b/lisp/gnus/smime.el
@@ -614,12 +614,10 @@ A string or a list of strings is returned."
 
 (defvar smime-buffer "*SMIME*")
 
-(defvar smime-mode-map
-  (let ((map (make-sparse-keymap)))
-    (suppress-keymap map)
-    (define-key map "q" 'smime-exit)
-    (define-key map "f" 'smime-certificate-info)
-    map))
+(defvar-keymap smime-mode-map
+  :suppress t
+  "q" #'smime-exit
+  "f" #'smime-certificate-info)
 
 (autoload 'gnus-completing-read "gnus-util")
 
diff --git a/lisp/help-fns.el b/lisp/help-fns.el
index 1ccf9bb428..eef895ae88 100644
--- a/lisp/help-fns.el
+++ b/lisp/help-fns.el
@@ -229,7 +229,7 @@ interactive command."
                (lambda (f) (if want-command
                           (commandp f)
                         (or (fboundp f) (get f 'function-documentation))))
-               t nil nil
+               'confirm nil nil
                (and fn (symbol-name fn)))))
     (unless (equal val "")
       (setq fn (intern val)))
@@ -457,7 +457,9 @@ the C sources, too."
                     load-path '(".el" ".elc") 'readable))))))))
 
     (cond
-     ((and (not file-name) (subrp type))
+     ((and (not file-name)
+           (subrp type)
+           (not (subr-native-elisp-p type)))
       ;; A built-in function.  The form is from `describe-function-1'.
       (if (or (get-buffer " *DOC*")
               (and also-c-source
@@ -515,8 +517,11 @@ the C sources, too."
     (let ((pt2 (with-current-buffer standard-output (point)))
           (remapped (command-remapping function)))
       (unless (memq remapped '(ignore undefined))
-        (let* ((all-keys (where-is-internal
-                          (or remapped function) overriding-local-map nil nil))
+        (let* ((all-keys
+                (with-current-buffer
+                    (or describe-function-orig-buffer (current-buffer))
+                  (where-is-internal
+                   (or remapped function) overriding-local-map nil nil)))
                (seps (seq-group-by
                       (lambda (key)
                         (and (vectorp key)
@@ -583,36 +588,43 @@ the C sources, too."
                   keys))
 
 (defun help-fns--insert-menu-bindings (menus heading)
-  (seq-do-indexed
-   (lambda (menu i)
-     (insert
-      (cond ((zerop i) "")
-            ((= i (1- (length menus))) " and ")
-            (t ", ")))
-     (let ((map (lookup-key global-map (seq-take menu 1)))
-           (start (point)))
-       (seq-do-indexed
-        (lambda (entry level)
-          (when (symbolp map)
-            (setq map (symbol-function map)))
-          (when-let ((elem (assq entry (cdr map))))
-            (when heading
-              (insert heading)
-              (setq heading nil start (point)))
-            (when (> level 0)
-              (insert
-               (if (char-displayable-p ?→)
-                   " → "
-                 " => ")))
-            (if (eq (nth 1 elem) 'menu-item)
-                (progn
-                  (insert (nth 2 elem))
-                  (setq map (cadddr elem)))
-              (insert (nth 1 elem))
-              (setq map (cddr elem)))))
-        (cdr (seq-into menu 'list)))
-       (put-text-property start (point) 'face 'help-key-binding)))
-   menus))
+  (let ((strings nil))
+    ;; First collect all the printed representations of menus.
+    (dolist (menu menus)
+      (let ((map (lookup-key global-map (seq-take menu 1)))
+            (string nil))
+        (seq-do-indexed
+         (lambda (entry level)
+           (when (symbolp map)
+             (setq map (symbol-function map)))
+           (when-let ((elem (assq entry (cdr map))))
+             (when (> level 0)
+               (push (if (char-displayable-p ?→)
+                         " → "
+                       " => ")
+                     string))
+             (if (eq (nth 1 elem) 'menu-item)
+                 (progn
+                   (push (nth 2 elem) string)
+                   (setq map (cadddr elem)))
+               (push (nth 1 elem) string)
+               (setq map (cddr elem)))))
+         (cdr (seq-into menu 'list)))
+        (when string
+          (push string strings))))
+    ;; Then output them.
+    (when strings
+      (when heading
+        (insert heading))
+      (seq-do-indexed
+       (lambda (string i)
+         (insert
+          (cond ((zerop i) "")
+                ((= i (1- (length menus))) " and ")
+                (t ", "))
+          (propertize (string-join (nreverse string))
+                      'face 'help-key-binding)))
+       strings))))
 
 (defun help-fns--compiler-macro (function)
   (pcase-dolist (`(,type . ,handler)
@@ -709,13 +721,13 @@ the C sources, too."
                           (get function
                                'derived-mode-parent))))
     (when parent-mode
-      (insert (substitute-command-keys "  Parent mode: `"))
+      (insert (substitute-quotes "  Parent mode: `"))
       (let ((beg (point)))
         (insert (format "%s" parent-mode))
         (make-text-button beg (point)
                           'type 'help-function
                           'help-args (list parent-mode)))
-      (insert (substitute-command-keys "'.\n")))))
+      (insert (substitute-quotes "'.\n")))))
 
 (defun help-fns--obsolete (function)
   ;; Ignore lambda constructs, keyboard macros, etc.
@@ -1156,7 +1168,8 @@ Returns a list of the form (REAL-FUNCTION DEF ALIASED 
REAL-DEF)."
 (add-hook 'help-fns-describe-function-functions #'help-fns--compiler-macro 100)
 
 (defun help-fns--generalized-variable (function)
-  (when (and (get function 'gv-expander)
+  (when (and (symbolp function)
+             (get function 'gv-expander)
              ;; Don't mention obsolete generalized variables.
              (not (get function 'byte-obsolete-generalized-variable)))
     (insert (format-message "  `%s' is also a " function)
@@ -1556,7 +1569,7 @@ This cancels value editing without updating the value."
     (princ "  This variable may be risky if used as a \
 file-local variable.\n")
     (when (assq variable safe-local-variable-values)
-      (princ (substitute-command-keys
+      (princ (substitute-quotes
               "  However, you have added it to \
 `safe-local-variable-values'.\n")))))
 
@@ -1606,8 +1619,8 @@ variable.\n")))
                  (insert-text-button
                   file 'type 'help-dir-local-var-def
                    'help-args (list variable file)))
-               (princ (substitute-command-keys "'.\n"))))
-          (princ (substitute-command-keys
+                (princ (substitute-quotes "'.\n"))))
+          (princ (substitute-quotes
                  "  This variable's value is file-local.\n")))))))
 
 (add-hook 'help-fns-describe-variable-functions #'help-fns--var-watchpoints)
@@ -1687,10 +1700,10 @@ variable.\n")))
      ((not permanent-local))
      ((bufferp locus)
       (princ
-       (substitute-command-keys
+       (substitute-quotes
         "  This variable's buffer-local value is permanent.\n")))
      (t
-      (princ (substitute-command-keys
+      (princ (substitute-quotes
              "  This variable's value is permanent \
 if it is given a local binding.\n"))))))
 
@@ -1767,9 +1780,9 @@ If FRAME is omitted or nil, use the selected frame."
                     (setq help-mode--current-data (list :symbol f))
                   (setq help-mode--current-data (list :symbol f
                                                       :file file-name))
-                 (princ (substitute-command-keys "Defined in `"))
+                (princ (substitute-quotes "Defined in `"))
                  (princ (help-fns-short-filename file-name))
-                 (princ (substitute-command-keys "'"))
+                (princ (substitute-quotes "'"))
                  ;; Make a hyperlink to the library.
                  (save-excursion
                    (re-search-backward
@@ -2179,8 +2192,7 @@ documentation for the major and minor modes of that 
buffer."
        ;; Document the minor modes fully.
         (insert (buttonize
                  (propertize pretty-minor-mode 'help-minor-mode mode)
-                 (lambda (mode)
-                   (describe-function mode))
+                 #'describe-function
                  mode))
         (let ((indicator
                (format-mode-line (assq mode minor-mode-alist))))
@@ -2189,7 +2201,8 @@ documentation for the major and minor modes of that 
buffer."
                              "no indicator"
                            (format "indicator%s"
                                    indicator)))))
-       (insert (help-split-fundoc (documentation mode) nil 'doc)))))
+       (insert (or (help-split-fundoc (documentation mode) nil 'doc)
+                   "No docstring")))))
   (forward-line -1)
   (fill-paragraph nil)
   (forward-paragraph 1)
diff --git a/lisp/help.el b/lisp/help.el
index 15ab3192ad..b4b9120da3 100644
--- a/lisp/help.el
+++ b/lisp/help.el
@@ -1204,7 +1204,16 @@ Otherwise, return a new string."
                 (delete-char 2)
                 (let* ((fun (intern (buffer-substring (point) (1- end-point))))
                        (key (with-current-buffer orig-buf
-                              (where-is-internal fun keymap t))))
+                              (where-is-internal fun
+                                                 (and keymap
+                                                      (list keymap))
+                                                 t))))
+                  ;; If we're looking in a particular keymap which has
+                  ;; no binding, then we need to redo the lookup, with
+                  ;; the global map as well this time.
+                  (when (and (not key) keymap)
+                    (setq key (with-current-buffer orig-buf
+                                (where-is-internal fun keymap t))))
                   (if (not key)
                       ;; Function is not on any key.
                       (let ((op (point)))
@@ -1260,9 +1269,9 @@ Otherwise, return a new string."
                   (cond
                    ((null this-keymap)
                     (insert "\nUses keymap "
-                            (substitute-command-keys "`")
+                            (substitute-quotes "`")
                             (symbol-name name)
-                            (substitute-command-keys "'")
+                            (substitute-quotes "'")
                             ", which is not currently defined.\n")
                     (unless generate-summary
                       (setq keymap nil)))
@@ -1291,6 +1300,18 @@ Otherwise, return a new string."
              (t (forward-char 1)))))
         (buffer-string)))))
 
+(defun substitute-quotes (string)
+  "Substitute quote characters for display.
+Each grave accent \\=` is replaced by left quote, and each
+apostrophe \\=' is replaced by right quote.  Left and right quote
+characters are specified by `text-quoting-style'."
+  (cond ((eq (text-quoting-style) 'curve)
+         (string-replace "`" "‘"
+                         (string-replace "'" "’" string)))
+        ((eq (text-quoting-style) 'straight)
+         (string-replace "`" "'" string))
+        (t string)))
+
 (defvar help--keymaps-seen nil)
 (defun describe-map-tree (startmap &optional partial shadow prefix title
                                    no-menu transl always-title mention-shadow
diff --git a/lisp/hexl.el b/lisp/hexl.el
index 7f965486ea..b8d25bfb1f 100644
--- a/lisp/hexl.el
+++ b/lisp/hexl.el
@@ -687,7 +687,7 @@ If there is no byte at the target address move to the last 
byte in that line."
 
 (defun hexl-beginning-of-buffer (arg)
   "Move to the beginning of the hexl buffer.
-Leaves `hexl-mark' at previous position.
+Leaves mark at previous position.
 With prefix arg N, puts point N bytes of the way from the true beginning."
   (interactive "p")
   (push-mark)
diff --git a/lisp/hilit-chg.el b/lisp/hilit-chg.el
index 4832dd9023..00748e12da 100644
--- a/lisp/hilit-chg.el
+++ b/lisp/hilit-chg.el
@@ -118,7 +118,6 @@
 ;;
 ;;     Other interactive functions (that could be bound if desired):
 ;; `highlight-changes-mode'
-;; `highlight-changes-toggle-visibility'
 ;; `highlight-changes-remove-highlight'
 ;; `highlight-compare-with-file'
 ;; `highlight-compare-buffers'
diff --git a/lisp/hl-line.el b/lisp/hl-line.el
index e5ca6819f0..693c94eea8 100644
--- a/lisp/hl-line.el
+++ b/lisp/hl-line.el
@@ -154,6 +154,12 @@ non-selected window.  Hl-Line mode uses the function
 When `hl-line-sticky-flag' is nil, Hl-Line mode highlights the
 line about point in the selected window only."
   :group 'hl-line
+  ;; If the global mode is switched on, then `M-x hl-line-mode' should
+  ;; switch the mode off in this buffer.
+  (when global-hl-line-mode
+    (setq hl-line-mode nil)
+    (setq-local global-hl-line-mode nil)
+    (global-hl-line-unhighlight))
   (if hl-line-mode
       (progn
         ;; In case `kill-all-local-variables' is called.
diff --git a/lisp/htmlfontify.el b/lisp/htmlfontify.el
index bf7446f151..b1fdbd2c4a 100644
--- a/lisp/htmlfontify.el
+++ b/lisp/htmlfontify.el
@@ -226,7 +226,6 @@ to make them safe."
   :tag   "html-quote-regex"
   :type  '(regexp))
 
-(define-obsolete-variable-alias 'hfy-post-html-hooks 'hfy-post-html-hook 
"24.3")
 (defcustom hfy-post-html-hook nil
   "List of functions to call after creating and filling the HTML buffer.
 These functions will be called with the HTML buffer as the current buffer."
diff --git a/lisp/icomplete.el b/lisp/icomplete.el
index b1fcf9ae71..19afdaa278 100644
--- a/lisp/icomplete.el
+++ b/lisp/icomplete.el
@@ -1,7 +1,6 @@
 ;;; icomplete.el --- minibuffer completion incremental feedback -*- 
lexical-binding: t -*-
 
-;; Copyright (C) 1992-1994, 1997, 1999, 2001-2022 Free Software
-;; Foundation, Inc.
+;; Copyright (C) 1992-2022 Free Software Foundation, Inc.
 
 ;; Author: Ken Manheimer <ken dot manheimer at gmail...>
 ;; Created: Mar 1993 Ken Manheimer, klm@nist.gov - first release to usenet
@@ -173,15 +172,13 @@ Used to implement the option 
`icomplete-show-matches-on-no-input'.")
   (let ((non-essential t)) ;E.g. don't prompt for password!
     (icomplete-exhibit)))
 
-(defvar icomplete-minibuffer-map
-  (let ((map (make-sparse-keymap)))
-    (define-key map [?\M-\t] #'icomplete-force-complete)
-    (define-key map [remap minibuffer-complete-and-exit] #'icomplete-ret)
-    (define-key map [?\C-j]  #'icomplete-force-complete-and-exit)
-    (define-key map [?\C-.]  #'icomplete-forward-completions)
-    (define-key map [?\C-,]  #'icomplete-backward-completions)
-    map)
-  "Keymap used by `icomplete-mode' in the minibuffer.")
+(defvar-keymap icomplete-minibuffer-map
+  :doc "Keymap used by `icomplete-mode' in the minibuffer."
+  "C-M-i" #'icomplete-force-complete
+  "C-j"   #'icomplete-force-complete-and-exit
+  "C-."   #'icomplete-forward-completions
+  "C-,"   #'icomplete-backward-completions
+  "<remap> <minibuffer-complete-and-exit>" #'icomplete-ret)
 
 (defun icomplete-ret ()
   "Exit minibuffer for icomplete."
@@ -393,22 +390,20 @@ if that doesn't produce a completion match."
              (delete-region (1+ (point)) (point-max)))))
         (t (call-interactively 'backward-delete-char))))
 
-(defvar icomplete-fido-mode-map
-  (let ((map (make-sparse-keymap)))
-    (define-key map (kbd "C-k")     #'icomplete-fido-kill)
-    (define-key map (kbd "C-d")     #'icomplete-fido-delete-char)
-    (define-key map (kbd "RET")     #'icomplete-fido-ret)
-    (define-key map (kbd "C-m")     #'icomplete-fido-ret)
-    (define-key map (kbd "DEL")     #'icomplete-fido-backward-updir)
-    (define-key map (kbd "M-j")     #'icomplete-fido-exit)
-    (define-key map (kbd "C-s")     #'icomplete-forward-completions)
-    (define-key map (kbd "C-r")     #'icomplete-backward-completions)
-    (define-key map (kbd "<right>") #'icomplete-forward-completions)
-    (define-key map (kbd "<left>")  #'icomplete-backward-completions)
-    (define-key map (kbd "C-.")     #'icomplete-forward-completions)
-    (define-key map (kbd "C-,")     #'icomplete-backward-completions)
-    map)
-  "Keymap used by `fido-mode' in the minibuffer.")
+(defvar-keymap icomplete-fido-mode-map
+  :doc "Keymap used by `fido-mode' in the minibuffer."
+  "C-k"     #'icomplete-fido-kill
+  "C-d"     #'icomplete-fido-delete-char
+  "RET"     #'icomplete-fido-ret
+  "C-m"     #'icomplete-fido-ret
+  "DEL"     #'icomplete-fido-backward-updir
+  "M-j"     #'icomplete-fido-exit
+  "C-s"     #'icomplete-forward-completions
+  "C-r"     #'icomplete-backward-completions
+  "<right>" #'icomplete-forward-completions
+  "<left>"  #'icomplete-backward-completions
+  "C-."     #'icomplete-forward-completions
+  "C-,"     #'icomplete-backward-completions)
 
 (defun icomplete--fido-mode-setup ()
   "Setup `fido-mode''s minibuffer."
@@ -634,16 +629,14 @@ Usually run by inclusion in `minibuffer-setup-hook'."
                  (completion--cache-all-sorted-completions beg end (cons comp 
all))))
        finally return all)))
 
-(defvar icomplete-vertical-mode-minibuffer-map
-  (let ((map (make-sparse-keymap)))
-    (define-key map (kbd "C-n")    #'icomplete-forward-completions)
-    (define-key map (kbd "C-p")    #'icomplete-backward-completions)
-    (define-key map (kbd "<down>") #'icomplete-forward-completions)
-    (define-key map (kbd "<up>")   #'icomplete-backward-completions)
-    (define-key map (kbd "M-<")    #'icomplete-vertical-goto-first)
-    (define-key map (kbd "M->")    #'icomplete-vertical-goto-last)
-    map)
-  "Keymap used by `icomplete-vertical-mode' in the minibuffer.")
+(defvar-keymap icomplete-vertical-mode-minibuffer-map
+  :doc "Keymap used by `icomplete-vertical-mode' in the minibuffer."
+  "C-n"    #'icomplete-forward-completions
+  "C-p"    #'icomplete-backward-completions
+  "<down>" #'icomplete-forward-completions
+  "<up>"   #'icomplete-backward-completions
+  "M-<"    #'icomplete-vertical-goto-first
+  "M->"    #'icomplete-vertical-goto-last)
 
 (defun icomplete--vertical-minibuffer-setup ()
   "Setup the minibuffer for vertical display of completion candidates."
diff --git a/lisp/ido.el b/lisp/ido.el
index 520513b1d2..1d0082da97 100644
--- a/lisp/ido.el
+++ b/lisp/ido.el
@@ -1507,15 +1507,18 @@ Removes badly formatted data and ignored directories."
   (add-hook 'minibuffer-setup-hook #'ido-minibuffer-setup)
   (add-hook 'choose-completion-string-functions 
#'ido-choose-completion-string))
 
+(defun ido--ffap-find-file (file)
+  (find-file file))
+
 (define-minor-mode ido-everywhere
   "Toggle use of Ido for all buffer/file reading."
   :global t
   (remove-function read-file-name-function #'ido-read-file-name)
   (remove-function read-buffer-function #'ido-read-buffer)
   (when (boundp 'ffap-file-finder)
-    (remove-function ffap-file-finder #'ido-find-file)
+    (remove-function ffap-file-finder #'ido--ffap-find-file)
     (when ido-mode
-      (add-function :override ffap-file-finder #'ido-find-file)))
+      (add-function :override ffap-file-finder #'ido--ffap-find-file)))
   (when ido-everywhere
     (if (not ido-mode)
         (ido-mode 'both)
diff --git a/lisp/ielm.el b/lisp/ielm.el
index 47c1792118..fd41afa243 100644
--- a/lisp/ielm.el
+++ b/lisp/ielm.el
@@ -472,6 +472,34 @@ nonempty, then flushes the buffer."
   ;; Set the process mark in the current buffer to POS.
   (set-marker (process-mark (get-buffer-process (current-buffer))) pos))
 
+;;; Input fontification
+
+(defcustom ielm-fontify-input-enable t
+  "Enable fontification of input in ielm buffers.
+This variable only has effect when creating an ielm buffer.  Use
+the command `comint-fontify-input-mode' to toggle fontification
+of input in an already existing ielm buffer."
+  :type 'boolean
+  :safe 'booleanp
+  :version "29.1")
+
+(defcustom ielm-indirect-setup-hook nil
+  "Hook run in an indirect buffer for input fontification.
+Input fontification and indentation of an IELM buffer, if
+enabled, is performed in an indirect buffer, whose indentation
+and syntax highlighting are set up with `emacs-lisp-mode'.  In
+addition to `comint-indirect-setup-hook', run this hook with the
+indirect buffer as the current buffer after its setup is done.
+This can be used to further customize fontification and other
+behavior of the indirect buffer."
+  :type 'boolean
+  :safe 'booleanp
+  :version "29.1")
+
+(defun ielm-indirect-setup-hook ()
+  "Run `ielm-indirect-setup-hook'."
+  (run-hooks 'ielm-indirect-setup-hook))
+
 ;;; Major mode
 
 (define-derived-mode inferior-emacs-lisp-mode comint-mode "IELM"
@@ -526,6 +554,10 @@ The behavior of IELM may be customized with the following 
variables:
 Customized bindings may be defined in `ielm-map', which currently contains:
 \\{ielm-map}"
   :syntax-table emacs-lisp-mode-syntax-table
+  :after-hook
+  (and (null comint-use-prompt-regexp)
+       ielm-fontify-input-enable
+       (comint-fontify-input-mode))
 
   (setq comint-prompt-regexp (concat "^" (regexp-quote ielm-prompt)))
   (setq-local paragraph-separate "\\'")
@@ -564,6 +596,10 @@ Customized bindings may be defined in `ielm-map', which 
currently contains:
   (setq-local font-lock-defaults
        '(ielm-font-lock-keywords nil nil ((?: . "w") (?- . "w") (?* . "w"))))
 
+  (add-hook 'comint-indirect-setup-hook
+            #'ielm-indirect-setup-hook 'append t)
+  (setq comint-indirect-setup-function #'emacs-lisp-mode)
+
   ;; A dummy process to keep comint happy. It will never get any input
   (unless (comint-check-proc (current-buffer))
     ;; Was cat, but on non-Unix platforms that might not exist, so
diff --git a/lisp/image-dired.el b/lisp/image-dired.el
deleted file mode 100644
index 9f12354111..0000000000
--- a/lisp/image-dired.el
+++ /dev/null
@@ -1,3080 +0,0 @@
-;;; image-dired.el --- use dired to browse and manipulate your images -*- 
lexical-binding: t -*-
-
-;; Copyright (C) 2005-2022 Free Software Foundation, Inc.
-
-;; Version: 0.4.11
-;; Keywords: multimedia
-;; Author: Mathias Dahl <mathias.rem0veth1s.dahl@gmail.com>
-
-;; This file is part of GNU Emacs.
-
-;; GNU Emacs is free software: you can redistribute it and/or modify
-;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation, either version 3 of the License, or
-;; (at your option) any later version.
-
-;; GNU Emacs is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-;; GNU General Public License for more details.
-
-;; You should have received a copy of the GNU General Public License
-;; along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.
-
-;;; Commentary:
-
-;; BACKGROUND
-;; ==========
-;;
-;;  I needed a program to browse, organize and tag my pictures.  I got
-;; tired of the old gallery program I used as it did not allow
-;; multi-file operations easily.  Also, it put things out of my
-;; control.  Image viewing programs I tested did not allow multi-file
-;; operations or did not do what I wanted it to.
-;;
-;;  So, I got the idea to use the wonderful functionality of Emacs and
-;; `dired' to do it.  It would allow me to do almost anything I wanted,
-;; which is basically just to browse all my pictures in an easy way,
-;; letting me manipulate and tag them in various ways.  `dired' already
-;; provide all the file handling and navigation facilities; I only
-;; needed to add some functions to display the images.
-;;
-;;  I briefly tried out thumbs.el, and although it seemed more
-;; powerful than this package, it did not work the way I wanted to.  It
-;; was too slow to create thumbnails of all files in a directory (I
-;; currently keep all my 2000+ images in the same directory) and
-;; browsing the thumbnail buffer was slow too.  image-dired.el will not
-;; create thumbnails until they are needed and the browsing is done
-;; quickly and easily in Dired.  I copied a great deal of ideas and
-;; code from there though... :)
-;;
-;;  `image-dired' stores the thumbnail files in `image-dired-dir'
-;; using the file name format ORIGNAME.thumb.ORIGEXT.  For example
-;; ~/.emacs.d/image-dired/myimage01.thumb.jpg.  The "database" is for
-;; now just a plain text file with the following format:
-;;
-;; file-name-non-directory;comment:comment-text;tag1;tag2;tag3;...;tagN
-;;
-;;
-;; PREREQUISITES
-;; =============
-;;
-;; * The GraphicsMagick or ImageMagick package; Image-Dired uses
-;;   whichever is available.
-;;
-;;   A) For GraphicsMagick, `gm' is used.
-;;      Find it here:  http://www.graphicsmagick.org/
-;;
-;;   B) For ImageMagick, `convert' and `mogrify' are used.
-;;      Find it here:  https://www.imagemagick.org.
-;;
-;; * For non-lossy rotation of JPEG images, the JpegTRAN program is
-;;   needed.
-;;
-;; * For `image-dired-set-exif-data' to work, the command line tool `exiftool' 
is
-;;   needed.  It can be found here: https://exiftool.org/.  This
-;;   function is, among other things, used for writing comments to
-;;   image files using `image-dired-thumbnail-set-image-description'.
-;;
-;;
-;; USAGE
-;; =====
-;;
-;; This information has been moved to the manual.  Type `C-h r' to open
-;; the Emacs manual and go to the node Thumbnails by typing `g
-;; Image-Dired RET'.
-;;
-;; Quickstart: M-x image-dired RET DIRNAME RET
-;;
-;; where DIRNAME is a directory containing image files.
-;;
-;; LIMITATIONS
-;; ===========
-;;
-;; * Supports all image formats that Emacs and convert supports, but
-;;   the thumbnails are hard-coded to JPEG or PNG format.  It uses
-;;   JPEG by default, but can optionally follow the Thumbnail Managing
-;;   Standard (v0.9.0, Dec 2020), which mandates PNG.  See the user
-;;   option `image-dired-thumbnail-storage'.
-;;
-;; * WARNING: The "database" format used might be changed so keep a
-;;   backup of `image-dired-db-file' when testing new versions.
-;;
-;; TODO
-;; ====
-;;
-;; * Investigate if it is possible to also write the tags to the image
-;;   files.
-;;
-;; * From thumbs.el: Add an option for clean-up/max-size functionality
-;;   for thumbnail directory.
-;;
-;; * From thumbs.el: Add setroot function.
-;;
-;; * Add `image-dired-display-thumbs-ring' and functions to cycle that.  Find 
out
-;;   which is best, saving old batch just before inserting new, or
-;;   saving the current batch in the ring when inserting it.  Adding
-;;   it probably needs rewriting `image-dired-display-thumbs' to be more 
general.
-;;
-;; * Find some way of toggling on and off really nice keybindings in
-;;   Dired (for example, using C-n or <down> instead of C-S-n).
-;;   Richard suggested that we could keep C-t as prefix for
-;;   image-dired commands as it is currently not used in Dired.  He
-;;   also suggested that `dired-next-line' and `dired-previous-line'
-;;   figure out if image-dired is enabled in the current buffer and,
-;;   if it is, call `image-dired-dired-next-line' and 
`image-dired-dired-previous-line',
-;;   respectively.  Update: This is partly done; some bindings have
-;;   now been added to Dired.
-;;
-;; * In some way keep track of buffers and windows and stuff so that
-;;   it works as the user expects.
-;;
-;; * More/better documentation.
-
-;;; Code:
-
-(require 'dired)
-(require 'exif)
-(require 'image-mode)
-(require 'widget)
-(require 'xdg)
-
-(eval-when-compile
-  (require 'cl-lib)
-  (require 'wid-edit))
-
-
-;;; Customizable variables
-
-(defgroup image-dired nil
-  "Use Dired to browse your images as thumbnails, and more."
-  :prefix "image-dired-"
-  :link '(info-link "(emacs) Image-Dired")
-  :group 'multimedia)
-
-(defcustom image-dired-dir (locate-user-emacs-file "image-dired/")
-  "Directory where thumbnail images are stored.
-
-The value of this option will be ignored if Image-Dired is
-customized to use the Thumbnail Managing Standard; they will be
-saved in \"$XDG_CACHE_HOME/thumbnails/\" instead.  See
-`image-dired-thumbnail-storage'."
-  :type 'directory)
-
-(defcustom image-dired-thumbnail-storage 'use-image-dired-dir
-  "How `image-dired' stores thumbnail files.
-There are two ways that Image-Dired can store and generate
-thumbnails.  If you set this variable to one of the two following
-values, they will be stored in the JPEG format:
-
-- `use-image-dired-dir' means that the thumbnails are stored in a
-  central directory.
-
-- `per-directory' means that each thumbnail is stored in a
-  subdirectory called \".image-dired\" in the same directory
-  where the image file is.
-
-It can also use the \"Thumbnail Managing Standard\", which allows
-sharing of thumbnails across different programs.  Thumbnails will
-be stored in \"$XDG_CACHE_HOME/thumbnails/\" instead of in
-`image-dired-dir'.  Thumbnails are saved in the PNG format, and
-can be one of the following sizes:
-
-- `standard' means use thumbnails sized 128x128.
-- `standard-large' means use thumbnails sized 256x256.
-- `standard-x-large' means use thumbnails sized 512x512.
-- `standard-xx-large' means use thumbnails sized 1024x1024.
-
-For more information on the Thumbnail Managing Standard, see:
-https://specifications.freedesktop.org/thumbnail-spec/thumbnail-spec-latest.html";
-  :type '(choice :tag "How to store thumbnail files"
-                 (const :tag "Use image-dired-dir" use-image-dired-dir)
-                 (const :tag "Thumbnail Managing Standard (normal 128x128)"
-                        standard)
-                 (const :tag "Thumbnail Managing Standard (large 256x256)"
-                        standard-large)
-                 (const :tag "Thumbnail Managing Standard (larger 512x512)"
-                        standard-x-large)
-                 (const :tag "Thumbnail Managing Standard (extra large 
1024x1024)"
-                        standard-xx-large)
-                 (const :tag "Per-directory" per-directory))
-  :version "29.1")
-
-(defconst image-dired--thumbnail-standard-sizes
-  '( standard standard-large
-     standard-x-large standard-xx-large)
-  "List of symbols representing thumbnail sizes in Thumbnail Managing 
Standard.")
-
-(defcustom image-dired-db-file
-  (expand-file-name ".image-dired_db" image-dired-dir)
-  "Database file where file names and their associated tags are stored."
-  :type 'file)
-
-(defcustom image-dired-cmd-create-thumbnail-program
-  (if (executable-find "gm") "gm" "convert")
-  "Executable used to create thumbnail.
-Used together with `image-dired-cmd-create-thumbnail-options'."
-  :type 'file
-  :version "29.1")
-
-(defcustom image-dired-cmd-create-thumbnail-options
-  (let ((opts '("-size" "%wx%h" "%f[0]"
-                "-resize" "%wx%h>"
-                "-strip" "jpeg:%t")))
-    (if (executable-find "gm") (cons "convert" opts) opts))
-  "Options of command used to create thumbnail image.
-Used with `image-dired-cmd-create-thumbnail-program'.
-Available format specifiers are: %w which is replaced by
-`image-dired-thumb-width', %h which is replaced by `image-dired-thumb-height',
-%f which is replaced by the file name of the original image and %t
-which is replaced by the file name of the thumbnail file."
-  :version "29.1"
-  :type '(repeat (string :tag "Argument")))
-
-(defcustom image-dired-cmd-pngnq-program
-  ;; Prefer pngquant to pngnq-s9 as it is faster on my machine.
-  ;;   The project also seems more active than the alternatives.
-  ;; Prefer pngnq-s9 to pngnq as it fixes bugs in pngnq.
-  ;; The pngnq project seems dead (?) since 2011 or so.
-  (or (executable-find "pngquant")
-      (executable-find "pngnq-s9")
-      (executable-find "pngnq"))
-  "The file name of the `pngquant' or `pngnq' program.
-It quantizes colors of PNG images down to 256 colors or fewer
-using the NeuQuant algorithm."
-  :version "29.1"
-  :type '(choice (const :tag "Not Set" nil) file))
-
-(defcustom image-dired-cmd-pngnq-options
-  (if (executable-find "pngquant")
-      '("--ext" "-nq8.png" "%t") ; same extension as "pngnq"
-    '("-f" "%t"))
-  "Arguments to pass `image-dired-cmd-pngnq-program'.
-Available format specifiers are the same as in
-`image-dired-cmd-create-thumbnail-options'."
-  :type '(repeat (string :tag "Argument"))
-  :version "29.1")
-
-(defcustom image-dired-cmd-pngcrush-program (executable-find "pngcrush")
-  "The file name of the `pngcrush' program.
-It optimizes the compression of PNG images.  Also it adds PNG textual chunks
-with the information required by the Thumbnail Managing Standard."
-  :type '(choice (const :tag "Not Set" nil) file))
-
-(defcustom image-dired-cmd-pngcrush-options
-  `("-q"
-    "-text" "b" "Description" "Thumbnail of file://%f"
-    "-text" "b" "Software" ,(emacs-version)
-    ;; "-text b \"Thumb::Image::Height\" \"%oh\" "
-    ;; "-text b \"Thumb::Image::Mimetype\" \"%mime\" "
-    ;; "-text b \"Thumb::Image::Width\" \"%ow\" "
-    "-text" "b" "Thumb::MTime" "%m"
-    ;; "-text b \"Thumb::Size\" \"%b\" "
-    "-text" "b" "Thumb::URI" "file://%f"
-    "%q" "%t")
-  "Arguments for `image-dired-cmd-pngcrush-program'.
-Available format specifiers are the same as in
-`image-dired-cmd-create-thumbnail-options', with %q for a
-temporary file name (typically generated by pnqnq)."
-  :version "26.1"
-  :type '(repeat (string :tag "Argument")))
-
-(defcustom image-dired-cmd-optipng-program (executable-find "optipng")
-  "The file name of the `optipng' program."
-  :version "26.1"
-  :type '(choice (const :tag "Not Set" nil) file))
-
-(defcustom image-dired-cmd-optipng-options '("-o5" "%t")
-  "Arguments passed to `image-dired-cmd-optipng-program'.
-Available format specifiers are described in
-`image-dired-cmd-create-thumbnail-options'."
-  :version "26.1"
-  :type '(repeat (string :tag "Argument"))
-  :link '(url-link "man:optipng(1)"))
-
-(defcustom image-dired-cmd-create-standard-thumbnail-options
-  (append '("-size" "%wx%h" "%f[0]")
-          (unless (or image-dired-cmd-pngcrush-program
-                      image-dired-cmd-pngnq-program)
-            (list
-             "-set" "Thumb::MTime" "%m"
-             "-set" "Thumb::URI" "file://%f"
-             "-set" "Description" "Thumbnail of file://%f"
-             "-set" "Software" (emacs-version)))
-          '("-thumbnail" "%wx%h>" "png:%t"))
-  "Options for creating thumbnails according to the Thumbnail Managing 
Standard.
-Available format specifiers are the same as in
-`image-dired-cmd-create-thumbnail-options', with %m for file modification 
time."
-  :version "26.1"
-  :type '(repeat (string :tag "Argument")))
-
-(defcustom image-dired-cmd-rotate-original-program
-  "jpegtran"
-  "Executable used to rotate original image.
-Used together with `image-dired-cmd-rotate-original-options'."
-  :type 'file)
-
-(defcustom image-dired-cmd-rotate-original-options
-  '("-rotate" "%d" "-copy" "all" "-outfile" "%t" "%o")
-  "Arguments of command used to rotate original image.
-Used with `image-dired-cmd-rotate-original-program'.
-Available format specifiers are: %d which is replaced by the
-number of (positive) degrees to rotate the image, normally 90 or
-270 \(for 90 degrees right and left), %o which is replaced by the
-original image file name and %t which is replaced by
-`image-dired-temp-image-file'."
-  :version "26.1"
-  :type '(repeat (string :tag "Argument")))
-
-(defcustom image-dired-temp-rotate-image-file
-  (expand-file-name ".image-dired_rotate_temp" image-dired-dir)
-  "Temporary file for rotate operations."
-  :type 'file)
-
-(defcustom image-dired-rotate-original-ask-before-overwrite t
-  "Confirm overwrite of original file after rotate operation.
-If non-nil, ask user for confirmation before overwriting the
-original file with `image-dired-temp-rotate-image-file'."
-  :type 'boolean)
-
-(defcustom image-dired-cmd-write-exif-data-program
-  "exiftool"
-  "Program used to write EXIF data to image.
-Used together with `image-dired-cmd-write-exif-data-options'."
-  :type 'file)
-
-(defcustom image-dired-cmd-write-exif-data-options
-  '("-%t=%v" "%f")
-  "Arguments of command used to write EXIF data.
-Used with `image-dired-cmd-write-exif-data-program'.
-Available format specifiers are: %f which is replaced by
-the image file name, %t which is replaced by the tag name and %v
-which is replaced by the tag value."
-  :version "26.1"
-  :type '(repeat (string :tag "Argument")))
-
-(defcustom image-dired-thumb-size
-  (cond
-   ((eq 'standard image-dired-thumbnail-storage) 128)
-   ((eq 'standard-large image-dired-thumbnail-storage) 256)
-   ((eq 'standard-x-large image-dired-thumbnail-storage) 512)
-   ((eq 'standard-xx-large image-dired-thumbnail-storage) 1024)
-   (t 100))
-  "Size of thumbnails, in pixels.
-This is the default size for both `image-dired-thumb-width'
-and `image-dired-thumb-height'.
-
-The value of this option will be ignored if Image-Dired is
-customized to use the Thumbnail Managing Standard; the standard
-sizes will be used instead.  See `image-dired-thumbnail-storage'."
-  :type 'integer)
-
-(defcustom image-dired-thumb-width image-dired-thumb-size
-  "Width of thumbnails, in pixels."
-  :type 'integer)
-
-(defcustom image-dired-thumb-height image-dired-thumb-size
-  "Height of thumbnails, in pixels."
-  :type 'integer)
-
-(defcustom image-dired-thumb-relief 2
-  "Size of button-like border around thumbnails."
-  :type 'integer)
-
-(defcustom image-dired-thumb-margin 2
-  "Size of the margin around thumbnails.
-This is where you see the cursor."
-  :type 'integer)
-
-(defcustom image-dired-thumb-visible-marks t
-  "Make marks and flags visible in thumbnail buffer.
-If non-nil, apply the `image-dired-thumb-mark' face to marked
-images and `image-dired-thumb-flagged' to images flagged for
-deletion."
-  :type 'boolean
-  :version "28.1")
-
-(defface image-dired-thumb-mark
-  '((((class color) (min-colors 16)) :background "DarkOrange")
-    (((class color)) :foreground "yellow"))
-  "Face for marked images in thumbnail buffer."
-  :version "29.1")
-
-(defface image-dired-thumb-flagged
-  '((((class color) (min-colors 88) (background light)) :background "Red3")
-    (((class color) (min-colors 88) (background dark))  :background "Pink")
-    (((class color) (min-colors 16) (background light)) :background "Red3")
-    (((class color) (min-colors 16) (background dark))  :background "Pink")
-    (((class color) (min-colors 8)) :background "red")
-    (t :inverse-video t))
-  "Face for images flagged for deletion in thumbnail buffer."
-  :version "29.1")
-
-(defcustom image-dired-line-up-method 'dynamic
-  "Default method for line-up of thumbnails in thumbnail buffer.
-Used by `image-dired-display-thumbs' and other functions that needs
-to line-up thumbnails.  Dynamic means to use the available width of
-the window containing the thumbnail buffer, Fixed means to use
-`image-dired-thumbs-per-row', Interactive is for asking the user,
-and No line-up means that no automatic line-up will be done."
-  :type '(choice :tag "Default line-up method"
-                 (const :tag "Dynamic" dynamic)
-                (const :tag "Fixed" fixed)
-                (const :tag "Interactive" interactive)
-                 (const :tag "No line-up" none)))
-
-(defcustom image-dired-thumbs-per-row 3
-  "Number of thumbnails to display per row in thumb buffer."
-  :type 'integer)
-
-(defcustom image-dired-track-movement t
-  "The current state of the tracking and mirroring.
-For more information, see the documentation for
-`image-dired-toggle-movement-tracking'."
-  :type 'boolean)
-
-(defcustom image-dired-append-when-browsing nil
-  "Append thumbnails in thumbnail buffer when browsing.
-If non-nil, using `image-dired-next-line-and-display' and
-`image-dired-previous-line-and-display' will leave a trail of thumbnail
-images in the thumbnail buffer.  If you enable this and want to clean
-the thumbnail buffer because it is filled with too many thumbnails,
-just call `image-dired-display-thumb' to display only the image at point.
-This value can be toggled using `image-dired-toggle-append-browsing'."
-  :type 'boolean)
-
-(defcustom image-dired-dired-disp-props t
-  "If non-nil, display properties for Dired file when browsing.
-Used by `image-dired-next-line-and-display',
-`image-dired-previous-line-and-display' and 
`image-dired-mark-and-display-next'.
-If the database file is large, this can slow down image browsing in
-Dired and you might want to turn it off."
-  :type 'boolean)
-
-(defcustom image-dired-display-properties-format "%b: %f (%t): %c"
-  "Display format for thumbnail properties.
-%b is replaced with associated Dired buffer name, %f with file
-name (without path) of original image file, %t with the list of
-tags and %c with the comment."
-  :type 'string)
-
-(defcustom image-dired-external-viewer
-  ;; TODO: Use mailcap, dired-guess-shell-alist-default,
-  ;; dired-view-command-alist.
-  (cond ((executable-find "display"))
-        ((executable-find "xli"))
-        ((executable-find "qiv") "qiv -t")
-        ((executable-find "feh") "feh"))
-  "Name of external viewer.
-Including parameters.  Used when displaying original image from
-`image-dired-thumbnail-mode'."
-  :version "28.1"
-  :type '(choice string
-                 (const :tag "Not Set" nil)))
-
-(defcustom image-dired-main-image-directory
-  (or (xdg-user-dir "PICTURES") "~/pics/")
-  "Name of main image directory, if any.
-Used by `image-dired-copy-with-exif-file-name'."
-  :type 'string
-  :version "29.1")
-
-(defcustom image-dired-show-all-from-dir-max-files 500
-  "Maximum number of files in directory before prompting.
-
-If there are more image files than this in a selected directory,
-the `image-dired-show-all-from-dir' command will ask for
-confirmation before creating the thumbnail buffer.  If this
-variable is nil, it will never ask."
-  :type '(choice integer
-                 (const :tag "Disable warning" nil))
-  :version "29.1")
-
-(defcustom image-dired-marking-shows-next t
-  "If non-nil, marking, unmarking or flagging an image shows the next image.
-
-This affects the following commands:
-\\<image-dired-thumbnail-mode-map>
-    `image-dired-flag-thumb-original-file'   (bound to 
\\[image-dired-flag-thumb-original-file])
-    `image-dired-mark-thumb-original-file'   (bound to 
\\[image-dired-mark-thumb-original-file])
-    `image-dired-unmark-thumb-original-file' (bound to 
\\[image-dired-unmark-thumb-original-file])"
-  :type 'boolean
-  :version "29.1")
-
-
-;;; Util functions
-
-(defvar image-dired-debug nil
-  "Non-nil means enable debug messages.")
-
-(defun image-dired-debug-message (&rest args)
-  "Display debug message ARGS when `image-dired-debug' is non-nil."
-  (when image-dired-debug
-    (apply #'message args)))
-
-(defmacro image-dired--with-db-file (&rest body)
-  "Run BODY in a temp buffer containing `image-dired-db-file'.
-Return the last form in BODY."
-  (declare (indent 0) (debug t))
-  `(with-temp-buffer
-     (if (file-exists-p image-dired-db-file)
-        (insert-file-contents image-dired-db-file))
-     ,@body))
-
-(defun image-dired-dir ()
-  "Return the current thumbnail directory (from variable `image-dired-dir').
-Create the thumbnail directory if it does not exist."
-  (let ((image-dired-dir (file-name-as-directory
-               (expand-file-name image-dired-dir))))
-    (unless (file-directory-p image-dired-dir)
-      (with-file-modes #o700
-        (make-directory image-dired-dir t))
-      (message "Thumbnail directory created: %s" image-dired-dir))
-    image-dired-dir))
-
-(defun image-dired-insert-image (file type relief margin)
-  "Insert image FILE of image TYPE, using RELIEF and MARGIN, at point."
-  (let ((i `(image :type ,type
-                   :file ,file
-                   :relief ,relief
-                   :margin ,margin)))
-    (insert-image i)))
-
-(defun image-dired-get-thumbnail-image (file)
-  "Return the image descriptor for a thumbnail of image file FILE."
-  (unless (string-match-p (image-file-name-regexp) file)
-    (error "%s is not a valid image file" file))
-  (let* ((thumb-file (image-dired-thumb-name file))
-        (thumb-attr (file-attributes thumb-file)))
-    (when (or (not thumb-attr)
-             (time-less-p (file-attribute-modification-time thumb-attr)
-                          (file-attribute-modification-time
-                           (file-attributes file))))
-      (image-dired-create-thumb file thumb-file))
-    (create-image thumb-file)))
-
-(defun image-dired-insert-thumbnail (file original-file-name
-                                     associated-dired-buffer)
-  "Insert thumbnail image FILE.
-Add text properties ORIGINAL-FILE-NAME and ASSOCIATED-DIRED-BUFFER."
-  (let (beg end)
-    (setq beg (point))
-    (image-dired-insert-image
-     file
-     ;; Thumbnails are created asynchronously, so we might not yet
-     ;; have a file.  But if it exists, it might have been cached from
-     ;; before and we should use it instead of our current settings.
-     (or (and (file-exists-p file)
-              (image-type-from-file-header file))
-         (and (memq image-dired-thumbnail-storage
-                    image-dired--thumbnail-standard-sizes)
-              'png)
-         'jpeg)
-     image-dired-thumb-relief
-     image-dired-thumb-margin)
-    (setq end (point))
-    (add-text-properties
-     beg end
-     (list 'image-dired-thumbnail t
-           'original-file-name original-file-name
-           'associated-dired-buffer associated-dired-buffer
-           'tags (image-dired-list-tags original-file-name)
-           'mouse-face 'highlight
-           'comment (image-dired-get-comment original-file-name)))))
-
-(defun image-dired-thumb-name (file)
-  "Return absolute file name for thumbnail FILE.
-Depending on the value of `image-dired-thumbnail-storage', the
-file name of the thumbnail will vary:
-- For `use-image-dired-dir', make a SHA1-hash of the image file's
-  directory name and add that to make the thumbnail file name
-  unique.
-- For `per-directory' storage, just add a subdirectory.
-- For `standard' storage, produce the file name according to the
-  Thumbnail Managing Standard.  Among other things, an MD5-hash
-  of the image file's directory name will be added to the
-  filename.
-See also `image-dired-thumbnail-storage'."
-  (cond ((memq image-dired-thumbnail-storage
-               image-dired--thumbnail-standard-sizes)
-         (let ((thumbdir (cl-case image-dired-thumbnail-storage
-                           (standard "thumbnails/normal")
-                           (standard-large "thumbnails/large")
-                           (standard-x-large "thumbnails/x-large")
-                           (standard-xx-large "thumbnails/xx-large"))))
-           (expand-file-name
-            ;; MD5 is mandated by the Thumbnail Managing Standard.
-            (concat (md5 (concat "file://" (expand-file-name file))) ".png")
-            (expand-file-name thumbdir (xdg-cache-home)))))
-        ((eq 'use-image-dired-dir image-dired-thumbnail-storage)
-         (let* ((f (expand-file-name file))
-                (hash
-                 (md5 (file-name-as-directory (file-name-directory f)))))
-           (format "%s%s%s.thumb.%s"
-                   (file-name-as-directory (expand-file-name 
(image-dired-dir)))
-                   (file-name-base f)
-                   (if hash (concat "_" hash) "")
-                   (file-name-extension f))))
-        ((eq 'per-directory image-dired-thumbnail-storage)
-         (let ((f (expand-file-name file)))
-           (format "%s.image-dired/%s.thumb.%s"
-                   (file-name-directory f)
-                   (file-name-base f)
-                   (file-name-extension f))))))
-
-(defun image-dired--check-executable-exists (executable)
-  (unless (executable-find (symbol-value executable))
-    (error "Executable %S not found" executable)))
-
-
-;;; Creating thumbnails
-
-(defun image-dired-thumb-size (dimension)
-  "Return thumb size depending on `image-dired-thumbnail-storage'.
-DIMENSION should be either the symbol `width' or `height'."
-  (cond
-   ((eq 'standard image-dired-thumbnail-storage) 128)
-   ((eq 'standard-large image-dired-thumbnail-storage) 256)
-   ((eq 'standard-x-large image-dired-thumbnail-storage) 512)
-   ((eq 'standard-xx-large image-dired-thumbnail-storage) 1024)
-   (t (cl-ecase dimension
-        (width image-dired-thumb-width)
-        (height image-dired-thumb-height)))))
-
-(defvar image-dired--generate-thumbs-start nil
-  "Time when `display-thumbs' was called.")
-
-(defvar image-dired-queue nil
-  "List of items in the queue.
-Each item has the form (ORIGINAL-FILE TARGET-FILE).")
-
-(defvar image-dired-queue-active-jobs 0
-  "Number of active jobs in `image-dired-queue'.")
-
-(defvar image-dired-queue-active-limit (min 4 (max 2 (/ (num-processors) 2)))
-  "Maximum number of concurrent jobs permitted for generating images.
-Increase at own risk.  If you want to experiment with this,
-consider setting `image-dired-debug' to a non-nil value to see
-the time spent on generating thumbnails.  Run `image-clear-cache'
-and remove the cached thumbnail files between each trial run.")
-
-(defun image-dired-pngnq-thumb (spec)
-  "Quantize thumbnail described by format SPEC with pngnq(1)."
-  (let ((process
-         (apply #'start-process "image-dired-pngnq" nil
-                image-dired-cmd-pngnq-program
-                (mapcar (lambda (arg) (format-spec arg spec))
-                        image-dired-cmd-pngnq-options))))
-    (setf (process-sentinel process)
-          (lambda (process status)
-            (if (and (eq (process-status process) 'exit)
-                     (zerop (process-exit-status process)))
-                ;; Pass off to pngcrush, or just rename the
-                ;; THUMB-nq8.png file back to THUMB.png
-                (if (and image-dired-cmd-pngcrush-program
-                         (executable-find image-dired-cmd-pngcrush-program))
-                    (image-dired-pngcrush-thumb spec)
-                  (let ((nq8 (cdr (assq ?q spec)))
-                        (thumb (cdr (assq ?t spec))))
-                    (rename-file nq8 thumb t)))
-              (message "command %S %s" (process-command process)
-                       (string-replace "\n" "" status)))))
-    process))
-
-(defun image-dired-pngcrush-thumb (spec)
-  "Optimize thumbnail described by format SPEC with pngcrush(1)."
-  ;; If pngnq wasn't run, then the THUMB-nq8.png file does not exist.
-  ;; pngcrush needs an infile and outfile, so we just copy THUMB to
-  ;; THUMB-nq8.png and use the latter as a temp file.
-  (when (not image-dired-cmd-pngnq-program)
-    (let ((temp (cdr (assq ?q spec)))
-          (thumb (cdr (assq ?t spec))))
-      (copy-file thumb temp)))
-  (let ((process
-         (apply #'start-process "image-dired-pngcrush" nil
-                image-dired-cmd-pngcrush-program
-                (mapcar (lambda (arg) (format-spec arg spec))
-                        image-dired-cmd-pngcrush-options))))
-    (setf (process-sentinel process)
-          (lambda (process status)
-            (unless (and (eq (process-status process) 'exit)
-                         (zerop (process-exit-status process)))
-              (message "command %S %s" (process-command process)
-                       (string-replace "\n" "" status)))
-            (when (memq (process-status process) '(exit signal))
-              (let ((temp (cdr (assq ?q spec))))
-                (delete-file temp)))))
-    process))
-
-(defun image-dired-optipng-thumb (spec)
-  "Optimize thumbnail described by format SPEC with optipng(1)."
-  (let ((process
-         (apply #'start-process "image-dired-optipng" nil
-                image-dired-cmd-optipng-program
-                (mapcar (lambda (arg) (format-spec arg spec))
-                        image-dired-cmd-optipng-options))))
-    (setf (process-sentinel process)
-          (lambda (process status)
-            (unless (and (eq (process-status process) 'exit)
-                         (zerop (process-exit-status process)))
-              (message "command %S %s" (process-command process)
-                       (string-replace "\n" "" status)))))
-    process))
-
-(defun image-dired-create-thumb-1 (original-file thumbnail-file)
-  "For ORIGINAL-FILE, create thumbnail image named THUMBNAIL-FILE."
-  (image-dired--check-executable-exists
-   'image-dired-cmd-create-thumbnail-program)
-  (let* ((width (int-to-string (image-dired-thumb-size 'width)))
-         (height (int-to-string (image-dired-thumb-size 'height)))
-        (modif-time (format-time-string
-                     "%s" (file-attribute-modification-time
-                           (file-attributes original-file))))
-         (thumbnail-nq8-file (replace-regexp-in-string ".png\\'" "-nq8.png"
-                                                       thumbnail-file))
-         (spec
-          (list
-           (cons ?w width)
-           (cons ?h height)
-           (cons ?m modif-time)
-           (cons ?f original-file)
-           (cons ?q thumbnail-nq8-file)
-           (cons ?t thumbnail-file)))
-         (thumbnail-dir (file-name-directory thumbnail-file))
-         process)
-    (when (not (file-exists-p thumbnail-dir))
-      (with-file-modes #o700
-        (make-directory thumbnail-dir t))
-      (message "Thumbnail directory created: %s" thumbnail-dir))
-
-    ;; Thumbnail file creation processes begin here and are marshaled
-    ;; in a queue by `image-dired-create-thumb'.
-    (setq process
-          (apply #'start-process "image-dired-create-thumbnail" nil
-                 image-dired-cmd-create-thumbnail-program
-                 (mapcar
-                  (lambda (arg) (format-spec arg spec))
-                  (if (memq image-dired-thumbnail-storage
-                            image-dired--thumbnail-standard-sizes)
-                      image-dired-cmd-create-standard-thumbnail-options
-                    image-dired-cmd-create-thumbnail-options))))
-
-    (setf (process-sentinel process)
-          (lambda (process status)
-            ;; Trigger next in queue once a thumbnail has been created
-            (cl-decf image-dired-queue-active-jobs)
-            (image-dired-thumb-queue-run)
-            (when (= image-dired-queue-active-jobs 0)
-              (image-dired-debug-message
-               (format-time-string
-                "Generated thumbnails in %s.%3N seconds"
-               (time-subtract nil
-                               image-dired--generate-thumbs-start))))
-            (if (not (and (eq (process-status process) 'exit)
-                          (zerop (process-exit-status process))))
-                (message "Thumb could not be created for %s: %s"
-                         (abbreviate-file-name original-file)
-                         (string-replace "\n" "" status))
-              (set-file-modes thumbnail-file #o600)
-              (clear-image-cache thumbnail-file)
-              ;; PNG thumbnail has been created since we are
-              ;; following the XDG thumbnail spec, so try to optimize
-              (when (memq image-dired-thumbnail-storage
-                          image-dired--thumbnail-standard-sizes)
-                (cond
-                 ((and image-dired-cmd-pngnq-program
-                       (executable-find image-dired-cmd-pngnq-program))
-                  (image-dired-pngnq-thumb spec))
-                 ((and image-dired-cmd-pngcrush-program
-                       (executable-find image-dired-cmd-pngcrush-program))
-                  (image-dired-pngcrush-thumb spec))
-                 ((and image-dired-cmd-optipng-program
-                       (executable-find image-dired-cmd-optipng-program))
-                  (image-dired-optipng-thumb spec)))))))
-    process))
-
-(defun image-dired-thumb-queue-run ()
-  "Run a queued job if one exists and not too many jobs are running.
-Queued items live in `image-dired-queue'."
-  (while (and image-dired-queue
-              (< image-dired-queue-active-jobs
-                 image-dired-queue-active-limit))
-    (cl-incf image-dired-queue-active-jobs)
-    (apply #'image-dired-create-thumb-1 (pop image-dired-queue))))
-
-(defun image-dired-create-thumb (original-file thumbnail-file)
-  "Add a job for generating ORIGINAL-FILE thumbnail to `image-dired-queue'.
-The new file will be named THUMBNAIL-FILE."
-  (setq image-dired-queue
-        (nconc image-dired-queue
-               (list (list original-file thumbnail-file))))
-  (run-at-time 0 nil #'image-dired-thumb-queue-run))
-
-(defmacro image-dired--with-marked (&rest body)
-  "Eval BODY with point on each marked thumbnail.
-If no marked file could be found, execute BODY on the current
-thumbnail."
-  `(with-current-buffer image-dired-thumbnail-buffer
-     (let (found)
-       (save-mark-and-excursion
-         (goto-char (point-min))
-         (while (not (eobp))
-           (when (image-dired-thumb-file-marked-p)
-             (setq found t)
-             ,@body)
-           (forward-char)))
-       (unless found
-         ,@body))))
-
-;;;###autoload
-(defun image-dired-dired-toggle-marked-thumbs (&optional arg)
-  "Toggle thumbnails in front of file names in the Dired buffer.
-If no marked file could be found, insert or hide thumbnails on the
-current line.  ARG, if non-nil, specifies the files to use instead
-of the marked files.  If ARG is an integer, use the next ARG (or
-previous -ARG, if ARG<0) files."
-  (interactive "P")
-  (dired-map-over-marks
-   (let ((image-pos  (dired-move-to-filename))
-         (image-file (dired-get-filename nil t))
-         thumb-file
-         overlay)
-     (when (and image-file
-                (string-match-p (image-file-name-regexp) image-file))
-       (setq thumb-file (image-dired-get-thumbnail-image image-file))
-       ;; If image is not already added, then add it.
-       (let ((thumb-ov (cl-loop for ov in (overlays-in (point) (1+ (point)))
-                                if (overlay-get ov 'thumb-file) return ov)))
-         (if thumb-ov
-             (delete-overlay thumb-ov)
-          (put-image thumb-file image-pos)
-          (setq overlay
-                 (cl-loop for ov in (overlays-in (point) (1+ (point)))
-                          if (overlay-get ov 'put-image) return ov))
-          (overlay-put overlay 'image-file image-file)
-          (overlay-put overlay 'thumb-file thumb-file)))))
-   arg             ; Show or hide image on ARG next files.
-   'show-progress) ; Update dired display after each image is updated.
-  (add-hook 'dired-after-readin-hook
-            'image-dired-dired-after-readin-hook nil t))
-
-(defun image-dired-dired-after-readin-hook ()
-  "Relocate existing thumbnail overlays in Dired buffer after reverting.
-Move them to their corresponding files if they still exist.
-Otherwise, delete overlays."
-  (mapc (lambda (overlay)
-          (when (overlay-get overlay 'put-image)
-            (let* ((image-file (overlay-get overlay 'image-file))
-                   (image-pos (dired-goto-file image-file)))
-              (if image-pos
-                  (move-overlay overlay image-pos image-pos)
-                (delete-overlay overlay)))))
-        (overlays-in (point-min) (point-max))))
-
-(defun image-dired-next-line-and-display ()
-  "Move to next Dired line and display thumbnail image."
-  (interactive)
-  (dired-next-line 1)
-  (image-dired-display-thumbs
-   t (or image-dired-append-when-browsing nil) t)
-  (if image-dired-dired-disp-props
-      (image-dired-dired-display-properties)))
-
-(defun image-dired-previous-line-and-display ()
-  "Move to previous Dired line and display thumbnail image."
-  (interactive)
-  (dired-previous-line 1)
-  (image-dired-display-thumbs
-   t (or image-dired-append-when-browsing nil) t)
-  (if image-dired-dired-disp-props
-      (image-dired-dired-display-properties)))
-
-(defun image-dired-toggle-append-browsing ()
-  "Toggle `image-dired-append-when-browsing'."
-  (interactive)
-  (setq image-dired-append-when-browsing
-        (not image-dired-append-when-browsing))
-  (message "Append browsing %s"
-           (if image-dired-append-when-browsing
-               "on"
-             "off")))
-
-(defun image-dired-mark-and-display-next ()
-  "Mark current file in Dired and display next thumbnail image."
-  (interactive)
-  (dired-mark 1)
-  (image-dired-display-thumbs
-   t (or image-dired-append-when-browsing nil) t)
-  (if image-dired-dired-disp-props
-      (image-dired-dired-display-properties)))
-
-(defun image-dired-toggle-dired-display-properties ()
-  "Toggle `image-dired-dired-disp-props'."
-  (interactive)
-  (setq image-dired-dired-disp-props
-        (not image-dired-dired-disp-props))
-  (message "Dired display properties %s"
-           (if image-dired-dired-disp-props
-               "on"
-             "off")))
-
-(defvar image-dired-thumbnail-buffer "*image-dired*"
-  "Image-Dired's thumbnail buffer.")
-
-(defun image-dired-create-thumbnail-buffer ()
-  "Create thumb buffer and set `image-dired-thumbnail-mode'."
-  (let ((buf (get-buffer-create image-dired-thumbnail-buffer)))
-    (with-current-buffer buf
-      (setq buffer-read-only t)
-      (if (not (eq major-mode 'image-dired-thumbnail-mode))
-          (image-dired-thumbnail-mode)))
-    buf))
-
-(defvar image-dired-display-image-buffer "*image-dired-display-image*"
-  "Where larger versions of the images are display.")
-
-(defvar image-dired-saved-window-configuration nil
-  "Saved window configuration.")
-
-;;;###autoload
-(defun image-dired-dired-with-window-configuration (dir &optional arg)
-  "Open directory DIR and create a default window configuration.
-
-Convenience command that:
-
- - Opens Dired in folder DIR
- - Splits windows in most useful (?) way
- - Sets `truncate-lines' to t
-
-After the command has finished, you would typically mark some
-image files in Dired and type
-\\[image-dired-display-thumbs] (`image-dired-display-thumbs').
-
-If called with prefix argument ARG, skip splitting of windows.
-
-The current window configuration is saved and can be restored by
-calling `image-dired-restore-window-configuration'."
-  (interactive "DDirectory: \nP")
-  (let ((buf (image-dired-create-thumbnail-buffer))
-        (buf2 (get-buffer-create image-dired-display-image-buffer)))
-    (setq image-dired-saved-window-configuration
-          (current-window-configuration))
-    (dired dir)
-    (delete-other-windows)
-    (when (not arg)
-      (split-window-right)
-      (setq truncate-lines t)
-      (save-excursion
-        (other-window 1)
-        (pop-to-buffer-same-window buf)
-        (select-window (split-window-below))
-        (pop-to-buffer-same-window buf2)
-        (other-window -2)))))
-
-(defun image-dired-restore-window-configuration ()
-  "Restore window configuration.
-Restore any changes to the window configuration made by calling
-`image-dired-dired-with-window-configuration'."
-  (interactive nil image-dired-thumbnail-mode)
-  (if image-dired-saved-window-configuration
-      (set-window-configuration image-dired-saved-window-configuration)
-    (message "No saved window configuration")))
-
-(defun image-dired--line-up-with-method ()
-  "Line up thumbnails according to `image-dired-line-up-method'."
-  (cond ((eq 'dynamic image-dired-line-up-method)
-         (image-dired-line-up-dynamic))
-        ((eq 'fixed image-dired-line-up-method)
-         (image-dired-line-up))
-        ((eq 'interactive image-dired-line-up-method)
-         (image-dired-line-up-interactive))
-        ((eq 'none image-dired-line-up-method)
-         nil)
-        (t
-         (image-dired-line-up-dynamic))))
-
-;;;###autoload
-(defun image-dired-display-thumbs (&optional arg append do-not-pop)
-  "Display thumbnails of all marked files, in `image-dired-thumbnail-buffer'.
-If a thumbnail image does not exist for a file, it is created on the
-fly.  With prefix argument ARG, display only thumbnail for file at
-point (this is useful if you have marked some files but want to show
-another one).
-
-Recommended usage is to split the current frame horizontally so that
-you have the Dired buffer in the left window and the
-`image-dired-thumbnail-buffer' buffer in the right window.
-
-With optional argument APPEND, append thumbnail to thumbnail buffer
-instead of erasing it first.
-
-Optional argument DO-NOT-POP controls if `pop-to-buffer' should be
-used or not.  If non-nil, use `display-buffer' instead of
-`pop-to-buffer'.  This is used from functions like
-`image-dired-next-line-and-display' and
-`image-dired-previous-line-and-display' where we do not want the
-thumbnail buffer to be selected."
-  (interactive "P")
-  (setq image-dired--generate-thumbs-start  (current-time))
-  (let ((buf (image-dired-create-thumbnail-buffer))
-        thumb-name files dired-buf)
-    (if arg
-        (setq files (list (dired-get-filename)))
-      (setq files (dired-get-marked-files)))
-    (setq dired-buf (current-buffer))
-    (with-current-buffer buf
-      (let ((inhibit-read-only t))
-        (if (not append)
-            (erase-buffer)
-          (goto-char (point-max)))
-        (dolist (curr-file files)
-          (setq thumb-name (image-dired-thumb-name curr-file))
-          (when (not (file-exists-p thumb-name))
-            (image-dired-create-thumb curr-file thumb-name))
-          (image-dired-insert-thumbnail thumb-name curr-file dired-buf)))
-      (if do-not-pop
-          (display-buffer buf)
-        (pop-to-buffer buf))
-      (image-dired--line-up-with-method))))
-
-;;;###autoload
-(defun image-dired-show-all-from-dir (dir)
-  "Make a thumbnail buffer for all images in DIR and display it.
-Any file matching `image-file-name-regexp' is considered an image
-file.
-
-If the number of image files in DIR exceeds
-`image-dired-show-all-from-dir-max-files', ask for confirmation
-before creating the thumbnail buffer.  If that variable is nil,
-never ask for confirmation."
-  (interactive "DImage-Dired: ")
-  (dired dir)
-  (dired-mark-files-regexp (image-file-name-regexp))
-  (let ((files (dired-get-marked-files nil nil nil t)))
-    (cond ((and (null (cdr files)))
-           (message "No image files in directory"))
-          ((or (not image-dired-show-all-from-dir-max-files)
-               (<= (length (cdr files)) 
image-dired-show-all-from-dir-max-files)
-               (and (> (length (cdr files)) 
image-dired-show-all-from-dir-max-files)
-                    (y-or-n-p
-                     (format
-                      "Directory contains more than %d image files.  Proceed?"
-                      image-dired-show-all-from-dir-max-files))))
-           (image-dired-display-thumbs)
-           (pop-to-buffer image-dired-thumbnail-buffer)
-           (setq default-directory dir)
-           (image-dired-unmark-all-marks))
-          (t (message "Image-Dired canceled")))))
-
-;;;###autoload
-(defalias 'image-dired 'image-dired-show-all-from-dir)
-
-
-;;; Tags
-
-(defun image-dired-sane-db-file ()
-  "Check if `image-dired-db-file' exists.
-If not, try to create it (including any parent directories).
-Signal error if there are problems creating it."
-  (or (file-exists-p image-dired-db-file)
-      (let (dir buf)
-        (unless (file-directory-p (setq dir (file-name-directory
-                                             image-dired-db-file)))
-          (with-file-modes #o700
-            (make-directory dir t)))
-        (with-current-buffer (setq buf (create-file-buffer
-                                        image-dired-db-file))
-          (with-file-modes #o600
-            (write-file image-dired-db-file)))
-        (kill-buffer buf)
-        (file-exists-p image-dired-db-file))
-      (error "Could not create %s" image-dired-db-file)))
-
-(defvar image-dired-tag-history nil "Variable holding the tag history.")
-
-(defun image-dired-write-tags (file-tags)
-  "Write file tags to database.
-Write each file and tag in FILE-TAGS to the database.
-FILE-TAGS is an alist in the following form:
- ((FILE . TAG) ... )"
-  (image-dired-sane-db-file)
-  (let (end file tag)
-    (image-dired--with-db-file
-     (setq buffer-file-name image-dired-db-file)
-     (dolist (elt file-tags)
-       (setq file (car elt)
-            tag (cdr elt))
-       (goto-char (point-min))
-       (if (search-forward-regexp (format "^%s.*$" file) nil t)
-          (progn
-            (setq end (point))
-            (beginning-of-line)
-            (when (not (search-forward (format ";%s" tag) end t))
-              (end-of-line)
-              (insert (format ";%s" tag))))
-        (goto-char (point-max))
-        (insert (format "%s;%s\n" file tag))))
-     (save-buffer))))
-
-(defun image-dired-remove-tag (files tag)
-  "For all FILES, remove TAG from the image database."
-  (image-dired-sane-db-file)
-  (image-dired--with-db-file
-   (setq buffer-file-name image-dired-db-file)
-   (let (end)
-     (unless (listp files)
-       (if (stringp files)
-          (setq files (list files))
-        (error "Files must be a string or a list of strings!")))
-     (dolist (file files)
-       (goto-char (point-min))
-       (when (search-forward-regexp (format "^%s;" file) nil t)
-        (end-of-line)
-        (setq end (point))
-        (beginning-of-line)
-        (when (search-forward-regexp
-                (format "\\(;%s\\)\\($\\|;\\)" tag) end t)
-          (delete-region (match-beginning 1) (match-end 1))
-          ;; Check if file should still be in the database. If
-          ;; it has no tags or comments, it will be removed.
-          (end-of-line)
-          (setq end (point))
-          (beginning-of-line)
-          (when (not (search-forward ";" end t))
-            (kill-line 1))))))
-   (save-buffer)))
-
-(defun image-dired-list-tags (file)
-  "Read all tags for image FILE from the image database."
-  (image-dired-sane-db-file)
-  (image-dired--with-db-file
-   (let (end (tags ""))
-     (when (search-forward-regexp (format "^%s" file) nil t)
-       (end-of-line)
-       (setq end (point))
-       (beginning-of-line)
-       (if (search-forward ";" end t)
-          (if (search-forward "comment:" end t)
-              (if (search-forward ";" end t)
-                  (setq tags (buffer-substring (point) end)))
-            (setq tags (buffer-substring (point) end)))))
-     (split-string tags ";"))))
-
-;;;###autoload
-(defun image-dired-tag-files (arg)
-  "Tag marked file(s) in Dired.  With prefix ARG, tag file at point."
-  (interactive "P")
-  (let ((tag (completing-read
-              "Tags to add (separate tags with a semicolon): "
-              image-dired-tag-history nil nil nil 'image-dired-tag-history))
-        files)
-    (if arg
-        (setq files (list (dired-get-filename)))
-      (setq files (dired-get-marked-files)))
-    (image-dired-write-tags
-     (mapcar
-      (lambda (x)
-        (cons x tag))
-      files))))
-
-(defun image-dired-tag-thumbnail ()
-  "Tag current or marked thumbnails."
-  (interactive)
-  (let ((tag (completing-read
-              "Tags to add (separate tags with a semicolon): "
-              image-dired-tag-history nil nil nil 'image-dired-tag-history)))
-    (image-dired--with-marked
-     (image-dired-write-tags
-      (list (cons (image-dired-original-file-name) tag)))
-     (image-dired-update-property
-      'tags (image-dired-list-tags (image-dired-original-file-name))))))
-
-;;;###autoload
-(defun image-dired-delete-tag (arg)
-  "Remove tag for selected file(s).
-With prefix argument ARG, remove tag from file at point."
-  (interactive "P")
-  (let ((tag (completing-read "Tag to remove: " image-dired-tag-history
-                              nil nil nil 'image-dired-tag-history))
-        files)
-    (if arg
-        (setq files (list (dired-get-filename)))
-      (setq files (dired-get-marked-files)))
-    (image-dired-remove-tag files tag)))
-
-(defun image-dired-tag-thumbnail-remove ()
-  "Remove tag from current or marked thumbnails."
-  (interactive)
-  (let ((tag (completing-read "Tag to remove: " image-dired-tag-history
-                              nil nil nil 'image-dired-tag-history)))
-    (image-dired--with-marked
-     (image-dired-remove-tag (image-dired-original-file-name) tag)
-     (image-dired-update-property
-      'tags (image-dired-list-tags (image-dired-original-file-name))))))
-
-
-;;; Thumbnail mode (cont.)
-
-(defun image-dired-original-file-name ()
-  "Get original file name for thumbnail or display image at point."
-  (get-text-property (point) 'original-file-name))
-
-(defun image-dired-file-name-at-point ()
-  "Get abbreviated file name for thumbnail or display image at point."
-  (let ((f (image-dired-original-file-name)))
-    (when f
-      (abbreviate-file-name f))))
-
-(defun image-dired-associated-dired-buffer ()
-  "Get associated Dired buffer at point."
-  (get-text-property (point) 'associated-dired-buffer))
-
-(defun image-dired-get-buffer-window (buf)
-  "Return window where buffer BUF is."
-  (get-window-with-predicate
-   (lambda (window)
-     (equal (window-buffer window) buf))
-   nil t))
-
-(defun image-dired-track-original-file ()
-  "Track the original file in the associated Dired buffer.
-See documentation for `image-dired-toggle-movement-tracking'.
-Interactive use only useful if `image-dired-track-movement' is nil."
-  (interactive)
-  (let* ((dired-buf (image-dired-associated-dired-buffer))
-         (file-name (image-dired-original-file-name))
-         (window (image-dired-get-buffer-window dired-buf)))
-    (and (buffer-live-p dired-buf) file-name
-         (with-current-buffer dired-buf
-           (if (not (dired-goto-file file-name))
-               (message "Could not track file")
-             (if window (set-window-point window (point))))))))
-
-(defun image-dired-toggle-movement-tracking ()
-  "Turn on and off `image-dired-track-movement'.
-Tracking of the movements between thumbnail and Dired buffer so that
-they are \"mirrored\" in the dired buffer.  When this is on, moving
-around in the thumbnail or dired buffer will find the matching
-position in the other buffer."
-  (interactive)
-  (setq image-dired-track-movement (not image-dired-track-movement))
-  (message "Movement tracking %s" (if image-dired-track-movement "on" "off")))
-
-(defun image-dired-track-thumbnail ()
-  "Track current Dired file's thumb in `image-dired-thumbnail-buffer'.
-This is almost the same as what `image-dired-track-original-file' does,
-but the other way around."
-  (let ((file (dired-get-filename))
-        prop-val found window)
-    (when (get-buffer image-dired-thumbnail-buffer)
-      (with-current-buffer image-dired-thumbnail-buffer
-        (goto-char (point-min))
-        (while (and (not (eobp))
-                    (not found))
-          (if (and (setq prop-val
-                         (get-text-property (point) 'original-file-name))
-                   (string= prop-val file))
-              (setq found t))
-          (if (not found)
-              (forward-char 1)))
-        (when found
-          (if (setq window (image-dired-thumbnail-window))
-              (set-window-point window (point)))
-          (image-dired-update-header-line))))))
-
-(defun image-dired-dired-next-line (&optional arg)
-  "Call `dired-next-line', then track thumbnail.
-This can safely replace `dired-next-line'.
-With prefix argument, move ARG lines."
-  (interactive "P")
-  (dired-next-line (or arg 1))
-  (if image-dired-track-movement
-      (image-dired-track-thumbnail)))
-
-(defun image-dired-dired-previous-line (&optional arg)
-  "Call `dired-previous-line', then track thumbnail.
-This can safely replace `dired-previous-line'.
-With prefix argument, move ARG lines."
-  (interactive "P")
-  (dired-previous-line (or arg 1))
-  (if image-dired-track-movement
-      (image-dired-track-thumbnail)))
-
-(defun image-dired--display-thumb-properties-fun ()
-  (let ((old-buf (current-buffer))
-        (old-point (point)))
-    (lambda ()
-      (when (and (equal (current-buffer) old-buf)
-                 (= (point) old-point))
-        (ignore-errors
-          (image-dired-update-header-line))))))
-
-(defun image-dired-forward-image (&optional arg wrap-around)
-  "Move to next image and display properties.
-Optional prefix ARG says how many images to move; the default is
-one image.  Negative means move backwards.
-On reaching end or beginning of buffer, stop and show a message.
-
-If optional argument WRAP-AROUND is non-nil, wrap around: if
-point is on the last image, move to the last one and vice versa."
-  (interactive "p")
-  (setq arg (or arg 1))
-  (let (pos)
-    (dotimes (_ (abs arg))
-      (if (and (not (if (> arg 0) (eobp) (bobp)))
-               (save-excursion
-                 (forward-char (if (> arg 0) 1 -1))
-                 (while (and (not (if (> arg 0) (eobp) (bobp)))
-                             (not (image-dired-image-at-point-p)))
-                   (forward-char (if (> arg 0) 1 -1)))
-                 (setq pos (point))
-                 (image-dired-image-at-point-p)))
-          (progn (goto-char pos)
-                 (image-dired-update-header-line))
-        (if wrap-around
-            (progn (goto-char (if (> arg 0)
-                                  (point-min)
-                                ;; There are two spaces after the last image.
-                                (- (point-max) 2)))
-                   (image-dired-update-header-line))
-          (message "At %s image" (if (> arg 0) "last" "first"))
-          (run-at-time 1 nil (image-dired--display-thumb-properties-fun))))))
-  (when image-dired-track-movement
-    (image-dired-track-original-file)))
-
-(defun image-dired-backward-image (&optional arg)
-  "Move to previous image and display properties.
-Optional prefix ARG says how many images to move; the default is
-one image.  Negative means move forward.
-On reaching end or beginning of buffer, stop and show a message."
-  (interactive "p")
-  (image-dired-forward-image (- (or arg 1))))
-
-(defun image-dired-next-line ()
-  "Move to next line and display properties."
-  (interactive nil image-dired-thumbnail-mode)
-  (let ((goal-column (current-column)))
-    (forward-line 1)
-    (move-to-column goal-column))
-  ;; If we end up in an empty spot, back up to the next thumbnail.
-  (if (not (image-dired-image-at-point-p))
-      (image-dired-backward-image))
-  (if image-dired-track-movement
-      (image-dired-track-original-file))
-  (image-dired-update-header-line))
-
-
-(defun image-dired-previous-line ()
-  "Move to previous line and display properties."
-  (interactive nil image-dired-thumbnail-mode)
-  (let ((goal-column (current-column)))
-    (forward-line -1)
-    (move-to-column goal-column))
-  ;; If we end up in an empty spot, back up to the next
-  ;; thumbnail. This should only happen if the user deleted a
-  ;; thumbnail and did not refresh, so it is not very common. But we
-  ;; can handle it in a good manner, so why not?
-  (if (not (image-dired-image-at-point-p))
-      (image-dired-backward-image))
-  (if image-dired-track-movement
-      (image-dired-track-original-file))
-  (image-dired-update-header-line))
-
-(defun image-dired-beginning-of-buffer ()
-  "Move to the first image in the buffer and display properties."
-  (interactive nil image-dired-thumbnail-mode)
-  (goto-char (point-min))
-  (while (and (not (image-at-point-p))
-              (not (eobp)))
-    (forward-char 1))
-  (when image-dired-track-movement
-    (image-dired-track-original-file))
-  (image-dired-update-header-line))
-
-(defun image-dired-end-of-buffer ()
-  "Move to the last image in the buffer and display properties."
-  (interactive nil image-dired-thumbnail-mode)
-  (goto-char (point-max))
-  (while (and (not (image-at-point-p))
-              (not (bobp)))
-    (forward-char -1))
-  (when image-dired-track-movement
-    (image-dired-track-original-file))
-  (image-dired-update-header-line))
-
-(defun image-dired-format-properties-string (buf file props comment)
-  "Format display properties.
-BUF is the associated Dired buffer, FILE is the original image file
-name, PROPS is a stringified list of tags and COMMENT is the image file's
-comment."
-  (format-spec
-   image-dired-display-properties-format
-   (list
-    (cons ?b (or buf ""))
-    (cons ?f file)
-    (cons ?t (or props ""))
-    (cons ?c (or comment "")))))
-
-(defun image-dired-update-header-line ()
-  "Update image information in the header line."
-  (when (and (not (eobp))
-             (memq major-mode '(image-dired-thumbnail-mode
-                                image-dired-display-image-mode)))
-    (let ((file-name (file-name-nondirectory (image-dired-original-file-name)))
-          (dired-buf (buffer-name (image-dired-associated-dired-buffer)))
-          (props (mapconcat #'identity (get-text-property (point) 'tags) ", "))
-          (comment (get-text-property (point) 'comment))
-          (message-log-max nil))
-      (if file-name
-          (setq header-line-format
-                (image-dired-format-properties-string
-                 dired-buf
-                 file-name
-                 props
-                 comment))))))
-
-(defun image-dired-dired-file-marked-p (&optional marker)
-  "In Dired, return t if file on current line is marked.
-If optional argument MARKER is non-nil, it is a character to look
-for.  The default is to look for `dired-marker-char'."
-  (setq marker (or marker dired-marker-char))
-  (save-excursion
-    (beginning-of-line)
-    (and (looking-at dired-re-mark)
-         (= (aref (match-string 0) 0) marker))))
-
-(defun image-dired-dired-file-flagged-p ()
-  "In Dired, return t if file on current line is flagged for deletion."
-  (image-dired-dired-file-marked-p dired-del-marker))
-
-(defmacro image-dired--with-thumbnail-buffer (&rest body)
-  (declare (indent defun) (debug t))
-  `(if-let ((buf (get-buffer image-dired-thumbnail-buffer)))
-       (with-current-buffer buf
-         (if-let ((win (get-buffer-window buf)))
-             (with-selected-window win
-               ,@body)
-           ,@body))
-     (user-error "No such buffer: %s" image-dired-thumbnail-buffer)))
-
-(defmacro image-dired--on-file-in-dired-buffer (&rest body)
-  "Run BODY with point on file at point in Dired buffer.
-Should be called from commands in `image-dired-thumbnail-mode'."
-  (declare (indent defun) (debug t))
-  `(let ((file-name (image-dired-original-file-name))
-         (dired-buf (image-dired-associated-dired-buffer)))
-     (if (not (and dired-buf file-name))
-         (message "No image, or image with correct properties, at point")
-       (with-current-buffer dired-buf
-         (when (dired-goto-file file-name)
-           ,@body
-           (image-dired-thumb-update-marks))))))
-
-(defmacro image-dired--do-mark-command (maybe-next &rest body)
-  "Helper macro for the mark, unmark and flag commands.
-Run BODY in Dired buffer.
-If optional argument MAYBE-NEXT is non-nil, show next image
-according to `image-dired-marking-shows-next'."
-  (declare (indent defun) (debug t))
-  `(image-dired--with-thumbnail-buffer
-     (image-dired--on-file-in-dired-buffer
-       ,@body)
-     ,(when maybe-next
-        '(if image-dired-marking-shows-next
-             (image-dired-display-next-thumbnail-original)
-           (image-dired-next-line)))))
-
-(defun image-dired-mark-thumb-original-file ()
-  "Mark original image file in associated Dired buffer."
-  (interactive nil image-dired-thumbnail-mode image-dired-display-image-mode)
-  (image-dired--do-mark-command t
-    (dired-mark 1)))
-
-(defun image-dired-unmark-thumb-original-file ()
-  "Unmark original image file in associated Dired buffer."
-  (interactive nil image-dired-thumbnail-mode image-dired-display-image-mode)
-  (image-dired--do-mark-command t
-    (dired-unmark 1)))
-
-(defun image-dired-flag-thumb-original-file ()
-  "Flag original image file for deletion in associated Dired buffer."
-  (interactive nil image-dired-thumbnail-mode image-dired-display-image-mode)
-  (image-dired--do-mark-command t
-    (dired-flag-file-deletion 1)))
-
-(defun image-dired-toggle-mark-thumb-original-file ()
-  "Toggle mark on original image file in associated Dired buffer."
-  (interactive nil image-dired-thumbnail-mode image-dired-display-image-mode)
-  (image-dired--do-mark-command nil
-    (if (image-dired-dired-file-marked-p)
-        (dired-unmark 1)
-      (dired-mark 1))))
-
-(defun image-dired-unmark-all-marks ()
-  "Remove all marks from all files in associated Dired buffer.
-Also update the marks in the thumbnail buffer."
-  (interactive nil image-dired-thumbnail-mode image-dired-display-image-mode)
-  (image-dired--do-mark-command nil
-    (dired-unmark-all-marks))
-  (image-dired--with-thumbnail-buffer
-    (image-dired-thumb-update-marks)))
-
-(defun image-dired-jump-original-dired-buffer ()
-  "Jump to the Dired buffer associated with the current image file.
-You probably want to use this together with
-`image-dired-track-original-file'."
-  (interactive nil image-dired-thumbnail-mode)
-  (let ((buf (image-dired-associated-dired-buffer))
-        window frame)
-    (setq window (image-dired-get-buffer-window buf))
-    (if window
-        (progn
-          (if (not (equal (selected-frame) (setq frame (window-frame window))))
-              (select-frame-set-input-focus frame))
-          (select-window window))
-      (message "Associated dired buffer not visible"))))
-
-;;;###autoload
-(defun image-dired-jump-thumbnail-buffer ()
-  "Jump to thumbnail buffer."
-  (interactive)
-  (let ((window (image-dired-thumbnail-window))
-        frame)
-    (if window
-        (progn
-          (if (not (equal (selected-frame) (setq frame (window-frame window))))
-              (select-frame-set-input-focus frame))
-          (select-window window))
-      (message "Thumbnail buffer not visible"))))
-
-(defvar image-dired-thumbnail-mode-line-up-map
-  (let ((map (make-sparse-keymap)))
-    ;; map it to "g" so that the user can press it more quickly
-    (define-key map "g" #'image-dired-line-up-dynamic)
-    ;; "f" for "fixed" number of thumbs per row
-    (define-key map "f" #'image-dired-line-up)
-    ;; "i" for "interactive"
-    (define-key map "i" #'image-dired-line-up-interactive)
-    map)
-  "Keymap for line-up commands in `image-dired-thumbnail-mode'.")
-
-(defvar image-dired-thumbnail-mode-tag-map
-  (let ((map (make-sparse-keymap)))
-    ;; map it to "t" so that the user can press it more quickly
-    (define-key map "t" #'image-dired-tag-thumbnail)
-    ;; "r" for "remove"
-    (define-key map "r" #'image-dired-tag-thumbnail-remove)
-    map)
-  "Keymap for tag commands in `image-dired-thumbnail-mode'.")
-
-(defvar image-dired-thumbnail-mode-map
-  (let ((map (make-sparse-keymap)))
-    (define-key map [right] #'image-dired-forward-image)
-    (define-key map [left] #'image-dired-backward-image)
-    (define-key map [up] #'image-dired-previous-line)
-    (define-key map [down] #'image-dired-next-line)
-    (define-key map "\C-f" #'image-dired-forward-image)
-    (define-key map "\C-b" #'image-dired-backward-image)
-    (define-key map "\C-p" #'image-dired-previous-line)
-    (define-key map "\C-n" #'image-dired-next-line)
-
-    (define-key map "<" #'image-dired-beginning-of-buffer)
-    (define-key map ">" #'image-dired-end-of-buffer)
-    (define-key map (kbd "M-<") #'image-dired-beginning-of-buffer)
-    (define-key map (kbd "M->") #'image-dired-end-of-buffer)
-
-    (define-key map "d" #'image-dired-flag-thumb-original-file)
-    (define-key map [delete] #'image-dired-flag-thumb-original-file)
-    (define-key map "m" #'image-dired-mark-thumb-original-file)
-    (define-key map "u" #'image-dired-unmark-thumb-original-file)
-    (define-key map "U" #'image-dired-unmark-all-marks)
-    (define-key map "." #'image-dired-track-original-file)
-    (define-key map [tab] #'image-dired-jump-original-dired-buffer)
-
-    ;; add line-up map
-    (define-key map "g" image-dired-thumbnail-mode-line-up-map)
-    ;; add tag map
-    (define-key map "t" image-dired-thumbnail-mode-tag-map)
-
-    (define-key map "\C-m" #'image-dired-display-thumbnail-original-image)
-    (define-key map [C-return] #'image-dired-thumbnail-display-external)
-
-    (define-key map "L" #'image-dired-rotate-original-left)
-    (define-key map "R" #'image-dired-rotate-original-right)
-
-    (define-key map "D" #'image-dired-thumbnail-set-image-description)
-    (define-key map "S" #'image-dired-slideshow-start)
-    (define-key map "\C-d" #'image-dired-delete-char)
-    (define-key map " " #'image-dired-display-next-thumbnail-original)
-    (define-key map (kbd "DEL") 
#'image-dired-display-previous-thumbnail-original)
-    (define-key map "c" #'image-dired-comment-thumbnail)
-
-    ;; Mouse
-    (define-key map [mouse-2] #'image-dired-mouse-display-image)
-    (define-key map [mouse-1] #'image-dired-mouse-select-thumbnail)
-    (define-key map [mouse-3] #'image-dired-mouse-select-thumbnail)
-    (define-key map [down-mouse-1] #'image-dired-mouse-select-thumbnail)
-    (define-key map [down-mouse-2] #'image-dired-mouse-select-thumbnail)
-    (define-key map [down-mouse-3] #'image-dired-mouse-select-thumbnail)
-    ;; Seems I must first set C-down-mouse-1 to undefined, or else it
-    ;; will trigger the buffer menu. If I try to instead bind
-    ;; C-down-mouse-1 to `image-dired-mouse-toggle-mark', I get a message
-    ;; about C-mouse-1 not being defined afterwards. Annoying, but I
-    ;; probably do not completely understand mouse events.
-    (define-key map [C-down-mouse-1] #'undefined)
-    (define-key map [C-mouse-1] #'image-dired-mouse-toggle-mark)
-    map)
-  "Keymap for `image-dired-thumbnail-mode'.")
-
-(easy-menu-define image-dired-thumbnail-mode-menu 
image-dired-thumbnail-mode-map
-  "Menu for `image-dired-thumbnail-mode'."
-  '("Image-Dired"
-    ["Display image" image-dired-display-thumbnail-original-image]
-    ["Display in external viewer" image-dired-thumbnail-display-external]
-    ["Jump to Dired buffer" image-dired-jump-original-dired-buffer]
-    "---"
-    ["Mark image" image-dired-mark-thumb-original-file]
-    ["Unmark image" image-dired-unmark-thumb-original-file]
-    ["Unmark all images" image-dired-unmark-all-marks]
-    ["Flag for deletion" image-dired-flag-thumb-original-file]
-    ["Delete marked images" image-dired-delete-marked]
-    "---"
-    ["Rotate original right" image-dired-rotate-original-right]
-    ["Rotate original left" image-dired-rotate-original-left]
-    "---"
-    ["Comment thumbnail" image-dired-comment-thumbnail]
-    ["Tag current or marked thumbnails" image-dired-tag-thumbnail]
-    ["Remove tag from current or marked thumbnails"
-     image-dired-tag-thumbnail-remove]
-    ["Start slideshow" image-dired-slideshow-start]
-    "---"
-    ("View Options"
-     ["Toggle movement tracking" image-dired-toggle-movement-tracking
-      :style toggle
-      :selected image-dired-track-movement]
-     "---"
-     ["Line up thumbnails" image-dired-line-up]
-     ["Dynamic line up" image-dired-line-up-dynamic]
-     ["Refresh thumb" image-dired-refresh-thumb])
-    ["Quit" quit-window]))
-
-(defvar image-dired-display-image-mode-map
-  (let ((map (make-sparse-keymap)))
-    (define-key map "S" #'image-dired-slideshow-start)
-    (define-key map (kbd "SPC") #'image-dired-display-next-thumbnail-original)
-    (define-key map (kbd "DEL") 
#'image-dired-display-previous-thumbnail-original)
-    (define-key map "n" #'image-dired-display-next-thumbnail-original)
-    (define-key map "p" #'image-dired-display-previous-thumbnail-original)
-    (define-key map "m" #'image-dired-mark-thumb-original-file)
-    (define-key map "d" #'image-dired-flag-thumb-original-file)
-    (define-key map "u" #'image-dired-unmark-thumb-original-file)
-    (define-key map "U" #'image-dired-unmark-all-marks)
-    ;; Disable keybindings from `image-mode-map' that doesn't make sense here.
-    (define-key map "o" nil) ; image-save
-    map)
-  "Keymap for `image-dired-display-image-mode'.")
-
-(define-derived-mode image-dired-thumbnail-mode
-  special-mode "image-dired-thumbnail"
-  "Browse and manipulate thumbnail images using Dired.
-Use `image-dired-minor-mode' to get a nice setup."
-  :interactive nil
-  (buffer-disable-undo)
-  (add-hook 'file-name-at-point-functions 'image-dired-file-name-at-point nil 
t)
-  (setq-local window-resize-pixelwise t)
-  (setq-local bookmark-make-record-function #'image-dired-bookmark-make-record)
-  ;; Use approximately as much vertical spacing as horizontal.
-  (setq-local line-spacing (frame-char-width)))
-
-
-;;; Display image mode
-
-(define-derived-mode image-dired-display-image-mode
-  image-mode "image-dired-image-display"
-  "Mode for displaying and manipulating original image.
-Resized or in full-size."
-  :interactive nil
-  (add-hook 'file-name-at-point-functions #'image-dired-file-name-at-point nil 
t))
-
-(defvar image-dired-minor-mode-map
-  (let ((map (make-sparse-keymap)))
-    ;; (set-keymap-parent map dired-mode-map)
-    ;; Hijack previous and next line movement. Let C-p and C-b be
-    ;; though...
-    (define-key map "p" #'image-dired-dired-previous-line)
-    (define-key map "n" #'image-dired-dired-next-line)
-    (define-key map [up] #'image-dired-dired-previous-line)
-    (define-key map [down] #'image-dired-dired-next-line)
-
-    (define-key map (kbd "C-S-n") #'image-dired-next-line-and-display)
-    (define-key map (kbd "C-S-p") #'image-dired-previous-line-and-display)
-    (define-key map (kbd "C-S-m") #'image-dired-mark-and-display-next)
-
-    (define-key map "\C-td" #'image-dired-display-thumbs)
-    (define-key map [tab] #'image-dired-jump-thumbnail-buffer)
-    (define-key map "\C-ti" #'image-dired-dired-display-image)
-    (define-key map "\C-tx" #'image-dired-dired-display-external)
-    (define-key map "\C-ta" #'image-dired-display-thumbs-append)
-    (define-key map "\C-t." #'image-dired-display-thumb)
-    (define-key map "\C-tc" #'image-dired-dired-comment-files)
-    (define-key map "\C-tf" #'image-dired-mark-tagged-files)
-    map)
-  "Keymap for `image-dired-minor-mode'.")
-
-(easy-menu-define image-dired-minor-mode-menu image-dired-minor-mode-map
-  "Menu for `image-dired-minor-mode'."
-  '("Image-dired"
-    ["Display thumb for next file" image-dired-next-line-and-display]
-    ["Display thumb for previous file" image-dired-previous-line-and-display]
-    ["Mark and display next" image-dired-mark-and-display-next]
-    "---"
-    ["Create thumbnails for marked files" image-dired-create-thumbs]
-    "---"
-    ["Display thumbnails append" image-dired-display-thumbs-append]
-    ["Display this thumbnail" image-dired-display-thumb]
-    ["Display image" image-dired-dired-display-image]
-    ["Display in external viewer" image-dired-dired-display-external]
-    "---"
-    ["Toggle display properties" image-dired-toggle-dired-display-properties
-     :style toggle
-     :selected image-dired-dired-disp-props]
-    ["Toggle append browsing" image-dired-toggle-append-browsing
-     :style toggle
-     :selected image-dired-append-when-browsing]
-    ["Toggle movement tracking" image-dired-toggle-movement-tracking
-     :style toggle
-     :selected image-dired-track-movement]
-    "---"
-    ["Jump to thumbnail buffer" image-dired-jump-thumbnail-buffer]
-    ["Mark tagged files" image-dired-mark-tagged-files]
-    ["Comment files" image-dired-dired-comment-files]
-    ["Copy with EXIF file name" image-dired-copy-with-exif-file-name]))
-
-;;;###autoload
-(define-minor-mode image-dired-minor-mode
-  "Setup easy-to-use keybindings for the commands to be used in Dired mode.
-Note that n, p and <down> and <up> will be hijacked and bound to
-`image-dired-dired-next-line' and `image-dired-dired-previous-line'."
-  :keymap image-dired-minor-mode-map)
-
-(declare-function clear-image-cache "image.c" (&optional filter))
-
-(defun image-dired-create-thumbs (&optional arg)
-  "Create thumbnail images for all marked files in Dired.
-With prefix argument ARG, create thumbnails even if they already exist
-\(i.e. use this to refresh your thumbnails)."
-  (interactive "P")
-  (let (thumb-name)
-    (dolist (curr-file (dired-get-marked-files))
-      (setq thumb-name (image-dired-thumb-name curr-file))
-      ;; If the user overrides the exist check, we must clear the
-      ;; image cache so that if the user wants to display the
-      ;; thumbnail, it is not fetched from cache.
-      (when arg
-        (clear-image-cache (expand-file-name thumb-name)))
-      (when (or (not (file-exists-p thumb-name))
-                arg)
-        (image-dired-create-thumb curr-file thumb-name)))))
-
-
-;;; Slideshow
-
-(defcustom image-dired-slideshow-delay 5.0
-  "Seconds to wait before showing the next image in a slideshow.
-This is used by `image-dired-slideshow-start'."
-  :type 'float
-  :version "29.1")
-
-(define-obsolete-variable-alias 'image-dired-slideshow-timer
-  'image-dired--slideshow-timer "29.1")
-(defvar image-dired--slideshow-timer nil
-  "Slideshow timer.")
-
-(defvar image-dired--slideshow-initial nil)
-
-(defun image-dired-slideshow-step ()
-  "Step to next image in a slideshow."
-  (if-let ((buf (get-buffer image-dired-thumbnail-buffer)))
-      (with-current-buffer buf
-        (image-dired-display-next-thumbnail-original))
-    (image-dired-slideshow-stop)))
-
-(defun image-dired-slideshow-start (&optional arg)
-  "Start a slideshow, waiting `image-dired-slideshow-delay' between images.
-
-With prefix argument ARG, wait that many seconds before going to
-the next image.
-
-With a negative prefix argument, prompt user for the delay."
-  (interactive "P" image-dired-thumbnail-mode image-dired-display-image-mode)
-  (let ((delay (if (not arg)
-                   image-dired-slideshow-delay
-                 (if (> arg 0)
-                     arg
-                   (string-to-number
-                    (let ((delay (number-to-string 
image-dired-slideshow-delay)))
-                      (read-string
-                       (format-prompt "Delay, in seconds. Decimals are 
accepted" delay))
-                      delay))))))
-    (setq image-dired--slideshow-timer
-          (run-with-timer
-           0 delay
-           'image-dired-slideshow-step))
-    (add-hook 'post-command-hook 'image-dired-slideshow-stop)
-    (setq image-dired--slideshow-initial t)
-    (message "Running slideshow; use any command to stop")))
-
-(defun image-dired-slideshow-stop ()
-  "Cancel slideshow."
-  ;; Make sure we don't immediately stop after
-  ;; `image-dired-slideshow-start'.
-  (unless image-dired--slideshow-initial
-    (remove-hook 'post-command-hook 'image-dired-slideshow-stop)
-    (cancel-timer image-dired--slideshow-timer))
-  (setq image-dired--slideshow-initial nil))
-
-
-;;; Thumbnail mode (cont. 3)
-
-(defun image-dired-delete-char ()
-  "Remove current thumbnail from thumbnail buffer and line up."
-  (interactive nil image-dired-thumbnail-mode)
-  (let ((inhibit-read-only t))
-    (delete-char 1)
-    (when (= (following-char) ?\s)
-      (delete-char 1))))
-
-;;;###autoload
-(defun image-dired-display-thumbs-append ()
-  "Append thumbnails to `image-dired-thumbnail-buffer'."
-  (interactive)
-  (image-dired-display-thumbs nil t t))
-
-;;;###autoload
-(defun image-dired-display-thumb ()
-  "Shorthand for `image-dired-display-thumbs' with prefix argument."
-  (interactive)
-  (image-dired-display-thumbs t nil t))
-
-(defun image-dired-line-up ()
-  "Line up thumbnails according to `image-dired-thumbs-per-row'.
-See also `image-dired-line-up-dynamic'."
-  (interactive)
-  (let ((inhibit-read-only t))
-    (goto-char (point-min))
-    (while (and (not (image-dired-image-at-point-p))
-                (not (eobp)))
-      (delete-char 1))
-    (while (not (eobp))
-      (forward-char)
-      (while (and (not (image-dired-image-at-point-p))
-                  (not (eobp)))
-        (delete-char 1)))
-    (goto-char (point-min))
-    (let ((seen 0)
-          (thumb-prev-pos 0)
-          (thumb-width-chars
-           (ceiling (/ (+ (* 2 image-dired-thumb-relief)
-                          (* 2 image-dired-thumb-margin)
-                          (image-dired-thumb-size 'width))
-                       (float (frame-char-width))))))
-      (while (not (eobp))
-        (forward-char)
-        (if (= image-dired-thumbs-per-row 1)
-            (insert "\n")
-          (cl-incf thumb-prev-pos thumb-width-chars)
-          (insert (propertize " " 'display `(space :align-to ,thumb-prev-pos)))
-          (cl-incf seen)
-          (when (and (= seen (- image-dired-thumbs-per-row 1))
-                    (not (eobp)))
-            (forward-char)
-            (insert "\n")
-            (setq seen 0)
-            (setq thumb-prev-pos 0)))))
-    (goto-char (point-min))))
-
-(defun image-dired-line-up-dynamic ()
-  "Line up thumbnails images dynamically.
-Calculate how many thumbnails fit."
-  (interactive)
-  (let* ((char-width (frame-char-width))
-        (width (image-dired-window-width-pixels 
(image-dired-thumbnail-window)))
-        (image-dired-thumbs-per-row
-         (/ width
-            (+ (* 2 image-dired-thumb-relief)
-               (* 2 image-dired-thumb-margin)
-               (image-dired-thumb-size 'width)
-               char-width))))
-    (image-dired-line-up)))
-
-(defun image-dired-line-up-interactive ()
-  "Line up thumbnails interactively.
-Ask user how many thumbnails should be displayed per row."
-  (interactive)
-  (let ((image-dired-thumbs-per-row
-         (string-to-number (read-string "How many thumbs per row: "))))
-    (if (not (> image-dired-thumbs-per-row 0))
-        (message "Number must be greater than 0")
-      (image-dired-line-up))))
-
-(defun image-dired-thumbnail-display-external ()
-  "Display original image for thumbnail at point using external viewer."
-  (interactive)
-  (let ((file (image-dired-original-file-name)))
-    (if (not (image-dired-image-at-point-p))
-        (message "No thumbnail at point")
-      (if (not file)
-          (message "No original file name found")
-        (start-process "image-dired-thumb-external" nil
-                       image-dired-external-viewer file)))))
-
-;;;###autoload
-(defun image-dired-dired-display-external ()
-  "Display file at point using an external viewer."
-  (interactive)
-  (let ((file (dired-get-filename)))
-    (start-process "image-dired-external" nil
-                   image-dired-external-viewer file)))
-
-(defun image-dired-window-width-pixels (window)
-  "Calculate WINDOW width in pixels."
-    (* (window-width window) (frame-char-width)))
-
-(defun image-dired-display-window ()
-  "Return window where `image-dired-display-image-buffer' is visible."
-  (get-window-with-predicate
-   (lambda (window)
-     (equal (buffer-name (window-buffer window)) 
image-dired-display-image-buffer))
-   nil t))
-
-(defun image-dired-thumbnail-window ()
-  "Return window where `image-dired-thumbnail-buffer' is visible."
-  (get-window-with-predicate
-   (lambda (window)
-     (equal (buffer-name (window-buffer window)) image-dired-thumbnail-buffer))
-   nil t))
-
-(defun image-dired-associated-dired-buffer-window ()
-  "Return window where associated Dired buffer is visible."
-  (let (buf)
-    (if (image-dired-image-at-point-p)
-        (progn
-          (setq buf (image-dired-associated-dired-buffer))
-          (get-window-with-predicate
-           (lambda (window)
-             (equal (window-buffer window) buf))))
-      (error "No thumbnail image at point"))))
-
-(defun image-dired-display-image (file &optional _ignored)
-  "Display image FILE in image buffer.
-Use this when you want to display the image, in a new window.
-The window will use `image-dired-display-image-mode' which is
-based on `image-mode'."
-  (declare (advertised-calling-convention (file) "29.1"))
-  (setq file (expand-file-name file))
-  (when (not (file-exists-p file))
-    (error "No such file: %s" file))
-  (let ((buf (get-buffer image-dired-display-image-buffer))
-        (cur-win (selected-window)))
-    (when buf
-      (kill-buffer buf))
-    (when-let ((buf (find-file-noselect file nil t)))
-      (pop-to-buffer buf)
-      (rename-buffer image-dired-display-image-buffer)
-      (image-dired-display-image-mode)
-      (select-window cur-win))))
-
-(defun image-dired-display-thumbnail-original-image (&optional arg)
-  "Display current thumbnail's original image in display buffer.
-See documentation for `image-dired-display-image' for more information.
-With prefix argument ARG, display image in its original size."
-  (interactive "P")
-  (let ((file (image-dired-original-file-name)))
-    (if (not (string-equal major-mode "image-dired-thumbnail-mode"))
-        (message "Not in image-dired-thumbnail-mode")
-      (if (not (image-dired-image-at-point-p))
-          (message "No thumbnail at point")
-        (if (not file)
-            (message "No original file name found")
-          (image-dired-display-image file arg))))))
-
-
-;;;###autoload
-(defun image-dired-dired-display-image (&optional arg)
-  "Display current image file.
-See documentation for `image-dired-display-image' for more information.
-With prefix argument ARG, display image in its original size."
-  (interactive "P")
-  (image-dired-display-image (dired-get-filename) arg))
-
-(defun image-dired-image-at-point-p ()
-  "Return non-nil if there is an `image-dired' thumbnail at point."
-  (get-text-property (point) 'image-dired-thumbnail))
-
-(defun image-dired-refresh-thumb ()
-  "Force creation of new image for current thumbnail."
-  (interactive nil image-dired-thumbnail-mode)
-  (let* ((file (image-dired-original-file-name))
-         (thumb (expand-file-name (image-dired-thumb-name file))))
-    (clear-image-cache (expand-file-name thumb))
-    (image-dired-create-thumb file thumb)))
-
-(defun image-dired-rotate-original (degrees)
-  "Rotate original image DEGREES degrees."
-  (image-dired--check-executable-exists
-   'image-dired-cmd-rotate-original-program)
-  (if (not (image-dired-image-at-point-p))
-      (message "No image at point")
-    (let* ((file (image-dired-original-file-name))
-           (spec
-            (list
-             (cons ?d degrees)
-             (cons ?o (expand-file-name file))
-             (cons ?t image-dired-temp-rotate-image-file))))
-      (unless (eq 'jpeg (image-type file))
-        (user-error "Only JPEG images can be rotated"))
-      (if (not (= 0 (apply #'call-process 
image-dired-cmd-rotate-original-program
-                           nil nil nil
-                           (mapcar (lambda (arg) (format-spec arg spec))
-                                   image-dired-cmd-rotate-original-options))))
-          (error "Could not rotate image")
-        (image-dired-display-image image-dired-temp-rotate-image-file)
-        (if (or (and image-dired-rotate-original-ask-before-overwrite
-                     (y-or-n-p
-                     "Rotate to temp file OK.  Overwrite original image? "))
-                (not image-dired-rotate-original-ask-before-overwrite))
-            (progn
-              (copy-file image-dired-temp-rotate-image-file file t)
-              (image-dired-refresh-thumb))
-          (image-dired-display-image file))))))
-
-(defun image-dired-rotate-original-left ()
-  "Rotate original image left (counter clockwise) 90 degrees.
-The result of the rotation is displayed in the image display area
-and a confirmation is needed before the original image files is
-overwritten.  This confirmation can be turned off using
-`image-dired-rotate-original-ask-before-overwrite'."
-  (interactive)
-  (image-dired-rotate-original "270"))
-
-(defun image-dired-rotate-original-right ()
-  "Rotate original image right (clockwise) 90 degrees.
-The result of the rotation is displayed in the image display area
-and a confirmation is needed before the original image files is
-overwritten.  This confirmation can be turned off using
-`image-dired-rotate-original-ask-before-overwrite'."
-  (interactive)
-  (image-dired-rotate-original "90"))
-
-
-;;; EXIF support
-
-(defun image-dired-get-exif-file-name (file)
-  "Use the image's EXIF information to return a unique file name.
-The file name should be unique as long as you do not take more than
-one picture per second.  The original file name is suffixed at the end
-for traceability.  The format of the returned file name is
-YYYY_MM_DD_HH_MM_DD_ORIG_FILE_NAME.jpg.  Used from
-`image-dired-copy-with-exif-file-name'."
-  (let (data no-exif-data-found)
-    (if (not (eq 'jpeg (image-type (expand-file-name file))))
-        (setq no-exif-data-found t
-              data (format-time-string
-                    "%Y:%m:%d %H:%M:%S"
-                    (file-attribute-modification-time
-                     (file-attributes (expand-file-name file)))))
-      (setq data (exif-field 'date-time (exif-parse-file
-                                         (expand-file-name file)))))
-    (while (string-match "[ :]" data)
-      (setq data (replace-match "_" nil nil data)))
-    (format "%s%s%s" data
-            (if no-exif-data-found
-                "_noexif_"
-              "_")
-            (file-name-nondirectory file))))
-
-(defun image-dired-thumbnail-set-image-description ()
-  "Set the ImageDescription EXIF tag for the original image.
-If the image already has a value for this tag, it is used as the
-default value at the prompt."
-  (interactive)
-  (if (not (image-dired-image-at-point-p))
-      (message "No thumbnail at point")
-    (let* ((file (image-dired-original-file-name))
-           (old-value (or (exif-field 'description (exif-parse-file file)) 
"")))
-      (if (eq 0
-              (image-dired-set-exif-data file "ImageDescription"
-                                   (read-string "Value of ImageDescription: "
-                                               old-value)))
-          (message "Successfully wrote ImageDescription tag")
-        (error "Could not write ImageDescription tag")))))
-
-(defun image-dired-set-exif-data (file tag-name tag-value)
-  "In FILE, set EXIF tag TAG-NAME to value TAG-VALUE."
-  (image-dired--check-executable-exists
-   'image-dired-cmd-write-exif-data-program)
-  (let ((spec
-         (list
-          (cons ?f (expand-file-name file))
-          (cons ?t tag-name)
-          (cons ?v tag-value))))
-    (apply #'call-process image-dired-cmd-write-exif-data-program nil nil nil
-           (mapcar (lambda (arg) (format-spec arg spec))
-                   image-dired-cmd-write-exif-data-options))))
-
-(defun image-dired-copy-with-exif-file-name ()
-  "Copy file with unique name to main image directory.
-Copy current or all marked files in Dired to a new file in your
-main image directory, using a file name generated by
-`image-dired-get-exif-file-name'.  A typical usage for this if when
-copying images from a digital camera into the image directory.
-
- Typically, you would open up the folder with the incoming
-digital images, mark the files to be copied, and execute this
-function.  The result is a couple of new files in
-`image-dired-main-image-directory' called
-2005_05_08_12_52_00_dscn0319.jpg,
-2005_05_08_14_27_45_dscn0320.jpg etc."
-  (interactive)
-  (let (new-name
-        (files (dired-get-marked-files)))
-    (mapc
-     (lambda (curr-file)
-       (setq new-name
-             (format "%s/%s"
-                     (file-name-as-directory
-                      (expand-file-name image-dired-main-image-directory))
-                     (image-dired-get-exif-file-name curr-file)))
-       (message "Copying %s to %s" curr-file new-name)
-       (copy-file curr-file new-name))
-     files)))
-
-;;; Thumbnail mode (cont.)
-
-(defun image-dired-display-next-thumbnail-original (&optional arg)
-  "Move to the next image in the thumbnail buffer and display it.
-With prefix ARG, move that many thumbnails."
-  (interactive "p" image-dired-thumbnail-mode image-dired-display-image-mode)
-  (image-dired--with-thumbnail-buffer
-    (image-dired-forward-image arg t)
-    (image-dired-display-thumbnail-original-image)))
-
-(defun image-dired-display-previous-thumbnail-original (arg)
-  "Move to the previous image in the thumbnail buffer and display it.
-With prefix ARG, move that many thumbnails."
-  (interactive "p" image-dired-thumbnail-mode image-dired-display-image-mode)
-  (image-dired-display-next-thumbnail-original (- arg)))
-
-
-;;; Image Comments
-
-(defun image-dired-write-comments (file-comments)
-  "Write file comments to database.
-Write file comments to one or more files.
-FILE-COMMENTS is an alist on the following form:
- ((FILE . COMMENT) ... )"
-  (image-dired-sane-db-file)
-  (let (end comment-beg-pos comment-end-pos file comment)
-    (image-dired--with-db-file
-     (setq buffer-file-name image-dired-db-file)
-     (dolist (elt file-comments)
-       (setq file (car elt)
-            comment (cdr elt))
-       (goto-char (point-min))
-       (if (search-forward-regexp (format "^%s.*$" file) nil t)
-          (progn
-            (setq end (point))
-            (beginning-of-line)
-            ;; Delete old comment, if any
-            (when (search-forward ";comment:" end t)
-              (setq comment-beg-pos (match-beginning 0))
-              ;; Any tags after the comment?
-              (if (search-forward ";" end t)
-                  (setq comment-end-pos (- (point) 1))
-                (setq comment-end-pos end))
-              ;; Delete comment tag and comment
-              (delete-region comment-beg-pos comment-end-pos))
-            ;; Insert new comment
-            (beginning-of-line)
-            (unless (search-forward ";" end t)
-              (end-of-line)
-              (insert ";"))
-            (insert (format "comment:%s;" comment)))
-        ;; File does not exist in database - add it.
-        (goto-char (point-max))
-        (insert (format "%s;comment:%s\n" file comment))))
-     (save-buffer))))
-
-(defun image-dired-update-property (prop value)
-  "Update text property PROP with value VALUE at point."
-  (let ((inhibit-read-only t))
-    (put-text-property
-     (point) (1+ (point))
-     prop
-     value)))
-
-;;;###autoload
-(defun image-dired-dired-comment-files ()
-  "Add comment to current or marked files in Dired."
-  (interactive)
-  (let ((comment (image-dired-read-comment)))
-    (image-dired-write-comments
-     (mapcar
-      (lambda (curr-file)
-        (cons curr-file comment))
-      (dired-get-marked-files)))))
-
-(defun image-dired-comment-thumbnail ()
-  "Add comment to current thumbnail in thumbnail buffer."
-  (interactive)
-  (let* ((file (image-dired-original-file-name))
-         (comment (image-dired-read-comment file)))
-    (image-dired-write-comments (list (cons file comment)))
-    (image-dired-update-property 'comment comment))
-  (image-dired-update-header-line))
-
-(defun image-dired-read-comment (&optional file)
-  "Read comment for an image.
-Optionally use old comment from FILE as initial value."
-  (let ((comment
-         (read-string
-          "Comment: "
-          (if file (image-dired-get-comment file)))))
-    comment))
-
-(defun image-dired-get-comment (file)
-  "Get comment for file FILE."
-  (image-dired-sane-db-file)
-  (image-dired--with-db-file
-   (let (end comment-beg-pos comment-end-pos comment)
-     (when (search-forward-regexp (format "^%s" file) nil t)
-       (end-of-line)
-       (setq end (point))
-       (beginning-of-line)
-       (when (search-forward ";comment:" end t)
-        (setq comment-beg-pos (point))
-        (if (search-forward ";" end t)
-            (setq comment-end-pos (- (point) 1))
-          (setq comment-end-pos end))
-        (setq comment (buffer-substring
-                       comment-beg-pos comment-end-pos))))
-     comment)))
-
-;;;###autoload
-(defun image-dired-mark-tagged-files (regexp)
-  "Use REGEXP to mark files with matching tag.
-A `tag' is a keyword, a piece of meta data, associated with an
-image file and stored in image-dired's database file.  This command
-lets you input a regexp and this will be matched against all tags
-on all image files in the database file.  The files that have a
-matching tag will be marked in the Dired buffer."
-  (interactive "sMark tagged files (regexp): ")
-  (image-dired-sane-db-file)
-  (let ((hits 0)
-        files)
-    (image-dired--with-db-file
-      ;; Collect matches
-      (while (search-forward-regexp "\\(^[^;\n]+\\);\\(.*\\)" nil t)
-        (let ((file (match-string 1))
-              (tags (split-string (match-string 2) ";")))
-          (when (seq-find (lambda (tag)
-                            (string-match-p regexp tag))
-                          tags)
-            (push file files)))))
-    ;; Mark files
-    (dolist (curr-file files)
-      ;; I tried using `dired-mark-files-regexp' but it was waaaay to
-      ;; slow.  Don't bother about hits found in other directories
-      ;; than the current one.
-      (when (string= (file-name-as-directory
-                     (expand-file-name default-directory))
-                    (file-name-as-directory
-                     (file-name-directory curr-file)))
-       (setq curr-file (file-name-nondirectory curr-file))
-       (goto-char (point-min))
-       (when (search-forward-regexp (format "\\s %s$" curr-file) nil t)
-         (setq hits (+ hits 1))
-         (dired-mark 1))))
-    (message "%d files with matching tag marked" hits)))
-
-
-
-;;; Mouse support
-
-(defun image-dired-mouse-display-image (event)
-  "Use mouse EVENT, call `image-dired-display-image' to display image.
-Track this in associated Dired buffer if `image-dired-track-movement' is
-non-nil."
-  (interactive "e")
-  (mouse-set-point event)
-  (goto-char (posn-point (event-end event)))
-  (unless (image-at-point-p)
-    (image-dired-backward-image))
-  (let ((file (image-dired-original-file-name)))
-    (when file
-      (if image-dired-track-movement
-         (image-dired-track-original-file))
-      (image-dired-display-image file))))
-
-(defun image-dired-mouse-select-thumbnail (event)
-  "Use mouse EVENT to select thumbnail image.
-Track this in associated Dired buffer if `image-dired-track-movement' is
-non-nil."
-  (interactive "e")
-  (mouse-set-point event)
-  (goto-char (posn-point (event-end event)))
-  (unless (image-at-point-p)
-    (image-dired-backward-image))
-  (if image-dired-track-movement
-      (image-dired-track-original-file))
-  (image-dired-update-header-line))
-
-
-
-;;; Dired marks and tags
-
-(defun image-dired-thumb-file-marked-p (&optional flagged)
-  "Check if file is marked in associated Dired buffer.
-If optional argument FLAGGED is non-nil, check if file is flagged
-for deletion instead."
-  (let ((file-name (image-dired-original-file-name))
-        (dired-buf (image-dired-associated-dired-buffer)))
-    (when (and dired-buf file-name)
-      (with-current-buffer dired-buf
-        (save-excursion
-          (when (dired-goto-file file-name)
-            (if flagged
-                (image-dired-dired-file-flagged-p)
-              (image-dired-dired-file-marked-p))))))))
-
-(defun image-dired-thumb-file-flagged-p ()
-  "Check if file is flagged for deletion in associated Dired buffer."
-  (image-dired-thumb-file-marked-p t))
-
-(defun image-dired-delete-marked ()
-  "Delete current or marked thumbnails and associated images."
-  (interactive)
-  (image-dired--with-marked
-   (image-dired-delete-char)
-   (unless (bobp)
-     (backward-char)))
-  (image-dired--line-up-with-method)
-  (with-current-buffer (image-dired-associated-dired-buffer)
-    (dired-do-delete)))
-
-(defun image-dired-thumb-update-marks ()
-  "Update the marks in the thumbnail buffer."
-  (when image-dired-thumb-visible-marks
-    (with-current-buffer image-dired-thumbnail-buffer
-      (save-mark-and-excursion
-        (goto-char (point-min))
-        (let ((inhibit-read-only t))
-          (while (not (eobp))
-            (with-silent-modifications
-              (cond ((image-dired-thumb-file-marked-p)
-                     (add-face-text-property (point) (1+ (point))
-                                             'image-dired-thumb-mark))
-                    ((image-dired-thumb-file-flagged-p)
-                     (add-face-text-property (point) (1+ (point))
-                                             'image-dired-thumb-flagged))
-                    (t (remove-text-properties (point) (1+ (point))
-                                               '(face 
image-dired-thumb-mark)))))
-            (forward-char)))))))
-
-(defun image-dired-mouse-toggle-mark-1 ()
-  "Toggle Dired mark for current thumbnail.
-Track this in associated Dired buffer if
-`image-dired-track-movement' is non-nil."
-  (when image-dired-track-movement
-    (image-dired-track-original-file))
-  (image-dired-toggle-mark-thumb-original-file))
-
-(defun image-dired-mouse-toggle-mark (event)
-  "Use mouse EVENT to toggle Dired mark for thumbnail.
-Toggle marks of all thumbnails in region, if it's active.
-Track this in associated Dired buffer if
-`image-dired-track-movement' is non-nil."
-  (interactive "e")
-  (if (use-region-p)
-      (let ((end (region-end)))
-        (save-excursion
-          (goto-char (region-beginning))
-          (while (<= (point) end)
-            (when (image-dired-image-at-point-p)
-              (image-dired-mouse-toggle-mark-1))
-            (forward-char))))
-    (mouse-set-point event)
-    (goto-char (posn-point (event-end event)))
-    (image-dired-mouse-toggle-mark-1))
-  (image-dired-thumb-update-marks))
-
-(defun image-dired-dired-display-properties ()
-  "Display properties for Dired file in the echo area."
-  (interactive)
-  (let* ((file (dired-get-filename))
-         (file-name (file-name-nondirectory file))
-         (dired-buf (buffer-name (current-buffer)))
-         (props (mapconcat #'identity (image-dired-list-tags file) ", "))
-         (comment (image-dired-get-comment file))
-         (message-log-max nil))
-    (if file-name
-        (message "%s"
-         (image-dired-format-properties-string
-          dired-buf
-          file-name
-          props
-          comment)))))
-
-
-
-;;; Gallery support
-
-;; TODO:
-;; * Support gallery creation when using per-directory thumbnail
-;;   storage.
-;; * Enhanced gallery creation with basic CSS-support and pagination
-;;   of tag pages with many pictures.
-
-(defgroup image-dired-gallery nil
-  "Image-Dired support for generating a HTML gallery."
-  :prefix "image-dired-"
-  :group 'image-dired
-  :version "29.1")
-
-(defcustom image-dired-gallery-dir
-  (expand-file-name ".image-dired_gallery" image-dired-dir)
-  "Directory to store generated gallery html pages.
-The name of this directory needs to be \"shared\" to the public
-so that it can access the index.html page that image-dired creates."
-  :type 'directory)
-
-(defcustom image-dired-gallery-image-root-url
-  "https://example.org/image-diredpics";
-  "URL where the full size images are to be found on your web server.
-Note that this URL has to be configured on your web server.
-Image-Dired expects to find pictures in this directory.
-This is used by `image-dired-gallery-generate'."
-  :type 'string
-  :version "29.1")
-
-(defcustom image-dired-gallery-thumb-image-root-url
-  "https://example.org/image-diredthumbs";
-  "URL where the thumbnail images are to be found on your web server.
-Note that URL path has to be configured on your web server.
-Image-Dired expects to find pictures in this directory.
-This is used by `image-dired-gallery-generate'."
-  :type 'string
-  :version "29.1")
-
-(defcustom image-dired-gallery-hidden-tags
-  (list "private" "hidden" "pending")
-  "List of \"hidden\" tags.
-Used by `image-dired-gallery-generate' to leave out \"hidden\" images."
-  :type '(repeat string))
-
-(defvar image-dired-tag-file-list nil
-  "List to store tag-file structure.")
-
-(defvar image-dired-file-tag-list nil
-  "List to store file-tag structure.")
-
-(defvar image-dired-file-comment-list nil
-  "List to store file comments.")
-
-(defun image-dired--add-to-tag-file-lists (tag file)
-  "Helper function used from `image-dired--create-gallery-lists'.
-
-Add TAG to FILE in one list and FILE to TAG in the other.
-
-Lisp structures look like the following:
-
-image-dired-file-tag-list:
-
-  ((\"filename1\" \"tag1\" \"tag2\" \"tag3\" ...)
-   (\"filename2\" \"tag1\" \"tag2\" \"tag3\" ...)
-   ...)
-
-image-dired-tag-file-list:
-
- ((\"tag1\" \"filename1\" \"filename2\" \"filename3\" ...)
-  (\"tag2\" \"filename1\" \"filename2\" \"filename3\" ...)
-  ...)"
-  ;; Add tag to file list
-  (let (curr)
-    (if image-dired-file-tag-list
-        (if (setq curr (assoc file image-dired-file-tag-list))
-            (setcdr curr (cons tag (cdr curr)))
-          (setcdr image-dired-file-tag-list
-                  (cons (list file tag) (cdr image-dired-file-tag-list))))
-      (setq image-dired-file-tag-list (list (list file tag))))
-    ;; Add file to tag list
-    (if image-dired-tag-file-list
-        (if (setq curr (assoc tag image-dired-tag-file-list))
-            (if (not (member file curr))
-                (setcdr curr (cons file (cdr curr))))
-          (setcdr image-dired-tag-file-list
-                  (cons (list tag file) (cdr image-dired-tag-file-list))))
-      (setq image-dired-tag-file-list (list (list tag file))))))
-
-(defun image-dired--add-to-file-comment-list (file comment)
-  "Helper function used from `image-dired--create-gallery-lists'.
-
-For FILE, add COMMENT to list.
-
-Lisp structure looks like the following:
-
-image-dired-file-comment-list:
-
-  ((\"filename1\" .  \"comment1\")
-   (\"filename2\" .  \"comment2\")
-   ...)"
-  (if image-dired-file-comment-list
-      (if (not (assoc file image-dired-file-comment-list))
-          (setcdr image-dired-file-comment-list
-                  (cons (cons file comment)
-                        (cdr image-dired-file-comment-list))))
-    (setq image-dired-file-comment-list (list (cons file comment)))))
-
-(defun image-dired--create-gallery-lists ()
-  "Create temporary lists used by `image-dired-gallery-generate'."
-  (image-dired-sane-db-file)
-  (image-dired--with-db-file
-   (let (end beg file row-tags)
-     (setq image-dired-tag-file-list nil)
-     (setq image-dired-file-tag-list nil)
-     (setq image-dired-file-comment-list nil)
-     (goto-char (point-min))
-     (while (search-forward-regexp "^." nil t)
-       (end-of-line)
-       (setq end (point))
-       (beginning-of-line)
-       (setq beg (point))
-       (unless (search-forward ";" end nil)
-        (error "Something is really wrong, check format of database"))
-       (setq row-tags (split-string
-                      (buffer-substring beg end) ";"))
-       (setq file (car row-tags))
-       (dolist (x (cdr row-tags))
-        (if (not (string-match "^comment:\\(.*\\)" x))
-             (image-dired--add-to-tag-file-lists x file)
-           (image-dired--add-to-file-comment-list file (match-string 1 x)))))))
-  ;; Sort tag-file list
-  (setq image-dired-tag-file-list
-        (sort image-dired-tag-file-list
-              (lambda (x y)
-                (string< (car x) (car y))))))
-
-(defun image-dired--hidden-p (file)
-  "Return t if image FILE has a \"hidden\" tag."
-  (cl-loop for tag in (cdr (assoc file image-dired-file-tag-list))
-           if (member tag image-dired-gallery-hidden-tags) return t))
-
-(defun image-dired-gallery-generate ()
-  "Generate gallery pages.
-First we create a couple of Lisp structures from the database to make
-it easier to generate, then HTML-files are created in
-`image-dired-gallery-dir'."
-  (interactive)
-  (if (eq 'per-directory image-dired-thumbnail-storage)
-      (error "Currently, gallery generation is not supported \
-when using per-directory thumbnail file storage"))
-  (image-dired--create-gallery-lists)
-  (let ((tags image-dired-tag-file-list)
-       (index-file (format "%s/index.html" image-dired-gallery-dir))
-        count tag tag-file
-        comment file-tags tag-link tag-link-list)
-    ;; Make sure gallery root exist
-    (if (file-exists-p image-dired-gallery-dir)
-        (if (not (file-directory-p image-dired-gallery-dir))
-            (error "Variable image-dired-gallery-dir is not a directory"))
-      ;; FIXME: Should we set umask to 077 here, as we do for thumbnails?
-      (make-directory image-dired-gallery-dir))
-    ;; Open index file
-    (with-temp-file index-file
-      (if (file-exists-p index-file)
-         (insert-file-contents index-file))
-      (insert "<html>\n")
-      (insert "  <body>\n")
-      (insert "   <h2>Image-Dired Gallery</h2>\n")
-      (insert (format "<p>\n    Gallery generated %s\n   <p>\n"
-                     (current-time-string)))
-      (insert "   <h3>Tag index</h3>\n")
-      (setq count 1)
-      ;; Pre-generate list of all tag links
-      (dolist (curr tags)
-       (setq tag (car curr))
-       (when (not (member tag image-dired-gallery-hidden-tags))
-         (setq tag-link (format "<a href=\"%d.html\">%s</a>" count tag))
-         (if tag-link-list
-             (setq tag-link-list
-                   (append tag-link-list (list (cons tag tag-link))))
-           (setq tag-link-list (list (cons tag tag-link))))
-         (setq count (1+ count))))
-      (setq count 1)
-      ;; Main loop where we generated thumbnail pages per tag
-      (dolist (curr tags)
-       (setq tag (car curr))
-       ;; Don't display hidden tags
-       (when (not (member tag image-dired-gallery-hidden-tags))
-         ;; Insert link to tag page in index
-         (insert (format "    %s<br>\n" (cdr (assoc tag tag-link-list))))
-         ;; Open per-tag file
-         (setq tag-file (format "%s/%s.html" image-dired-gallery-dir count))
-         (with-temp-file tag-file
-           (if (file-exists-p tag-file)
-               (insert-file-contents tag-file))
-           (erase-buffer)
-           (insert "<html>\n")
-           (insert "  <body>\n")
-           (insert "  <p><a href=\"index.html\">Index</a></p>\n")
-           (insert (format "  <h2>Images with tag &quot;%s&quot;</h2>" tag))
-           ;; Main loop for files per tag page
-           (dolist (file (cdr curr))
-             (unless (image-dired-hidden-p file)
-               ;; Insert thumbnail with link to full image
-               (insert
-                (format "<a href=\"%s/%s\"><img src=\"%s/%s\"%s></a>\n"
-                        image-dired-gallery-image-root-url
-                        (file-name-nondirectory file)
-                        image-dired-gallery-thumb-image-root-url
-                        (file-name-nondirectory (image-dired-thumb-name file)) 
file))
-               ;; Insert comment, if any
-               (if (setq comment (cdr (assoc file 
image-dired-file-comment-list)))
-                   (insert (format "<br>\n%s<br>\n" comment))
-                 (insert "<br>\n"))
-               ;; Insert links to other tags, if any
-               (when (> (length
-                         (setq file-tags (assoc file 
image-dired-file-tag-list))) 2)
-                 (insert "[ ")
-                 (dolist (extra-tag file-tags)
-                   ;; Only insert if not file name or the main tag
-                   (if (and (not (equal extra-tag tag))
-                            (not (equal extra-tag file)))
-                       (insert
-                        (format "%s " (cdr (assoc extra-tag tag-link-list))))))
-                 (insert "]<br>\n"))))
-           (insert "  <p><a href=\"index.html\">Index</a></p>\n")
-           (insert "  </body>\n")
-           (insert "</html>\n"))
-         (setq count (1+ count))))
-      (insert "  </body>\n")
-      (insert "</html>"))))
-
-
-;;; Tag support
-
-(defvar image-dired-widget-list nil
-  "List to keep track of meta data in edit buffer.")
-
-(declare-function widget-forward "wid-edit" (arg))
-
-;;;###autoload
-(defun image-dired-dired-edit-comment-and-tags ()
-  "Edit comment and tags of current or marked image files.
-Edit comment and tags for all marked image files in an
-easy-to-use form."
-  (interactive)
-  (setq image-dired-widget-list nil)
-  ;; Setup buffer.
-  (let ((files (dired-get-marked-files)))
-    (pop-to-buffer-same-window "*Image-Dired Edit Meta Data*")
-    (kill-all-local-variables)
-    (let ((inhibit-read-only t))
-      (erase-buffer))
-    (remove-overlays)
-    ;; Some help for the user.
-    (widget-insert
-"\nEdit comments and tags for each image.  Separate multiple tags
-with a comma.  Move forward between fields using TAB or RET.
-Move to the previous field using backtab (S-TAB).  Save by
-activating the Save button at the bottom of the form or cancel
-the operation by activating the Cancel button.\n\n")
-    ;; Here comes all images and a comment and tag field for each
-    ;; image.
-    (let (thumb-file img comment-widget tag-widget)
-
-      (dolist (file files)
-
-       (setq thumb-file (image-dired-thumb-name file)
-             img (create-image thumb-file))
-
-       (insert-image img)
-       (widget-insert "\n\nComment: ")
-       (setq comment-widget
-             (widget-create 'editable-field
-                            :size 60
-                            :format "%v "
-                            :value (or (image-dired-get-comment file) "")))
-       (widget-insert "\nTags:    ")
-       (setq tag-widget
-             (widget-create 'editable-field
-                            :size 60
-                            :format "%v "
-                            :value (or (mapconcat
-                                        #'identity
-                                        (image-dired-list-tags file)
-                                        ",") "")))
-       ;; Save information in all widgets so that we can use it when
-       ;; the user saves the form.
-       (setq image-dired-widget-list
-             (append image-dired-widget-list
-                     (list (list file comment-widget tag-widget))))
-       (widget-insert "\n\n")))
-
-    ;; Footer with Save and Cancel button.
-    (widget-insert "\n")
-    (widget-create 'push-button
-                 :notify
-                 (lambda (&rest _ignore)
-                   (image-dired-save-information-from-widgets)
-                   (bury-buffer)
-                   (message "Done"))
-                 "Save")
-    (widget-insert " ")
-    (widget-create 'push-button
-                   :notify
-                   (lambda (&rest _ignore)
-                     (bury-buffer)
-                     (message "Operation canceled"))
-                   "Cancel")
-    (widget-insert "\n")
-    (use-local-map widget-keymap)
-    (widget-setup)
-    ;; Jump to the first widget.
-    (widget-forward 1)))
-
-(defun image-dired-save-information-from-widgets ()
-  "Save information found in `image-dired-widget-list'.
-Use the information in `image-dired-widget-list' to save comments and
-tags to their respective image file.  Internal function used by
-`image-dired-dired-edit-comment-and-tags'."
-  (let (file comment tag-string tag-list lst)
-    (image-dired-write-comments
-          (mapcar
-           (lambda (widget)
-             (setq file (car widget)
-                   comment (widget-value (cadr widget)))
-             (cons file comment))
-           image-dired-widget-list))
-    (image-dired-write-tags
-     (dolist (widget image-dired-widget-list lst)
-       (setq file (car widget)
-             tag-string (widget-value (car (cddr widget)))
-             tag-list (split-string tag-string ","))
-       (dolist (tag tag-list)
-         (push (cons file tag) lst))))))
-
-
-;;; bookmark.el support
-
-(declare-function bookmark-make-record-default
-                  "bookmark" (&optional no-file no-context posn))
-(declare-function bookmark-prop-get "bookmark" (bookmark prop))
-
-(defun image-dired-bookmark-name ()
-  "Create a default bookmark name for the current EWW buffer."
-  (file-name-nondirectory
-   (directory-file-name
-    (file-name-directory (image-dired-original-file-name)))))
-
-(defun image-dired-bookmark-make-record ()
-  "Create a bookmark for the current EWW buffer."
-  `(,(image-dired-bookmark-name)
-    ,@(bookmark-make-record-default t)
-    (location . ,(file-name-directory (image-dired-original-file-name)))
-    (image-dired-file . ,(file-name-nondirectory 
(image-dired-original-file-name)))
-    (handler . image-dired-bookmark-jump)))
-
-;;;###autoload
-(defun image-dired-bookmark-jump (bookmark)
-  "Default bookmark handler for Image-Dired buffers."
-  ;; User already cached thumbnails, so disable any checking.
-  (let ((image-dired-show-all-from-dir-max-files nil))
-    (image-dired (bookmark-prop-get bookmark 'location))
-    ;; TODO: Go to the bookmarked file, if it exists.
-    ;; (bookmark-prop-get bookmark 'image-dired-file)
-    (goto-char (point-min))))
-
-(put 'image-dired-bookmark-jump 'bookmark-handler-type "Image-Dired")
-
-;;; Obsolete
-
-;;;###autoload
-(define-obsolete-function-alias 'tumme #'image-dired "24.4")
-
-;;;###autoload
-(define-obsolete-function-alias 'image-dired-setup-dired-keybindings
-  #'image-dired-minor-mode "26.1")
-
-(defcustom image-dired-temp-image-file
-  (expand-file-name ".image-dired_temp" image-dired-dir)
-  "Name of temporary image file used by various commands."
-  :type 'file)
-(make-obsolete-variable 'image-dired-temp-image-file
-                        "no longer used." "29.1")
-
-(defcustom image-dired-cmd-create-temp-image-program
-  (if (executable-find "gm") "gm" "convert")
-  "Executable used to create temporary image.
-Used together with `image-dired-cmd-create-temp-image-options'."
-  :type 'file
-  :version "29.1")
-(make-obsolete-variable 'image-dired-cmd-create-temp-image-program
-                        "no longer used." "29.1")
-
-(defcustom image-dired-cmd-create-temp-image-options
-  (let ((opts '("-size" "%wx%h" "%f[0]"
-                "-resize" "%wx%h>"
-                "-strip" "jpeg:%t")))
-    (if (executable-find "gm") (cons "convert" opts) opts))
-  "Options of command used to create temporary image for display window.
-Used together with `image-dired-cmd-create-temp-image-program',
-Available format specifiers are: %w and %h which are replaced by
-the calculated max size for width and height in the image display window,
-%f which is replaced by the file name of the original image and %t which
-is replaced by the file name of the temporary file."
-  :version "29.1"
-  :type '(repeat (string :tag "Argument")))
-(make-obsolete-variable 'image-dired-cmd-create-temp-image-options
-                        "no longer used." "29.1")
-
-(defcustom image-dired-display-window-width-correction 1
-  "Number to be used to correct image display window width.
-Change if the default (1) does not work (i.e. if the image does not
-completely fit)."
-  :type 'integer)
-(make-obsolete-variable 'image-dired-display-window-width-correction
-                        "no longer used." "29.1")
-
-(defcustom image-dired-display-window-height-correction 0
-  "Number to be used to correct image display window height.
-Change if the default (0) does not work (i.e. if the image does not
-completely fit)."
-  :type 'integer)
-(make-obsolete-variable 'image-dired-display-window-height-correction
-                        "no longer used." "29.1")
-
-(defun image-dired-display-window-width (window)
-  "Return width, in pixels, of WINDOW."
-  (declare (obsolete nil "29.1"))
-  (- (image-dired-window-width-pixels window)
-     image-dired-display-window-width-correction))
-
-(defun image-dired-display-window-height (window)
-  "Return height, in pixels, of WINDOW."
-  (declare (obsolete nil "29.1"))
-  (- (image-dired-window-height-pixels window)
-     image-dired-display-window-height-correction))
-
-(defun image-dired-window-height-pixels (window)
-  "Calculate WINDOW height in pixels."
-  (declare (obsolete nil "29.1"))
-  ;; Note: The mode-line consumes one line
-    (* (- (window-height window) 1) (frame-char-height)))
-
-(defcustom image-dired-cmd-read-exif-data-program "exiftool"
-  "Program used to read EXIF data to image.
-Used together with `image-dired-cmd-read-exif-data-options'."
-  :type 'file)
-(make-obsolete-variable 'image-dired-cmd-read-exif-data-program
-                        "use `exif-parse-file' and `exif-field' instead." 
"29.1")
-
-(defcustom image-dired-cmd-read-exif-data-options '("-s" "-s" "-s" "-%t" "%f")
-  "Arguments of command used to read EXIF data.
-Used with `image-dired-cmd-read-exif-data-program'.
-Available format specifiers are: %f which is replaced
-by the image file name and %t which is replaced by the tag name."
-  :version "26.1"
-  :type '(repeat (string :tag "Argument")))
-(make-obsolete-variable 'image-dired-cmd-read-exif-data-options
-                        "use `exif-parse-file' and `exif-field' instead." 
"29.1")
-
-(defun image-dired-get-exif-data (file tag-name)
-  "From FILE, return EXIF tag TAG-NAME."
-  (declare (obsolete "use `exif-parse-file' and `exif-field' instead."  
"29.1"))
-  (image-dired--check-executable-exists
-   'image-dired-cmd-read-exif-data-program)
-  (let ((buf (get-buffer-create "*image-dired-get-exif-data*"))
-        (spec (list (cons ?f file) (cons ?t tag-name)))
-        tag-value)
-    (with-current-buffer buf
-      (delete-region (point-min) (point-max))
-      (if (not (eq (apply #'call-process image-dired-cmd-read-exif-data-program
-                          nil t nil
-                          (mapcar
-                           (lambda (arg) (format-spec arg spec))
-                           image-dired-cmd-read-exif-data-options))
-                   0))
-          (error "Could not get EXIF tag")
-        (goto-char (point-min))
-        ;; Clean buffer from newlines and carriage returns before
-        ;; getting final info
-        (while (search-forward-regexp "[\n\r]" nil t)
-          (replace-match "" nil t))
-        (setq tag-value (buffer-substring (point-min) (point-max)))))
-    tag-value))
-
-(defcustom image-dired-cmd-rotate-thumbnail-program
-  (if (executable-find "gm") "gm" "mogrify")
-  "Executable used to rotate thumbnail.
-Used together with `image-dired-cmd-rotate-thumbnail-options'."
-  :type 'file
-  :version "29.1")
-(make-obsolete-variable 'image-dired-cmd-rotate-thumbnail-program nil "29.1")
-
-(defcustom image-dired-cmd-rotate-thumbnail-options
-  (let ((opts '("-rotate" "%d" "%t")))
-    (if (executable-find "gm") (cons "mogrify" opts) opts))
-  "Arguments of command used to rotate thumbnail image.
-Used with `image-dired-cmd-rotate-thumbnail-program'.
-Available format specifiers are: %d which is replaced by the
-number of (positive) degrees to rotate the image, normally 90 or 270
-\(for 90 degrees right and left), %t which is replaced by the file name
-of the thumbnail file."
-  :version "29.1"
-  :type '(repeat (string :tag "Argument")))
-(make-obsolete-variable 'image-dired-cmd-rotate-thumbnail-options nil "29.1")
-
-(defun image-dired-rotate-thumbnail (degrees)
-  "Rotate thumbnail DEGREES degrees."
-  (declare (obsolete image-dired-refresh-thumb "29.1"))
-  (image-dired--check-executable-exists
-   'image-dired-cmd-rotate-thumbnail-program)
-  (if (not (image-dired-image-at-point-p))
-      (message "No thumbnail at point")
-    (let* ((file (image-dired-thumb-name (image-dired-original-file-name)))
-           (thumb (expand-file-name file))
-           (spec (list (cons ?d degrees) (cons ?t thumb))))
-      (apply #'call-process image-dired-cmd-rotate-thumbnail-program nil nil 
nil
-             (mapcar (lambda (arg) (format-spec arg spec))
-                     image-dired-cmd-rotate-thumbnail-options))
-      (clear-image-cache thumb))))
-
-(defun image-dired-rotate-thumbnail-left ()
-  "Rotate thumbnail left (counter clockwise) 90 degrees."
-  (declare (obsolete image-dired-refresh-thumb "29.1"))
-  (interactive)
-  (with-suppressed-warnings ((obsolete image-dired-rotate-thumbnail))
-    (image-dired-rotate-thumbnail "270")))
-
-(defun image-dired-rotate-thumbnail-right ()
-  "Rotate thumbnail counter right (clockwise) 90 degrees."
-  (declare (obsolete image-dired-refresh-thumb "29.1"))
-  (interactive)
-  (with-suppressed-warnings ((obsolete image-dired-rotate-thumbnail))
-    (image-dired-rotate-thumbnail "90")))
-
-(defun image-dired-modify-mark-on-thumb-original-file (command)
-  "Modify mark in Dired buffer.
-COMMAND is one of `mark' for marking file in Dired, `unmark' for
-unmarking file in Dired or `flag' for flagging file for delete in
-Dired."
-  (declare (obsolete image-dired--on-file-in-dired-buffer "29.1"))
-  (let ((file-name (image-dired-original-file-name))
-        (dired-buf (image-dired-associated-dired-buffer)))
-    (if (not (and dired-buf file-name))
-        (message "No image, or image with correct properties, at point")
-    (with-current-buffer dired-buf
-        (message "%s" file-name)
-        (when (dired-goto-file file-name)
-          (cond ((eq command 'mark) (dired-mark 1))
-                ((eq command 'unmark) (dired-unmark 1))
-                ((eq command 'toggle)
-                 (if (image-dired-dired-file-marked-p)
-                     (dired-unmark 1)
-                   (dired-mark 1)))
-                ((eq command 'flag) (dired-flag-file-deletion 1)))
-          (image-dired-thumb-update-marks))))))
-
-(defun image-dired-display-current-image-full ()
-  "Display current image in full size."
-  (declare (obsolete image-transform-original "29.1"))
-  (interactive nil image-dired-thumbnail-mode)
-  (let ((file (image-dired-original-file-name)))
-    (if file
-        (progn
-          (image-dired-display-image file)
-          (with-current-buffer image-dired-display-image-buffer
-            (image-transform-original)))
-      (error "No original file name at point"))))
-
-(defun image-dired-display-current-image-sized ()
-  "Display current image in sized to fit window dimensions."
-  (declare (obsolete image-mode-fit-frame "29.1"))
-  (interactive nil image-dired-thumbnail-mode)
-  (let ((file (image-dired-original-file-name)))
-    (if file
-        (progn
-          (image-dired-display-image file))
-      (error "No original file name at point"))))
-
-(defun image-dired-add-to-tag-file-list (tag file)
-  "Add relation between TAG and FILE."
-  (declare (obsolete nil "29.1"))
-  (let (curr)
-    (if image-dired-tag-file-list
-        (if (setq curr (assoc tag image-dired-tag-file-list))
-            (if (not (member file curr))
-                (setcdr curr (cons file (cdr curr))))
-          (setcdr image-dired-tag-file-list
-                  (cons (list tag file) (cdr image-dired-tag-file-list))))
-      (setq image-dired-tag-file-list (list (list tag file))))))
-
-(defun image-dired-display-thumb-properties ()
-  "Display thumbnail properties in the echo area."
-  (declare (obsolete image-dired-update-header-line "29.1"))
-  (image-dired-update-header-line))
-
-(defvar image-dired-slideshow-count 0
-  "Keeping track on number of images in slideshow.")
-(make-obsolete-variable 'image-dired-slideshow-count "no longer used." "29.1")
-
-(defvar image-dired-slideshow-times 0
-  "Number of pictures to display in slideshow.")
-(make-obsolete-variable 'image-dired-slideshow-times "no longer used." "29.1")
-
-(define-obsolete-function-alias 'image-dired-create-display-image-buffer
-  #'ignore "29.1")
-(define-obsolete-function-alias 'image-dired-create-gallery-lists
-  #'image-dired--create-gallery-lists "29.1")
-(define-obsolete-function-alias 'image-dired-add-to-file-comment-list
-  #'image-dired--add-to-file-comment-list "29.1")
-(define-obsolete-function-alias 'image-dired-add-to-tag-file-lists
-  #'image-dired--add-to-tag-file-lists "29.1")
-(define-obsolete-function-alias 'image-dired-hidden-p
-  #'image-dired--hidden-p "29.1")
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;;;;;;;;; TEST-SECTION ;;;;;;;;;;;
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-;; (defvar image-dired-dir-max-size 12300000)
-
-;; (defun image-dired-test-clean-old-files ()
-;;   "Clean `image-dired-dir' from old thumbnail files.
-;; \"Oldness\" measured using last access time.  If the total size of all
-;; thumbnail files in `image-dired-dir' is larger than 
'image-dired-dir-max-size',
-;; old files are deleted until the max size is reached."
-;;   (let* ((files
-;;           (sort
-;;            (mapcar
-;;             (lambda (f)
-;;               (let ((fattribs (file-attributes f)))
-;;                 `(,(file-attribute-access-time fattribs)
-;;                   ,(file-attribute-size fattribs) ,f)))
-;;             (directory-files (image-dired-dir) t ".+\\.thumb\\..+$"))
-;;            ;; Sort function. Compare time between two files.
-;;            (lambda (l1 l2)
-;;               (time-less-p (car l1) (car l2)))))
-;;          (dirsize (apply '+ (mapcar (lambda (x) (cadr x)) files))))
-;;     (while (> dirsize image-dired-dir-max-size)
-;;       (y-or-n-p
-;;        (format "Size of thumbnail directory: %d, delete old file %s? "
-;;                dirsize (cadr (cdar files))))
-;;       (delete-file (cadr (cdar files)))
-;;       (setq dirsize (- dirsize (car (cdar files))))
-;;       (setq files (cdr files)))))
-
-(provide 'image-dired)
-
-;;; image-dired.el ends here
diff --git a/lisp/image-file.el b/lisp/image-file.el
index 0ed88e8e74..63f9e1100c 100644
--- a/lisp/image-file.el
+++ b/lisp/image-file.el
@@ -91,9 +91,10 @@ the variable is set using \\[customize]."
                      "\\'"))))
     (mapconcat
      #'identity
-     (delq nil (list exts-regexp
-                    image-file-name-regexps
-                    (car (rassq 'imagemagick image-type-file-name-regexps))))
+     (delq nil
+           (nconc (list exts-regexp
+                        (car (rassq 'imagemagick 
image-type-file-name-regexps)))
+                 (ensure-list image-file-name-regexps)))
      "\\|")))
 
 
diff --git a/lisp/image-mode.el b/lisp/image-mode.el
index 9485f1e006..bd208fbad4 100644
--- a/lisp/image-mode.el
+++ b/lisp/image-mode.el
@@ -498,8 +498,8 @@ image as text, when opening such images in `image-mode'."
   "s s"     #'image-transform-set-scale
   "s r"     #'image-transform-set-rotation
   "s m"     #'image-transform-set-smoothing
-  "s o"     #'image-transform-original
-  "s 0"     #'image-transform-reset
+  "s o"     #'image-transform-reset-to-original
+  "s 0"     #'image-transform-reset-to-initial
 
   ;; Multi-frame keys
   "RET"     #'image-toggle-animation
@@ -523,6 +523,9 @@ image as text, when opening such images in `image-mode'."
   "S-SPC"   #'image-scroll-down
   "DEL"     #'image-scroll-down
 
+  ;; Misc
+  "W"       #'image-mode-wallpaper-set
+
   ;; Remapped
   "<remap> <forward-char>"           #'image-forward-hscroll
   "<remap> <backward-char>"          #'image-backward-hscroll
@@ -567,9 +570,9 @@ image as text, when opening such images in `image-mode'."
      :help "Set rotation angle of the image"]
     ["Set Smoothing..." image-transform-set-smoothing
      :help "Toggle smoothing"]
-    ["Original Size" image-transform-original
+    ["Original Size" image-transform-reset-to-original
      :help "Reset image to actual size"]
-    ["Reset to Default Size" image-transform-reset
+    ["Reset to Default Size" image-transform-reset-to-initial
      :help "Reset all image transformations to initial size"]
     "--"
     ["Show Thumbnails"
@@ -663,6 +666,9 @@ Key bindings:
                      "(New file)")
                  "Empty buffer"))
     (image-mode--display)
+    (setq-local image-crop-buffer-text-function
+                ;; Use the binary image data directly for the buffer text.
+                (lambda (_text image) image))
     ;; Ensure that we recognize externally parsed image formats in
     ;; commands like `n'.
     (when image-use-external-converter
@@ -1051,7 +1057,13 @@ Otherwise, display the image by calling `image-mode'."
 
 (defun image-fit-to-window (window)
   "Adjust size of image to display it exactly in WINDOW boundaries."
-  (when (window-live-p window)
+  (when (and (window-live-p window)
+             ;; Don't resize anything if we're in the minibuffer
+             ;; (which may transitively change the window sizes if you
+             ;; hit TAB, for instance).
+             (not (minibuffer-window-active-p (selected-window)))
+             ;; Don't resize if there's a message in the echo area.
+             (not (current-message)))
     (with-current-buffer (window-buffer window)
       (when (derived-mode-p 'image-mode)
         (let ((spec (image-get-display-property)))
@@ -1262,7 +1274,7 @@ If N is negative, go to the previous file."
       (save-window-excursion
         (switch-to-buffer (cdr buffer) t t)
         (cl-case (car buffer)
-          ('dired
+          (dired
            (dired-goto-file file)
            (let (found)
              (while (and (not found)
@@ -1280,9 +1292,9 @@ If N is negative, go to the previous file."
                ;; If we didn't find a next/prev file, then restore
                ;; point.
                (dired-goto-file file))))
-          ('archive
+          (archive
            (setq next (archive-next-file-displayer file regexp n)))
-          ('tar
+          (tar
            (setq next (tar-next-file-displayer file regexp n))))))
     next))
 
@@ -1358,16 +1370,6 @@ If no such buffer exists, it will be opened."
       (message "%s%s" (capitalize (substring string 0 1))
                (substring string 1)))))
 
-(defun image-mode--images-in-directory (file)
-  (let* ((dir (file-name-directory buffer-file-name))
-        (files (directory-files dir nil
-                                (image-file-name-regexp) t)))
-    ;; Add the current file to the list of images if necessary, in
-    ;; case it does not match `image-file-name-regexp'.
-    (unless (member file files)
-      (push file files))
-    (sort files 'string-lessp)))
-
 
 ;;; Support for bookmark.el
 (declare-function bookmark-make-record-default
@@ -1387,7 +1389,18 @@ If no such buffer exists, it will be opened."
   (prog1 (bookmark-default-handler bmk)
     (when (not (string= image-type (bookmark-prop-get bmk 'image-type)))
       (image-toggle-display))))
+
 
+;;; Setting the wallpaper
+
+(defun image-mode-wallpaper-set ()
+  "Set the desktop background to the current image.
+This uses `wallpaper-set' (which see)."
+  (interactive nil image-mode)
+  (wallpaper-set buffer-file-name))
+
+
+;;; Image transformation
 
 (defsubst image-transform-width (width height)
   "Return the bounding box width of a rotated WIDTH x HEIGHT rectangle.
@@ -1593,14 +1606,14 @@ ROTATION should be in degrees."
   (setq image--transform-smoothing smoothing)
   (image-toggle-display-image))
 
-(defun image-transform-original ()
+(defun image-transform-reset-to-original ()
   "Display the current image with the original (actual) size and rotation."
   (interactive nil image-mode)
   (setq image-transform-resize nil
        image-transform-scale 1)
   (image-toggle-display-image))
 
-(defun image-transform-reset ()
+(defun image-transform-reset-to-initial ()
   "Display the current image with the default (initial) size and rotation."
   (interactive nil image-mode)
   (setq image-transform-resize image-auto-resize
@@ -1609,6 +1622,20 @@ ROTATION should be in degrees."
         image--transform-smoothing nil)
   (image-toggle-display-image))
 
+(defun image-mode--images-in-directory (file)
+  (declare (obsolete nil "29.1"))
+  (let* ((dir (file-name-directory buffer-file-name))
+         (files (directory-files dir nil
+                                 (image-file-name-regexp) t)))
+    ;; Add the current file to the list of images if necessary, in
+    ;; case it does not match `image-file-name-regexp'.
+    (unless (member file files)
+      (push file files))
+    (sort files 'string-lessp)))
+
+(define-obsolete-function-alias 'image-transform-original 
#'image-transform-reset-to-original "29.1")
+(define-obsolete-function-alias 'image-transform-reset 
#'image-transform-reset-to-initial "29.1")
+
 (provide 'image-mode)
 
 ;;; image-mode.el ends here
diff --git a/lisp/image.el b/lisp/image.el
index 9311125450..b6817d3fda 100644
--- a/lisp/image.el
+++ b/lisp/image.el
@@ -174,12 +174,15 @@ or \"ffmpeg\") is installed."
 
 (defvar-keymap image-map
   :doc "Map put into text properties on images."
-  "-" #'image-decrease-size
-  "+" #'image-increase-size
-  "r" #'image-rotate
-  "o" #'image-save
-  "h" #'image-flip-horizontally
-  "v" #'image-flip-vertically
+  "i" (define-keymap
+        "-" #'image-decrease-size
+        "+" #'image-increase-size
+        "r" #'image-rotate
+        "o" #'image-save
+        "c" #'image-crop
+        "x" #'image-cut
+        "h" #'image-flip-horizontally
+        "v" #'image-flip-vertically)
   "C-<wheel-down>" #'image-mouse-decrease-size
   "C-<mouse-5>"    #'image-mouse-decrease-size
   "C-<wheel-up>"   #'image-mouse-increase-size
@@ -474,8 +477,9 @@ must be available."
 
 ;;;###autoload
 (defun create-image (file-or-data &optional type data-p &rest props)
-  "Create an image.
-FILE-OR-DATA is an image file name or image data.
+  "Create an image from FILE-OR-DATA.
+FILE-OR-DATA is an image file name or image data.  If it is a relative
+file name, the function will look for it along `image-load-path'.
 
 Optional TYPE is a symbol describing the image type.  If TYPE is omitted
 or nil, try to determine the image type from its first few bytes
@@ -492,11 +496,7 @@ automatically scaled up in proportion to the default font.
 
 Value is the image created, or nil if images of type TYPE are not supported.
 
-Images should not be larger than specified by `max-image-size'.
-
-Image file names that are not absolute are searched for in the
-\"images\" sub-directory of `data-directory' and
-`x-bitmap-file-path' (in that order)."
+Images should not be larger than specified by `max-image-size'."
   (let ((data-format
          ;; Pass the image format, if any, if this is data.
          (and data-p (or (plist-get props :format) t))))
@@ -1152,41 +1152,43 @@ has no effect."
 
 (imagemagick-register-types)
 
+(defvar-keymap image--repeat-map
+  "+" #'image-increase-size
+  "-" #'image-decrease-size
+  "r" #'image-rotate)
+
 (defun image-increase-size (&optional n position)
   "Increase the image size by a factor of N.
 If N is 3, then the image size will be increased by 30%.  The
 default is 20%."
   (interactive "P")
+  (image--delayed-change-size (if n
+                                  (1+ (/ (prefix-numeric-value n) 10.0))
+                                1.2)
+                              position)
+  (set-transient-map image--repeat-map nil nil
+                     "Use %k for further adjustments"))
+
+(defun image--delayed-change-size (size position)
   ;; Wait for a bit of idle-time before actually performing the change,
   ;; so as to batch together sequences of closely consecutive size changes.
   ;; `image--change-size' just changes one value in a plist.  The actual
   ;; image resizing happens later during redisplay.  So if those
   ;; consecutive calls happen without any redisplay between them,
   ;; the costly operation of image resizing should happen only once.
-  (run-with-idle-timer 0.3 nil
-                       #'image--change-size
-                       (if n
-                           (1+ (/ (prefix-numeric-value n) 10.0))
-                         1.2)
-                       position))
+  (run-with-idle-timer 0.3 nil #'image--change-size size position))
 
 (defun image-decrease-size (&optional n position)
   "Decrease the image size by a factor of N.
 If N is 3, then the image size will be decreased by 30%.  The
 default is 20%."
   (interactive "P")
-  ;; Wait for a bit of idle-time before actually performing the change,
-  ;; so as to batch together sequences of closely consecutive size changes.
-  ;; `image--change-size' just changes one value in a plist.  The actual
-  ;; image resizing happens later during redisplay.  So if those
-  ;; consecutive calls happen without any redisplay between them,
-  ;; the costly operation of image resizing should happen only once.
-  (run-with-idle-timer 0.3 nil
-                       #'image--change-size
-                       (if n
-                           (- 1 (/ (prefix-numeric-value n) 10.0))
-                         0.8)
-                       position))
+  (image--delayed-change-size (if n
+                                  (- 1 (/ (prefix-numeric-value n) 10.0))
+                                0.8)
+                              position)
+  (set-transient-map image--repeat-map nil nil
+                     "Use %k for further adjustments"))
 
 (defun image-mouse-increase-size (&optional event)
   "Increase the image size using the mouse."
@@ -1271,7 +1273,9 @@ rotations by only multiples of 90 degrees."
                          (or angle 90))
                       ;; We don't want to exceed 360 degrees rotation,
                       ;; because it's not seen as valid in Exif data.
-                      360)))))
+                      360))))
+  (set-transient-map image--repeat-map nil nil
+                     "Use %k for further adjustments"))
 
 (defun image-save ()
   "Save the image under point.
diff --git a/lisp/image/exif.el b/lisp/image/exif.el
index b25968af53..53d2074ed7 100644
--- a/lisp/image/exif.el
+++ b/lisp/image/exif.el
@@ -271,13 +271,13 @@ VALUE is an integer representing BYTES characters."
   "Do type-based post-processing of the value."
   (cl-case type
     ;; Chop off trailing zero byte.
-    ('ascii (substring value 0 (1- (length value))))
-    ('rational (with-temp-buffer
-                 (set-buffer-multibyte nil)
-                 (insert value)
-                 (goto-char (point-min))
-                 (cons (exif--read-number 4 le)
-                       (exif--read-number 4 le))))
+    (ascii (substring value 0 (1- (length value))))
+    (rational (with-temp-buffer
+                (set-buffer-multibyte nil)
+                (insert value)
+                (goto-char (point-min))
+                (cons (exif--read-number 4 le)
+                      (exif--read-number 4 le))))
     (otherwise value)))
 
 (defun exif--read-chunk (bytes)
diff --git a/lisp/image/image-crop.el b/lisp/image/image-crop.el
new file mode 100644
index 0000000000..7fbbf85f93
--- /dev/null
+++ b/lisp/image/image-crop.el
@@ -0,0 +1,452 @@
+;;; image-crop.el --- Image Cropping  -*- lexical-binding: t -*-
+
+;; Copyright (C) 2022 Free Software Foundation, Inc.
+
+;; Keywords: multimedia
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; This package provides an interface for cropping images
+;; interactively, but relies on external programs to do the actual
+;; modifications to files.
+
+;;; Code:
+
+(require 'svg)
+(require 'text-property-search)
+(eval-when-compile (require 'subr-x))
+
+(defvar image-scaling-factor)
+(declare-function image-property "image.el" (image property))
+(declare-function image-size "image.c" (spec &optional pixels frame))
+(declare-function imagep "image.c" (spec))
+
+(defgroup image-crop ()
+  "Image cropping."
+  :group 'image)
+
+(defvar image-crop-exif-rotate nil
+  "If non-nil, rotate images by updating exif data.
+If nil, rotate the images \"physically\".")
+
+(defcustom image-crop-resize-command '("convert" "-resize" "%wx" "-" "%f:-")
+  "Command to resize an image.
+The following `format-spec' elements are allowed:
+
+%w: Width.
+%f: Result file type."
+  :type '(repeat string)
+  :version "29.1")
+
+(defcustom image-crop-cut-command '("convert" "-draw" "rectangle %l,%t %r,%b"
+                                    "-fill" "%c"
+                                    "-" "%f:-")
+  "Command to cut a rectangle out of an image.
+
+The following `format-spec' elements are allowed:
+%l: Left.
+%t: Top.
+%r: Right.
+%b: Bottom.
+%c: Color.
+%f: Result file type."
+  :type '(repeat string)
+  :version "29.1")
+
+(defcustom image-crop-crop-command '("convert" "+repage" "-crop" "%wx%h+%l+%t"
+                                    "-" "%f:-")
+  "Command to crop an image.
+
+The following `format-spec' elements are allowed:
+%l: Left.
+%t: Top.
+%w: Width.
+%h: Height.
+%f: Result file type."
+  :type '(repeat string)
+  :version "29.1")
+
+(defcustom image-crop-rotate-command '("convert" "-rotate" "%r" "-" "%f:-")
+  "Command to rotate an image.
+
+The following `format-spec' elements are allowed:
+%r: Rotation (in degrees).
+%f: Result file type."
+  :type '(repeat string)
+  :version "29.1")
+
+(defvar image-crop-buffer-text-function #'image-crop--default-buffer-text
+  "Function to return the buffer text for the cropped image.
+After cropping an image, the displayed image will be updated to
+show the cropped image in the buffer.  Different modes will have
+different ways to represent this image data in a buffer.  For
+instance, an HTML-based mode might want to represent the image
+with <img src=\"data:...base64...\">, but that's up to the mode.
+
+The default action is to not alter the buffer text at all.
+
+The function is called with two arguments: The first is the
+original buffer text, and the second parameter is the cropped
+image data.")
+
+(defcustom image-cut-color "black"
+  "Color to use for the rectangle cut from the image."
+  :type 'string
+  :version "29.1")
+
+;;;###autoload
+(defun image-cut (&optional color)
+  "Cut a rectangle from the image under point, filling it with COLOR.
+COLOR defaults to the value of `image-cut-color'.
+Interactively, with prefix argument, prompt for COLOR to use."
+  (interactive (list (and current-prefix-arg (read-color "Use color: "))))
+  (image-crop (if (zerop (length color)) image-cut-color color)))
+
+;;;###autoload
+(defun image-crop (&optional cut)
+  "Crop the image under point.
+If CUT is non-nil, remove a rectangle from the image instead of
+cropping the image.  In that case CUT should be the name of a
+color to fill the rectangle.
+
+While cropping the image, the following key bindings are available:
+
+`q':   Exit without changing anything.
+`RET': Crop/cut the image.
+`m':   Make mouse movements move the rectangle instead of altering the
+       rectangle shape.
+`s':   Same as `m', but make the rectangle into a square first.
+
+After cropping an image, you can save it by `M-x image-save' or
+\\<image-map>\\[image-save] when point is over the image."
+  (interactive)
+  (unless (image-type-available-p 'svg)
+    (error "SVG support is needed to crop images"))
+  (unless (executable-find (car image-crop-crop-command))
+    (error "Couldn't find %s command to crop the image"
+           (car image-crop-crop-command)))
+  (let ((image (get-text-property (point) 'display)))
+    (unless (imagep image)
+      (user-error "No image under point"))
+    (when (overlays-at (point))
+      (user-error "Can't edit images that have overlays"))
+    ;; We replace the image under point with an SVG image that looks
+    ;; just like that image.  That allows us to draw lines over it.
+    ;; At the end, we replace that SVG with a cropped version of the
+    ;; original image.
+    (let* ((data (cl-getf (cdr image) :data))
+          (undo-handle (prepare-change-group))
+          (type (cond
+                 ((cl-getf (cdr image) :format)
+                  (format "%s" (cl-getf (cdr image) :format)))
+                 (data
+                  (image-crop--content-type data))))
+          (image-scaling-factor 1)
+           (orig-point (point))
+          (size (image-size image t))
+          (svg (svg-create (car size) (cdr size)
+                           :xmlns:xlink "http://www.w3.org/1999/xlink";
+                           :stroke-width 5))
+           ;; We want to get the original text that's covered by the
+           ;; image so that we can restore it.
+           (image-start
+            (save-excursion
+              (let ((match (text-property-search-backward 'display image)))
+                (if match
+                    (prop-match-end match)
+                  (point-min)))))
+           (image-end
+            (save-excursion
+              (let ((match (text-property-search-forward 'display image)))
+                (if match
+                    (prop-match-beginning match)
+                  (point-max)))))
+          (text (buffer-substring image-start image-end))
+          (inhibit-read-only t)
+           orig-data svg-end)
+      (with-temp-buffer
+       (set-buffer-multibyte nil)
+       (if (null data)
+           (insert-file-contents-literally (cl-getf (cdr image) :file))
+         (insert data))
+       (let ((image-crop-exif-rotate nil))
+         (image-crop--possibly-rotate-buffer image))
+       (setq orig-data (buffer-string))
+       (setq type (image-crop--content-type orig-data))
+        (image-crop--process image-crop-resize-command
+                             `((?w . 600)
+                               (?f . ,(cadr (split-string type "/")))))
+       (setq data (buffer-string)))
+      (svg-embed svg data type t
+                :width (car size)
+                :height (cdr size))
+      (with-buffer-unmodified-if-unchanged
+        (delete-region image-start image-end)
+        (svg-insert-image svg)
+        (setq svg-end (point))
+        (let ((area (condition-case _
+                       (save-excursion
+                         (forward-line 1)
+                         (image-crop--crop-image-1
+                           svg (if cut "cut" "crop")))
+                      (quit nil))))
+          (message (substitute-command-keys
+                    "Type \\[image-save] to save %s image to file")
+                   (if cut "cut" "cropped"))
+         (delete-region image-start svg-end)
+         (if area
+             (image-crop--crop-image-update
+               area orig-data size type cut text)
+           ;; If the user didn't complete the crop, re-insert the
+           ;; original image (and text).
+           (insert text)
+            (goto-char orig-point))
+         (undo-amalgamate-change-group undo-handle))))))
+
+(defun image-crop--crop-image-update (area data size type cut text)
+  (let* ((image-scaling-factor 1)
+        (osize (image-size (create-image data nil t) t))
+        (factor (/ (float (car osize)) (car size)))
+        ;; width x height + left + top
+        (width (abs (truncate (* factor (- (cl-getf area :right)
+                                           (cl-getf area :left))))))
+        (height (abs (truncate (* factor (- (cl-getf area :bottom)
+                                            (cl-getf area :top))))))
+        (left (truncate (* factor (min (cl-getf area :left)
+                                       (cl-getf area :right)))))
+        (top (truncate (* factor (min (cl-getf area :top)
+                                      (cl-getf area :bottom))))))
+    (image-crop--insert-image-data
+     (with-temp-buffer
+       (set-buffer-multibyte nil)
+       (insert data)
+       (if cut
+          (image-crop--process image-crop-cut-command
+                                `((?l . ,left)
+                                  (?t . ,top)
+                                  (?r . ,(+ left width))
+                                  (?b . ,(+ top height))
+                                  (?c . ,cut)
+                                  (?f . ,(cadr (split-string type "/")))))
+        (image-crop--process image-crop-crop-command
+                              `((?l . ,left)
+                                (?t . ,top)
+                                (?w . ,width)
+                                (?h . ,height)
+                                (?f . ,(cadr (split-string type "/"))))))
+       (buffer-string))
+     text)))
+
+(defun image-crop--width (area)
+  (- (plist-get area :right) (plist-get area :left)))
+
+(defun image-crop--height (area)
+  (- (plist-get area :bottom) (plist-get area :top)))
+
+(defun image-crop--crop-image-1 (svg op)
+  (track-mouse
+    (cl-loop
+     with prompt = (format
+                    (substitute-command-keys
+                     "Select area for %s (click \\`mouse-1' and drag)")
+                    op)
+     and state = 'begin
+     and area = (list :left 0
+                     :top 0
+                     :right 0
+                     :bottom 0)
+     and corner = nil
+     for event = (read-event prompt)
+     do (cond
+         ;; Go to "square" mode.
+         ((eql event ?s)
+          (setq state 'move-unclick
+                prompt (format "Move square for %s" op))
+          (let ((size (min (image-crop--width area) (image-crop--height 
area))))
+            (setf (plist-get area :right) (+ (plist-get area :left) size)
+                  (plist-get area :bottom) (+ (plist-get area :top) size))))
+         ;; Go to "move" move.
+         ((eql event ?m)
+          (setq state 'move-unclick
+                prompt (format "Move for %s" op)))
+         ;; We have a (relevant) mouse event.
+         ((and (consp event)
+               (consp (cadr event))
+               (nth 7 (cadr event))
+              ;; Only do things if point is over the SVG being
+              ;; tracked.
+               (eq (cl-getf (cdr (nth 7 (cadr event))) :type)
+                  'svg))
+         (let ((pos (nth 8 (cadr event))))
+           (cl-case state
+             (begin
+              (cond
+               ((eq (car event) 'down-mouse-1)
+                (setq state 'stretch
+                       prompt (format "Stretch to end point for %s" op))
+                (setf (cl-getf area :left) (car pos)
+                      (cl-getf area :top) (cdr pos)
+                      (cl-getf area :right) (car pos)
+                      (cl-getf area :bottom) (cdr pos)))))
+             (stretch
+              (cond
+               ((eq (car event) 'mouse-movement)
+                (setf (cl-getf area :right) (car pos)
+                      (cl-getf area :bottom) (cdr pos)))
+               ((memq (car event) '(mouse-1 drag-mouse-1))
+                (setq state 'corner
+                       prompt (format
+                               (substitute-command-keys
+                                (concat
+                                 "Type \\`RET' to %s, or click and drag "
+                                 "\\`mouse-1' to adjust corners"))
+                               op)))))
+             (corner
+              (cond
+               ((eq (car event) 'down-mouse-1)
+                ;; Find out what corner we're close to.
+                (setq corner (image-crop--find-corner
+                              area pos
+                              '((:left :top)
+                                (:left :bottom)
+                                (:right :top)
+                                (:right :bottom))))
+                (when corner
+                  (setq state 'adjust
+                         prompt (format
+                                 (substitute-command-keys
+                                  "Adjusting %s area (release \\`mouse-1' to 
confirm)")
+                                 op))))))
+             (adjust
+              (cond
+               ((memq (car event) '(mouse drag-mouse-1))
+                (setq state 'corner
+                       prompt (format "Choose corner to adjust area for %s" 
op)))
+               ((eq (car event) 'mouse-movement)
+                (setf (cl-getf area (car corner)) (car pos)
+                      (cl-getf area (cadr corner)) (cdr pos)))))
+             (move-unclick
+              (cond
+               ((eq (car event) 'down-mouse-1)
+                (setq state 'move-click
+                       prompt (format "Move for %s" op)))))
+             (move-click
+              (cond
+               ((eq (car event) 'mouse-movement)
+                (setf (cl-getf area :right)
+                       (+ (car pos) (image-crop--width area)))
+                 (setf (cl-getf area :left) (car pos))
+                 (setf (cl-getf area :bottom)
+                       (+ (cdr pos) (image-crop--height area)))
+                 (setf (cl-getf area :top) (cdr pos)))
+               ((memq (car event) '(mouse-1 drag-mouse-1))
+                (setq state 'move-unclick
+                       prompt (format "Click to move for %s" op)))))))))
+     do (svg-rectangle svg (cl-getf area :left) (cl-getf area :top)
+                       (image-crop--width area) (image-crop--height area)
+                       :stroke-color "red" :stroke-width 2
+                       :fill-opacity 0.3 :fill "black" :id "rect")
+     while (not (member event '(return ?q)))
+     finally (return (and (eq event 'return)
+                         area)))))
+
+(defun image-crop--find-corner (area pos corners)
+  (cl-loop for corner in corners
+          ;; We accept 10 pixels off.
+          when (and (< (- (car pos) 10)
+                       (cl-getf area (car corner))
+                       (+ (car pos) 10))
+                    (< (- (cdr pos) 10)
+                       (cl-getf area (cadr corner))
+                       (+ (cdr pos) 10)))
+          return corner))
+
+(defun image-crop--content-type (image)
+  ;; Get the MIME type by running "file" over it.
+  (with-temp-buffer
+    (set-buffer-multibyte nil)
+    (insert image)
+    (call-process-region (point-min) (point-max)
+                        "file" t (current-buffer) nil
+                        "--mime-type" "-")
+    (cadr (split-string (buffer-string)))))
+
+(defun image-crop--possibly-rotate-buffer (image)
+  (when (imagep image)
+    (let ((content-type (image-crop--content-type (buffer-string))))
+      (when (image-property image :rotation)
+       (cond
+        ;; We can rotate jpegs losslessly by setting the correct
+        ;; orientation.
+        ((and image-crop-exif-rotate
+              (equal content-type "image/jpeg")
+              (executable-find "exiftool"))
+         (call-process-region
+          (point-min) (point-max) "exiftool" t (list (current-buffer) nil) nil
+          (format "-Orientation#=%d"
+                  (cl-case (truncate (image-property image :rotation))
+                    (0 0)
+                    (90 6)
+                    (180 3)
+                    (270 8)
+                    (otherwise 0)))
+          "-o" "-" "-"))
+        ;; Most other image formats have to be reencoded to do
+        ;; rotation.
+        (t
+          (image-crop--process
+           image-crop-rotate-command
+           `((?r . ,(image-property image :rotation))
+             (?f . ,(cadr (split-string content-type "/")))))
+         (when (and (equal content-type "image/jpeg")
+                    (executable-find "exiftool"))
+           (call-process-region
+            (point-min) (point-max) "exiftool"
+             t (list (current-buffer) nil) nil
+            "-Orientation#=0"
+            "-o" "-" "-")))))
+      (when (image-property image :width)
+        (image-crop--process
+         image-crop-resize-command
+         `((?w . ,(image-property image :width))
+           (?f . ,(cadr (split-string content-type "/")))))))))
+
+(defun image-crop--insert-image-data (image text)
+  (insert-image
+   (create-image image nil t
+                :max-width (- (frame-pixel-width) 50)
+                :max-height (- (frame-pixel-height) 150))
+   (funcall image-crop-buffer-text-function text image)
+   nil nil t))
+
+(defun image-crop--process (command expansions)
+  "Use `call-process-region' with COMMAND expanded with EXPANSIONS."
+  (apply
+   #'call-process-region (point-min) (point-max)
+   (format-spec (car command) expansions)
+   t (list (current-buffer) nil) nil
+   (mapcar (lambda (elem)
+             (format-spec elem expansions))
+           (cdr command))))
+
+(defun image-crop--default-buffer-text (text _image)
+  (substring-no-properties text))
+
+(provide 'image-crop)
+
+;;; image-crop.el ends here
diff --git a/lisp/image/image-dired-dired.el b/lisp/image/image-dired-dired.el
new file mode 100644
index 0000000000..46adf3f26f
--- /dev/null
+++ b/lisp/image/image-dired-dired.el
@@ -0,0 +1,412 @@
+;;; image-dired-dired.el --- Dired specific commands for Image-Dired  -*- 
lexical-binding: t -*-
+
+;; Copyright (C) 2005-2022 Free Software Foundation, Inc.
+
+;; Author: Mathias Dahl <mathias.rem0veth1s.dahl@gmail.com>
+;; Maintainer: Stefan Kangas <stefankangas@gmail.com>
+;; Keywords: multimedia
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; See the description of the `image-dired' package.
+
+;;; Code:
+
+(require 'image-dired)
+
+(defgroup image-dired-dired nil
+  "Dired specific commands for Image-Dired."
+  :prefix "image-dired-dired-"
+  :link '(info-link "(emacs) Image-Dired")
+  :group 'image-dired)
+
+(define-obsolete-variable-alias 'image-dired-append-when-browsing
+  'image-dired-dired-append-when-browsing "29.1")
+(defcustom image-dired-dired-append-when-browsing nil
+  "Append thumbnails in thumbnail buffer when browsing.
+If non-nil, using `image-dired-next-line-and-display' and
+`image-dired-previous-line-and-display' will leave a trail of thumbnail
+images in the thumbnail buffer.  If you enable this and want to clean
+the thumbnail buffer because it is filled with too many thumbnails,
+just call `image-dired-display-thumb' to display only the image at point.
+This value can be toggled using `image-dired-toggle-append-browsing'."
+  :type 'boolean)
+
+(defcustom image-dired-dired-disp-props t
+  "If non-nil, display properties for Dired file when browsing.
+Used by `image-dired-next-line-and-display',
+`image-dired-previous-line-and-display' and 
`image-dired-mark-and-display-next'.
+If the database file is large, this can slow down image browsing in
+Dired and you might want to turn it off."
+  :type 'boolean)
+
+;;;###autoload
+(defun image-dired-dired-toggle-marked-thumbs (&optional arg)
+  "Toggle thumbnails in front of file names in the Dired buffer.
+If no marked file could be found, insert or hide thumbnails on the
+current line.  ARG, if non-nil, specifies the files to use instead
+of the marked files.  If ARG is an integer, use the next ARG (or
+previous -ARG, if ARG<0) files."
+  (interactive "P" dired-mode)
+  (setq image-dired--generate-thumbs-start  (current-time))
+  (dired-map-over-marks
+   (let ((image-pos  (dired-move-to-filename))
+         (image-file (dired-get-filename nil t))
+         thumb-file
+         overlay)
+     (when (and image-file
+                (string-match-p (image-dired--file-name-regexp) image-file))
+       (setq thumb-file (create-image
+                         (image-dired--get-create-thumbnail-file image-file)))
+       ;; If image is not already added, then add it.
+       (let ((thumb-ov (cl-loop for ov in (overlays-in (point) (1+ (point)))
+                                if (overlay-get ov 'thumb-file) return ov)))
+         (if thumb-ov
+             (delete-overlay thumb-ov)
+           (put-image thumb-file image-pos)
+           (setq overlay
+                 (cl-loop for ov in (overlays-in (point) (1+ (point)))
+                          if (overlay-get ov 'put-image) return ov))
+           (overlay-put overlay 'image-file image-file)
+           (overlay-put overlay 'thumb-file thumb-file)))))
+   ;; Show or hide thumbnail on ARG next files.
+   arg)
+  (add-hook 'dired-after-readin-hook
+            'image-dired-dired-after-readin-hook nil t))
+
+(defun image-dired-dired-after-readin-hook ()
+  "Relocate existing thumbnail overlays in Dired buffer after reverting.
+Move them to their corresponding files if they still exist.
+Otherwise, delete overlays.
+Used by `image-dired-dired-toggle-marked-thumbs'."
+  (mapc (lambda (overlay)
+          (when (overlay-get overlay 'put-image)
+            (let* ((image-file (overlay-get overlay 'image-file))
+                   (image-pos (dired-goto-file image-file)))
+              (if image-pos
+                  (move-overlay overlay image-pos image-pos)
+                (delete-overlay overlay)))))
+        (overlays-in (point-min) (point-max))))
+
+(defun image-dired-next-line-and-display ()
+  "Move to next Dired line and display thumbnail image."
+  (interactive nil dired-mode)
+  (dired-next-line 1)
+  (image-dired-display-thumbs t image-dired-dired-append-when-browsing t)
+  (if image-dired-dired-disp-props
+      (image-dired-dired-display-properties)))
+
+(defun image-dired-previous-line-and-display ()
+  "Move to previous Dired line and display thumbnail image."
+  (interactive nil dired-mode)
+  (dired-previous-line 1)
+  (image-dired-display-thumbs t image-dired-dired-append-when-browsing t)
+  (if image-dired-dired-disp-props
+      (image-dired-dired-display-properties)))
+
+(defun image-dired-toggle-append-browsing ()
+  "Toggle `image-dired-dired-append-when-browsing'."
+  (interactive nil dired-mode)
+  (setq image-dired-dired-append-when-browsing
+        (not image-dired-dired-append-when-browsing))
+  (message "Append browsing %s"
+           (if image-dired-dired-append-when-browsing
+               "on"
+             "off")))
+
+(defun image-dired-mark-and-display-next ()
+  "Mark current file in Dired and display next thumbnail image."
+  (interactive nil dired-mode)
+  (dired-mark 1)
+  (image-dired-display-thumbs t image-dired-dired-append-when-browsing t)
+  (if image-dired-dired-disp-props
+      (image-dired-dired-display-properties)))
+
+(defun image-dired-toggle-dired-display-properties ()
+  "Toggle `image-dired-dired-disp-props'."
+  (interactive nil dired-mode)
+  (setq image-dired-dired-disp-props
+        (not image-dired-dired-disp-props))
+  (message "Dired display properties %s"
+           (if image-dired-dired-disp-props
+               "on"
+             "off")))
+
+(defun image-dired-track-thumbnail ()
+  "Track current Dired file's thumb in `image-dired-thumbnail-buffer'.
+This is almost the same as what `image-dired-track-original-file' does,
+but the other way around."
+  (let ((file (dired-get-filename))
+        prop-val found window)
+    (when (get-buffer image-dired-thumbnail-buffer)
+      (with-current-buffer image-dired-thumbnail-buffer
+        (goto-char (point-min))
+        (while (and (not (eobp))
+                    (not found))
+          (if (and (setq prop-val
+                         (get-text-property (point) 'original-file-name))
+                   (string= prop-val file))
+              (setq found t))
+          (if (not found)
+              (forward-char 1)))
+        (when found
+          (if (setq window (image-dired-thumbnail-window))
+              (set-window-point window (point)))
+          (image-dired--update-header-line))))))
+
+(defun image-dired-dired-next-line (&optional arg)
+  "Call `dired-next-line', then track thumbnail.
+This can safely replace `dired-next-line'.
+With prefix argument, move ARG lines."
+  (interactive "P" dired-mode)
+  (dired-next-line (or arg 1))
+  (if image-dired-track-movement
+      (image-dired-track-thumbnail)))
+
+(defun image-dired-dired-previous-line (&optional arg)
+  "Call `dired-previous-line', then track thumbnail.
+This can safely replace `dired-previous-line'.
+With prefix argument, move ARG lines."
+  (interactive "P" dired-mode)
+  (dired-previous-line (or arg 1))
+  (if image-dired-track-movement
+      (image-dired-track-thumbnail)))
+
+;;;###autoload
+(defun image-dired-jump-thumbnail-buffer ()
+  "Jump to thumbnail buffer."
+  (interactive nil dired-mode)
+  (let ((window (image-dired-thumbnail-window))
+        frame)
+    (if window
+        (progn
+          (if (not (equal (selected-frame) (setq frame (window-frame window))))
+              (select-frame-set-input-focus frame))
+          (select-window window))
+      (message "Thumbnail buffer not visible"))))
+
+(defvar-keymap image-dired-minor-mode-map
+  :doc "Keymap for `image-dired-minor-mode'."
+  "<remap> <dired-previous-line>" #'image-dired-dired-previous-line
+  "<remap> <dired-next-line>"     #'image-dired-dired-next-line
+  "C-S-n"  #'image-dired-next-line-and-display
+  "C-S-p"  #'image-dired-previous-line-and-display
+  "C-S-m"  #'image-dired-mark-and-display-next
+  "<tab>"  #'image-dired-jump-thumbnail-buffer
+
+  :menu
+  '("Image-Dired"
+    ["Display thumb for next file" image-dired-next-line-and-display]
+    ["Display thumb for previous file" image-dired-previous-line-and-display]
+    ["Mark and display next" image-dired-mark-and-display-next]
+    "---"
+    ["Create thumbnails for marked files" image-dired-create-thumbs]
+    "---"
+    ["Display thumbnails append" image-dired-display-thumbs-append]
+    ["Display this thumbnail" image-dired-display-thumb]
+    ["Display image" image-dired-dired-display-image]
+    ["Display in external viewer" image-dired-dired-display-external]
+    "---"
+    ["Toggle display properties" image-dired-toggle-dired-display-properties
+     :style toggle
+     :selected image-dired-dired-disp-props]
+    ["Toggle append browsing" image-dired-toggle-append-browsing
+     :style toggle
+     :selected image-dired-dired-append-when-browsing]
+    ["Toggle movement tracking" image-dired-toggle-movement-tracking
+     :style toggle
+     :selected image-dired-track-movement]
+    "---"
+    ["Jump to thumbnail buffer" image-dired-jump-thumbnail-buffer]
+    ["Mark tagged files" image-dired-mark-tagged-files]
+    ["Comment files" image-dired-dired-comment-files]
+    ["Copy with EXIF file name" image-dired-copy-with-exif-file-name]))
+
+;;;###autoload
+(define-minor-mode image-dired-minor-mode
+  "Setup easy-to-use keybindings for Image-Dired in Dired mode.
+
+This minor mode adds these additional bindings:
+\\<image-dired-minor-mode-map>
+  \\[image-dired-next-line-and-display]                Move to next line and 
display \
+thumbnail image.
+  \\[image-dired-previous-line-and-display]            Move to previous line \
+and display thumbnail image.
+  \\[image-dired-mark-and-display-next]                Mark current file and 
display \
+next thumbnail image.
+  \\[image-dired-jump-thumbnail-buffer]                Jump to thumbnail 
buffer.
+
+For reference, these are the default Image-Dired bindings that
+are always available in Dired:
+\\<dired-mode-map>
+  \\[image-dired-display-thumbs]               Display thumbnails of all 
marked files.
+  \\[image-dired-tag-files]            Tag marked file(s).
+  \\[image-dired-delete-tag]           Remove tag for selected file(s).
+  \\[image-dired-jump-thumbnail-buffer]                Jump to thumbnail 
buffer.
+  \\[image-dired-dired-display-image]          Display current image file.
+  \\[image-dired-dired-display-external]               Display file at point \
+using an external viewer.
+  \\[image-dired-display-thumbs-append]                Append thumbnails to \
+thumbnail buffer.
+  \\[image-dired-display-thumb]                Display thumbnails of all 
marked files.
+  \\[image-dired-dired-comment-files]          Add comment to current or \
+marked files in Dired.
+  \\[image-dired-mark-tagged-files]            Use REGEXP to mark files with \
+matching tag.
+  \\[image-dired-dired-toggle-marked-thumbs]   Toggle thumbnails in \
+front of file names.
+  \\[image-dired-dired-edit-comment-and-tags]          Edit comment and tags \
+of marked images."
+  :keymap image-dired-minor-mode-map)
+
+(declare-function clear-image-cache "image.c" (&optional filter))
+
+(defun image-dired-create-thumbs (&optional arg)
+  "Create thumbnail images for all marked files in Dired.
+With prefix argument ARG, create thumbnails even if they already exist
+\(i.e. use this to refresh your thumbnails)."
+  (interactive "P" dired-mode)
+  (let (thumb-name)
+    (dolist (curr-file (dired-get-marked-files))
+      (setq thumb-name (image-dired-thumb-name curr-file))
+      ;; If the user overrides the exist check, we must clear the
+      ;; image cache so that if the user wants to display the
+      ;; thumbnail, it is not fetched from cache.
+      (when arg
+        (clear-image-cache (expand-file-name thumb-name)))
+      (when (or (not (file-exists-p thumb-name))
+                arg)
+        (image-dired-create-thumb curr-file thumb-name)))))
+
+;;;###autoload
+(defun image-dired-display-thumbs-append ()
+  "Append thumbnails to `image-dired-thumbnail-buffer'."
+  (interactive nil dired-mode)
+  (image-dired-display-thumbs nil t t))
+
+;;;###autoload
+(defun image-dired-display-thumb ()
+  "Shorthand for `image-dired-display-thumbs' with prefix argument."
+  (interactive nil dired-mode)
+  (image-dired-display-thumbs t nil t))
+
+;;;###autoload
+(defun image-dired-dired-display-external ()
+  "Display file at point using an external viewer."
+  (interactive nil dired-mode)
+  (let ((file (dired-get-filename)))
+    (start-process "image-dired-external" nil
+                   image-dired-external-viewer file)))
+
+;;;###autoload
+(defun image-dired-dired-display-image (&optional _)
+  "Display current image file.
+See documentation for `image-dired-display-image' for more information."
+  (declare (advertised-calling-convention () "29.1"))
+  (interactive nil dired-mode)
+  (image-dired-display-image (dired-get-filename)))
+
+(defun image-dired-copy-with-exif-file-name ()
+  "Copy file with unique name to main image directory.
+Copy current or all marked files in Dired to a new file in your
+main image directory, using a file name generated by
+`image-dired-get-exif-file-name'.  A typical usage for this if when
+copying images from a digital camera into the image directory.
+
+ Typically, you would open up the folder with the incoming
+digital images, mark the files to be copied, and execute this
+function.  The result is a couple of new files in
+`image-dired-main-image-directory' called
+2005_05_08_12_52_00_dscn0319.jpg,
+2005_05_08_14_27_45_dscn0320.jpg etc."
+  (interactive nil dired-mode)
+  (let (new-name
+        (files (dired-get-marked-files)))
+    (mapc
+     (lambda (curr-file)
+       (setq new-name
+             (format "%s/%s"
+                     (file-name-as-directory
+                      (expand-file-name image-dired-main-image-directory))
+                     (image-dired-get-exif-file-name curr-file)))
+       (message "Copying %s to %s" curr-file new-name)
+       (copy-file curr-file new-name))
+     files)))
+
+;;;###autoload
+(defun image-dired-mark-tagged-files (regexp)
+  "Use REGEXP to mark files with matching tag.
+A `tag' is a keyword, a piece of meta data, associated with an
+image file and stored in image-dired's database file.  This command
+lets you input a regexp and this will be matched against all tags
+on all image files in the database file.  The files that have a
+matching tag will be marked in the Dired buffer."
+  (interactive "sMark tagged files (regexp): " dired-mode)
+  (image-dired-sane-db-file)
+  (let ((hits 0)
+        files)
+    (image-dired--with-db-file
+      ;; Collect matches
+      (while (search-forward-regexp "\\(^[^;\n]+\\);\\(.*\\)" nil t)
+        (let ((file (match-string 1))
+              (tags (split-string (match-string 2) ";")))
+          (when (seq-find (lambda (tag)
+                            (string-match-p regexp tag))
+                          tags)
+            (push file files)))))
+    ;; Mark files
+    (dolist (curr-file files)
+      ;; I tried using `dired-mark-files-regexp' but it was waaaay to
+      ;; slow.  Don't bother about hits found in other directories
+      ;; than the current one.
+      (when (string= (file-name-as-directory
+                      (expand-file-name default-directory))
+                     (file-name-as-directory
+                      (file-name-directory curr-file)))
+        (setq curr-file (file-name-nondirectory curr-file))
+        (goto-char (point-min))
+        (when (search-forward-regexp (format "\\s %s$" curr-file) nil t)
+          (setq hits (+ hits 1))
+          (dired-mark 1))))
+    (message "%d files with matching tag marked" hits)))
+
+(defun image-dired-dired-display-properties ()
+  "Display properties for Dired file in the echo area."
+  (interactive nil dired-mode)
+  (let* ((file-name (dired-get-filename))
+         (dired-buf (buffer-name (current-buffer)))
+         (image-count "")               ; TODO
+         (props (string-join (image-dired-list-tags file-name) ", "))
+         (comment (image-dired-get-comment file-name))
+         (message-log-max nil))
+    (if file-name
+        (message "%s"
+                 (image-dired-format-properties-string
+                  dired-buf
+                  file-name
+                  image-count
+                  props
+                  comment)))))
+
+(provide 'image-dired-dired)
+
+;; Local Variables:
+;; nameless-current-name: "image-dired"
+;; End:
+
+;;; image-dired-dired.el ends here
diff --git a/lisp/image/image-dired-external.el 
b/lisp/image/image-dired-external.el
new file mode 100644
index 0000000000..026a84560d
--- /dev/null
+++ b/lisp/image/image-dired-external.el
@@ -0,0 +1,473 @@
+;;; image-dired-external.el --- External process support for Image-Dired  -*- 
lexical-binding: t -*-
+
+;; Copyright (C) 2005-2022 Free Software Foundation, Inc.
+
+;; Author: Mathias Dahl <mathias.rem0veth1s.dahl@gmail.com>
+;; Maintainer: Stefan Kangas <stefankangas@gmail.com>
+;; Keywords: multimedia
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; See the description of the `image-dired' package.
+
+;;; Code:
+
+(require 'dired)
+(require 'exif)
+
+(require 'image-dired-util)
+
+(declare-function image-dired-display-image "image-dired")
+(declare-function clear-image-cache "image.c" (&optional filter))
+
+(defvar image-dired-dir)
+(defvar image-dired-thumb-size)
+(defvar image-dired-main-image-directory)
+(defvar image-dired-rotate-original-ask-before-overwrite)
+(defvar image-dired-thumbnail-storage)
+
+(defgroup image-dired-external nil
+  "External process support for Image-Dired."
+  :prefix "image-dired-"
+  :link '(info-link "(emacs) Image-Dired")
+  :group 'image-dired)
+
+(defcustom image-dired-cmd-create-thumbnail-program
+  (if (executable-find "gm") "gm" "convert")
+  "Executable used to create thumbnail.
+Used together with `image-dired-cmd-create-thumbnail-options'."
+  :type 'file
+  :version "29.1")
+
+(defcustom image-dired-cmd-create-thumbnail-options
+  (let ((opts '("-size" "%wx%h" "%f[0]"
+                "-resize" "%wx%h>"
+                "-strip" "jpeg:%t")))
+    (if (executable-find "gm") (cons "convert" opts) opts))
+  "Options of command used to create thumbnail image.
+Used with `image-dired-cmd-create-thumbnail-program'.
+Available format specifiers are:
+    %s, %w and %h, which are replaced by `image-dired-thumb-size'
+    %f which is replaced by the file name of the original image and
+    %t which is replaced by the file name of the thumbnail file."
+  :type '(repeat (string :tag "Argument"))
+  :version "29.1")
+
+(defcustom image-dired-cmd-pngnq-program
+  ;; Prefer pngquant to pngnq-s9 as it is faster on my machine.
+  ;;   The project also seems more active than the alternatives.
+  ;; Prefer pngnq-s9 to pngnq as it fixes bugs in pngnq.
+  ;; The pngnq project seems dead (?) since 2011 or so.
+  (or (executable-find "pngquant")
+      (executable-find "pngnq-s9")
+      (executable-find "pngnq"))
+  "The file name of the `pngquant' or `pngnq' program.
+It quantizes colors of PNG images down to 256 colors or fewer
+using the NeuQuant algorithm."
+  :version "29.1"
+  :type '(choice (const :tag "Not Set" nil) file))
+
+(defcustom image-dired-cmd-pngnq-options
+  (if (executable-find "pngquant")
+      '("--ext" "-nq8.png" "%t") ; same extension as "pngnq"
+    '("-f" "%t"))
+  "Arguments to pass `image-dired-cmd-pngnq-program'.
+Available format specifiers are the same as in
+`image-dired-cmd-create-thumbnail-options'."
+  :type '(repeat (string :tag "Argument"))
+  :version "29.1")
+
+(defcustom image-dired-cmd-pngcrush-program (executable-find "pngcrush")
+  "The file name of the `pngcrush' program.
+It optimizes the compression of PNG images.  Also it adds PNG textual chunks
+with the information required by the Thumbnail Managing Standard."
+  :type '(choice (const :tag "Not Set" nil) file))
+
+(defcustom image-dired-cmd-pngcrush-options
+  `("-q"
+    "-text" "b" "Description" "Thumbnail of file://%f"
+    "-text" "b" "Software" ,(emacs-version)
+    ;; "-text b \"Thumb::Image::Height\" \"%oh\" "
+    ;; "-text b \"Thumb::Image::Mimetype\" \"%mime\" "
+    ;; "-text b \"Thumb::Image::Width\" \"%ow\" "
+    "-text" "b" "Thumb::MTime" "%m"
+    ;; "-text b \"Thumb::Size\" \"%b\" "
+    "-text" "b" "Thumb::URI" "file://%f"
+    "%q" "%t")
+  "Arguments for `image-dired-cmd-pngcrush-program'.
+The available %-format specifiers are the same as in
+`image-dired-cmd-create-thumbnail-options', with \"%q\" for a
+temporary file name (typically generated by pnqnq)."
+  :version "26.1"
+  :type '(repeat (string :tag "Argument")))
+
+(defcustom image-dired-cmd-optipng-program (executable-find "optipng")
+  "The file name of the `optipng' program."
+  :version "26.1"
+  :type '(choice (const :tag "Not Set" nil) file))
+
+(defcustom image-dired-cmd-optipng-options '("-o5" "%t")
+  "Arguments passed to `image-dired-cmd-optipng-program'.
+Available format specifiers are described in
+`image-dired-cmd-create-thumbnail-options'."
+  :version "26.1"
+  :type '(repeat (string :tag "Argument"))
+  :link '(url-link "man:optipng(1)"))
+
+(defcustom image-dired-cmd-create-standard-thumbnail-options
+  (let ((opts (list
+               "-size" "%wx%h" "%f[0]"
+               "-set" "Thumb::MTime" "%m"
+               "-set" "Thumb::URI" "file://%f"
+               "-set" "Description" "Thumbnail of file://%f"
+               "-set" "Software" (emacs-version)
+               "-thumbnail" "%wx%h>" "png:%t")))
+    (if (executable-find "gm") (cons "convert" opts) opts))
+  "Options for creating thumbnails according to the Thumbnail Managing 
Standard.
+The available %-format specifiers are the same as in
+`image-dired-cmd-create-thumbnail-options', with \"%m\" for file
+modification time."
+  :type '(repeat (string :tag "Argument"))
+  :version "29.1")
+
+(defcustom image-dired-cmd-rotate-original-program "jpegtran"
+  "Executable used to rotate original image.
+Used together with `image-dired-cmd-rotate-original-options'."
+  :type 'file)
+
+(defcustom image-dired-cmd-rotate-original-options
+  '("-rotate" "%d" "-copy" "all" "-outfile" "%t" "%o")
+  "Arguments of command used to rotate original image.
+Used with `image-dired-cmd-rotate-original-program'.
+Available format specifiers are: %d which is replaced by the
+number of (positive) degrees to rotate the image, normally 90 or
+270 \(for 90 degrees right and left), %o which is replaced by the
+original image file name and %t which is replaced by
+`image-dired-temp-image-file'."
+  :version "26.1"
+  :type '(repeat (string :tag "Argument")))
+
+(defcustom image-dired-temp-rotate-image-file
+  (expand-file-name ".image-dired_rotate_temp"
+                    (locate-user-emacs-file "image-dired/"))
+  "Temporary file for rotate operations."
+  :type 'file)
+
+(defcustom image-dired-cmd-write-exif-data-program "exiftool"
+  "Program used to write EXIF data to image.
+Used together with `image-dired-cmd-write-exif-data-options'."
+  :type 'file)
+
+(defcustom image-dired-cmd-write-exif-data-options '("-%t=%v" "%f")
+  "Arguments of command used to write EXIF data.
+Used with `image-dired-cmd-write-exif-data-program'.
+Available format specifiers are: %f which is replaced by
+the image file name, %t which is replaced by the tag name and %v
+which is replaced by the tag value."
+  :version "26.1"
+  :type '(repeat (string :tag "Argument")))
+
+
+;;; Util functions
+
+(defun image-dired--check-executable-exists (executable)
+  (unless (executable-find (symbol-value executable))
+    (error "Executable %S not found" executable)))
+
+
+;;; Creating thumbnails
+
+(defun image-dired--thumb-size (&optional _)
+  "Return thumb size depending on `image-dired-thumbnail-storage'."
+  (declare (advertised-calling-convention () "29.1"))
+  (pcase image-dired-thumbnail-storage
+    ('standard 128)
+    ('standard-large 256)
+    ('standard-x-large 512)
+    ('standard-xx-large 1024)
+    (_ image-dired-thumb-size)))
+
+(defvar image-dired--generate-thumbs-start nil
+  "Time when `display-thumbs' was called.")
+
+(defvar image-dired-queue nil
+  "List of items in the queue.
+Each item has the form (ORIGINAL-FILE TARGET-FILE).")
+
+(defvar image-dired-queue-active-jobs 0
+  "Number of active jobs in `image-dired-queue'.")
+
+(defvar image-dired-queue-active-limit (min 4 (max 2 (/ (num-processors) 2)))
+  "Maximum number of concurrent jobs permitted for generating images.
+Increase at own risk.  If you want to experiment with this,
+consider setting `image-dired-debug' to a non-nil value to see
+the time spent on generating thumbnails.  Run `image-clear-cache'
+and remove the cached thumbnail files between each trial run.")
+
+(defun image-dired-pngnq-thumb (spec)
+  "Quantize thumbnail described by format SPEC with pngnq(1)."
+  (let ((process
+         (apply #'start-process "image-dired-pngnq" nil
+                image-dired-cmd-pngnq-program
+                (mapcar (lambda (arg) (format-spec arg spec))
+                        image-dired-cmd-pngnq-options))))
+    (setf (process-sentinel process)
+          (lambda (process status)
+            (if (and (eq (process-status process) 'exit)
+                     (zerop (process-exit-status process)))
+                ;; Pass off to pngcrush, or just rename the
+                ;; THUMB-nq8.png file back to THUMB.png
+                (if (and image-dired-cmd-pngcrush-program
+                         (executable-find image-dired-cmd-pngcrush-program))
+                    (image-dired-pngcrush-thumb spec)
+                  (let ((nq8 (cdr (assq ?q spec)))
+                        (thumb (cdr (assq ?t spec))))
+                    (rename-file nq8 thumb t)))
+              (message "command %S %s" (process-command process)
+                       (string-replace "\n" "" status)))))
+    process))
+
+(defun image-dired-pngcrush-thumb (spec)
+  "Optimize thumbnail described by format SPEC with pngcrush(1)."
+  ;; If pngnq wasn't run, then the THUMB-nq8.png file does not exist.
+  ;; pngcrush needs an infile and outfile, so we just copy THUMB to
+  ;; THUMB-nq8.png and use the latter as a temp file.
+  (when (not image-dired-cmd-pngnq-program)
+    (let ((temp (cdr (assq ?q spec)))
+          (thumb (cdr (assq ?t spec))))
+      (copy-file thumb temp)))
+  (let ((process
+         (apply #'start-process "image-dired-pngcrush" nil
+                image-dired-cmd-pngcrush-program
+                (mapcar (lambda (arg) (format-spec arg spec))
+                        image-dired-cmd-pngcrush-options))))
+    (setf (process-sentinel process)
+          (lambda (process status)
+            (unless (and (eq (process-status process) 'exit)
+                         (zerop (process-exit-status process)))
+              (message "command %S %s" (process-command process)
+                       (string-replace "\n" "" status)))
+            (when (memq (process-status process) '(exit signal))
+              (let ((temp (cdr (assq ?q spec))))
+                (delete-file temp)))))
+    process))
+
+(defun image-dired-optipng-thumb (spec)
+  "Optimize thumbnail described by format SPEC with optipng(1)."
+  (let ((process
+         (apply #'start-process "image-dired-optipng" nil
+                image-dired-cmd-optipng-program
+                (mapcar (lambda (arg) (format-spec arg spec))
+                        image-dired-cmd-optipng-options))))
+    (setf (process-sentinel process)
+          (lambda (process status)
+            (unless (and (eq (process-status process) 'exit)
+                         (zerop (process-exit-status process)))
+              (message "command %S %s" (process-command process)
+                       (string-replace "\n" "" status)))))
+    process))
+
+(defun image-dired-create-thumb-1 (original-file thumbnail-file)
+  "For ORIGINAL-FILE, create thumbnail image named THUMBNAIL-FILE."
+  (image-dired--check-executable-exists
+   'image-dired-cmd-create-thumbnail-program)
+  (let* ((size (number-to-string (image-dired--thumb-size)))
+         (modif-time (format-time-string
+                      "%s" (file-attribute-modification-time
+                            (file-attributes original-file))))
+         (thumbnail-nq8-file (replace-regexp-in-string ".png\\'" "-nq8.png"
+                                                       thumbnail-file))
+         (spec `((?s . ,size) (?w . ,size) (?h . ,size)
+                 (?m . ,modif-time)
+                 (?f . ,original-file)
+                 (?q . ,thumbnail-nq8-file)
+                 (?t . ,thumbnail-file)))
+         (thumbnail-dir (file-name-directory thumbnail-file))
+         process)
+    (when (not (file-exists-p thumbnail-dir))
+      (with-file-modes #o700
+        (make-directory thumbnail-dir t))
+      (message "Thumbnail directory created: %s" thumbnail-dir))
+
+    ;; Thumbnail file creation processes begin here and are marshaled
+    ;; in a queue by `image-dired-create-thumb'.
+    (let ((cmd image-dired-cmd-create-thumbnail-program)
+          (args (mapcar
+                 (lambda (arg) (format-spec arg spec))
+                 (if (memq image-dired-thumbnail-storage
+                           image-dired--thumbnail-standard-sizes)
+                     image-dired-cmd-create-standard-thumbnail-options
+                   image-dired-cmd-create-thumbnail-options))))
+      (image-dired-debug "Running %s %s" cmd (string-join args " "))
+      (setq process
+            (apply #'start-process "image-dired-create-thumbnail" nil
+                   cmd args)))
+
+    (setf (process-sentinel process)
+          (lambda (process status)
+            ;; Trigger next in queue once a thumbnail has been created
+            (cl-decf image-dired-queue-active-jobs)
+            (image-dired-thumb-queue-run)
+            (when (= image-dired-queue-active-jobs 0)
+              (image-dired-debug
+               (format-time-string
+                "Generated thumbnails in %s.%3N seconds"
+                (time-subtract nil
+                               image-dired--generate-thumbs-start))))
+            (if (not (and (eq (process-status process) 'exit)
+                          (zerop (process-exit-status process))))
+                (message "Thumb could not be created for %s: %s"
+                         (abbreviate-file-name original-file)
+                         (string-replace "\n" "" status))
+              (set-file-modes thumbnail-file #o600)
+              (clear-image-cache thumbnail-file)
+              ;; PNG thumbnail has been created since we are
+              ;; following the XDG thumbnail spec, so try to optimize
+              (when (memq image-dired-thumbnail-storage
+                          image-dired--thumbnail-standard-sizes)
+                (cond
+                 ((and image-dired-cmd-pngnq-program
+                       (executable-find image-dired-cmd-pngnq-program))
+                  (image-dired-pngnq-thumb spec))
+                 ((and image-dired-cmd-pngcrush-program
+                       (executable-find image-dired-cmd-pngcrush-program))
+                  (image-dired-pngcrush-thumb spec))
+                 ((and image-dired-cmd-optipng-program
+                       (executable-find image-dired-cmd-optipng-program))
+                  (image-dired-optipng-thumb spec)))))))
+    process))
+
+(defun image-dired-thumb-queue-run ()
+  "Run a queued job if one exists and not too many jobs are running.
+Queued items live in `image-dired-queue'."
+  (while (and image-dired-queue
+              (< image-dired-queue-active-jobs
+                 image-dired-queue-active-limit))
+    (cl-incf image-dired-queue-active-jobs)
+    (apply #'image-dired-create-thumb-1 (pop image-dired-queue))))
+
+(defun image-dired-create-thumb (original-file thumbnail-file)
+  "Add a job for generating ORIGINAL-FILE thumbnail to `image-dired-queue'.
+The new file will be named THUMBNAIL-FILE."
+  (setq image-dired-queue
+        (nconc image-dired-queue
+               (list (list original-file thumbnail-file))))
+  (run-at-time 0 nil #'image-dired-thumb-queue-run))
+
+(defun image-dired-refresh-thumb ()
+  "Force creation of new image for current thumbnail."
+  (interactive nil image-dired-thumbnail-mode)
+  (let* ((file (image-dired-original-file-name))
+         (thumb (expand-file-name (image-dired-thumb-name file))))
+    (clear-image-cache (expand-file-name thumb))
+    (image-dired-create-thumb file thumb)))
+
+(defun image-dired-rotate-original (degrees)
+  "Rotate original image DEGREES degrees."
+  (image-dired--check-executable-exists
+   'image-dired-cmd-rotate-original-program)
+  (if (not (image-dired-image-at-point-p))
+      (message "No image at point")
+    (let* ((file (image-dired-original-file-name))
+           (spec
+            (list
+             (cons ?d degrees)
+             (cons ?o (expand-file-name file))
+             (cons ?t image-dired-temp-rotate-image-file))))
+      (unless (eq 'jpeg (image-type file))
+        (user-error "Only JPEG images can be rotated"))
+      (if (not (= 0 (apply #'call-process 
image-dired-cmd-rotate-original-program
+                           nil nil nil
+                           (mapcar (lambda (arg) (format-spec arg spec))
+                                   image-dired-cmd-rotate-original-options))))
+          (error "Could not rotate image")
+        (image-dired-display-image image-dired-temp-rotate-image-file)
+        (if (or (and image-dired-rotate-original-ask-before-overwrite
+                     (y-or-n-p
+                      "Rotate to temp file OK.  Overwrite original image? "))
+                (not image-dired-rotate-original-ask-before-overwrite))
+            (progn
+              (copy-file image-dired-temp-rotate-image-file file t)
+              (image-dired-refresh-thumb))
+          (image-dired-display-image file))))))
+
+
+;;; EXIF support
+
+(defun image-dired-get-exif-file-name (file)
+  "Use the image's EXIF information to return a unique file name.
+The file name should be unique as long as you do not take more than
+one picture per second.  The original file name is suffixed at the end
+for traceability.  The format of the returned file name is
+YYYY_MM_DD_HH_MM_DD_ORIG_FILE_NAME.jpg.  Used from
+`image-dired-copy-with-exif-file-name'."
+  (let (data no-exif-data-found)
+    (if (not (eq 'jpeg (image-type (expand-file-name file))))
+        (setq no-exif-data-found t
+              data (format-time-string
+                    "%Y:%m:%d %H:%M:%S"
+                    (file-attribute-modification-time
+                     (file-attributes (expand-file-name file)))))
+      (setq data (exif-field 'date-time (exif-parse-file
+                                         (expand-file-name file)))))
+    (while (string-match "[ :]" data)
+      (setq data (replace-match "_" nil nil data)))
+    (format "%s%s%s" data
+            (if no-exif-data-found
+                "_noexif_"
+              "_")
+            (file-name-nondirectory file))))
+
+(defun image-dired-thumbnail-set-image-description ()
+  "Set the ImageDescription EXIF tag for the original image.
+If the image already has a value for this tag, it is used as the
+default value at the prompt."
+  (interactive nil image-dired-thumbnail-mode)
+  (if (not (image-dired-image-at-point-p))
+      (message "No thumbnail at point")
+    (let* ((file (image-dired-original-file-name))
+           (old-value (or (exif-field 'description (exif-parse-file file)) 
"")))
+      (if (eq 0
+              (image-dired-set-exif-data file "ImageDescription"
+                                         (read-string "Value of 
ImageDescription: "
+                                                      old-value)))
+          (message "Successfully wrote ImageDescription tag")
+        (error "Could not write ImageDescription tag")))))
+
+(defun image-dired-set-exif-data (file tag-name tag-value)
+  "In FILE, set EXIF tag TAG-NAME to value TAG-VALUE."
+  (image-dired--check-executable-exists
+   'image-dired-cmd-write-exif-data-program)
+  (let ((spec
+         (list
+          (cons ?f (expand-file-name file))
+          (cons ?t tag-name)
+          (cons ?v tag-value))))
+    (apply #'call-process image-dired-cmd-write-exif-data-program nil nil nil
+           (mapcar (lambda (arg) (format-spec arg spec))
+                   image-dired-cmd-write-exif-data-options))))
+
+(define-obsolete-function-alias 'image-dired-thumb-size 
#'image-dired--thumb-size "29.1")
+
+(provide 'image-dired-external)
+
+;; Local Variables:
+;; nameless-current-name: "image-dired"
+;; End:
+
+;;; image-dired-external.el ends here
diff --git a/lisp/image/image-dired-tags.el b/lisp/image/image-dired-tags.el
new file mode 100644
index 0000000000..dfd6473285
--- /dev/null
+++ b/lisp/image/image-dired-tags.el
@@ -0,0 +1,385 @@
+;;; image-dired-tags.el --- Tag support for Image-Dired  -*- lexical-binding: 
t -*-
+
+;; Copyright (C) 2005-2022 Free Software Foundation, Inc.
+
+;; Author: Mathias Dahl <mathias.rem0veth1s.dahl@gmail.com>
+;; Maintainer: Stefan Kangas <stefankangas@gmail.com>
+;; Keywords: multimedia
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; See the description of the `image-dired' package.
+
+;;; Code:
+
+(require 'dired)
+
+(require 'image-dired-util)
+
+(declare-function image-dired--with-marked "image-dired")
+
+(defvar image-dired-dir)
+(defvar image-dired-thumbnail-storage)
+(defvar image-dired-tags-db-file)
+
+(defmacro image-dired--with-db-file (&rest body)
+  "Run BODY in a temp buffer containing `image-dired-tags-db-file'.
+Return the last form in BODY."
+  (declare (indent 0) (debug t))
+  `(with-temp-buffer
+     (if (file-exists-p image-dired-tags-db-file)
+         (insert-file-contents image-dired-tags-db-file))
+     ,@body))
+
+(defun image-dired-sane-db-file ()
+  "Check if `image-dired-tags-db-file' exists.
+If not, try to create it (including any parent directories).
+Signal error if there are problems creating it."
+  (or (file-exists-p image-dired-tags-db-file)
+      (let (dir buf)
+        (unless (file-directory-p (setq dir (file-name-directory
+                                             image-dired-tags-db-file)))
+          (with-file-modes #o700
+            (make-directory dir t)))
+        (with-current-buffer (setq buf (create-file-buffer
+                                        image-dired-tags-db-file))
+          (with-file-modes #o600
+            (write-file image-dired-tags-db-file)))
+        (kill-buffer buf)
+        (file-exists-p image-dired-tags-db-file))
+      (error "Could not create %s" image-dired-tags-db-file)))
+
+(defvar image-dired-tag-history nil "Variable holding the tag history.")
+
+(defun image-dired-write-tags (file-tags)
+  "Write file tags to database.
+Write each file and tag in FILE-TAGS to the database.
+FILE-TAGS is an alist in the following form:
+ ((FILE . TAG) ... )"
+  (image-dired-sane-db-file)
+  (let (end file tag)
+    (image-dired--with-db-file
+      (setq buffer-file-name image-dired-tags-db-file)
+      (dolist (elt file-tags)
+        (setq file (car elt)
+              tag (cdr elt))
+        (goto-char (point-min))
+        (if (search-forward-regexp (format "^%s.*$" file) nil t)
+            (progn
+              (setq end (point))
+              (beginning-of-line)
+              (when (not (search-forward (format ";%s" tag) end t))
+                (end-of-line)
+                (insert (format ";%s" tag))))
+          (goto-char (point-max))
+          (insert (format "%s;%s\n" file tag))))
+      (save-buffer))))
+
+(defun image-dired-remove-tag (files tag)
+  "For all FILES, remove TAG from the image database."
+  (image-dired-sane-db-file)
+  (image-dired--with-db-file
+    (setq buffer-file-name image-dired-tags-db-file)
+    (let (end)
+      (unless (listp files)
+        (if (stringp files)
+            (setq files (list files))
+          (error "Files must be a string or a list of strings!")))
+      (dolist (file files)
+        (goto-char (point-min))
+        (when (search-forward-regexp (format "^%s;" file) nil t)
+          (end-of-line)
+          (setq end (point))
+          (beginning-of-line)
+          (when (search-forward-regexp
+                 (format "\\(;%s\\)\\($\\|;\\)" tag) end t)
+            (delete-region (match-beginning 1) (match-end 1))
+            ;; Check if file should still be in the database.
+            ;; If it has no tags or comments, it will be removed.
+            (end-of-line)
+            (setq end (point))
+            (beginning-of-line)
+            (when (not (search-forward ";" end t))
+              (kill-line 1))))))
+    (save-buffer)))
+
+(defun image-dired-list-tags (file)
+  "Read all tags for image FILE from the image database."
+  (image-dired-sane-db-file)
+  (image-dired--with-db-file
+    (let (end (tags ""))
+      (when (search-forward-regexp (format "^%s" file) nil t)
+        (end-of-line)
+        (setq end (point))
+        (beginning-of-line)
+        (if (search-forward ";" end t)
+            (if (search-forward "comment:" end t)
+                (if (search-forward ";" end t)
+                    (setq tags (buffer-substring (point) end)))
+              (setq tags (buffer-substring (point) end)))))
+      (split-string tags ";"))))
+
+;;;###autoload
+(defun image-dired-tag-files (arg)
+  "Tag marked file(s) in Dired.  With prefix ARG, tag file at point."
+  (interactive "P" dired-mode)
+  (let ((tag (completing-read
+              "Tags to add (separate tags with a semicolon): "
+              image-dired-tag-history nil nil nil 'image-dired-tag-history))
+        files)
+    (if arg
+        (setq files (list (dired-get-filename)))
+      (setq files (dired-get-marked-files)))
+    (image-dired-write-tags
+     (mapcar
+      (lambda (x)
+        (cons x tag))
+      files))))
+
+(defun image-dired-tag-thumbnail ()
+  "Tag current or marked thumbnails."
+  (interactive nil image-dired-thumbnail-mode)
+  (let ((tag (completing-read
+              "Tags to add (separate tags with a semicolon): "
+              image-dired-tag-history nil nil nil 'image-dired-tag-history)))
+    (image-dired--with-marked
+     (image-dired-write-tags
+      (list (cons (image-dired-original-file-name) tag)))
+     (image-dired-update-property
+      'tags (image-dired-list-tags (image-dired-original-file-name))))))
+
+;;;###autoload
+(defun image-dired-delete-tag (arg)
+  "Remove tag for selected file(s).
+With prefix argument ARG, remove tag from file at point."
+  (interactive "P" dired-mode)
+  (let ((tag (completing-read "Tag to remove: " image-dired-tag-history
+                              nil nil nil 'image-dired-tag-history))
+        files)
+    (if arg
+        (setq files (list (dired-get-filename)))
+      (setq files (dired-get-marked-files)))
+    (image-dired-remove-tag files tag)))
+
+(defun image-dired-tag-thumbnail-remove ()
+  "Remove tag from current or marked thumbnails."
+  (interactive nil image-dired-thumbnail-mode)
+  (let ((tag (completing-read "Tag to remove: " image-dired-tag-history
+                              nil nil nil 'image-dired-tag-history)))
+    (image-dired--with-marked
+     (image-dired-remove-tag (image-dired-original-file-name) tag)
+     (image-dired-update-property
+      'tags (image-dired-list-tags (image-dired-original-file-name))))))
+
+(defun image-dired-write-comments (file-comments)
+  "Write file comments to database.
+Write file comments to one or more files.
+FILE-COMMENTS is an alist on the following form:
+ ((FILE . COMMENT) ... )"
+  (image-dired-sane-db-file)
+  (let (end comment-beg-pos comment-end-pos file comment)
+    (image-dired--with-db-file
+      (setq buffer-file-name image-dired-tags-db-file)
+      (dolist (elt file-comments)
+        (setq file (car elt)
+              comment (cdr elt))
+        (goto-char (point-min))
+        (if (search-forward-regexp (format "^%s.*$" file) nil t)
+            (progn
+              (setq end (point))
+              (beginning-of-line)
+              ;; Delete old comment, if any
+              (when (search-forward ";comment:" end t)
+                (setq comment-beg-pos (match-beginning 0))
+                ;; Any tags after the comment?
+                (if (search-forward ";" end t)
+                    (setq comment-end-pos (- (point) 1))
+                  (setq comment-end-pos end))
+                ;; Delete comment tag and comment
+                (delete-region comment-beg-pos comment-end-pos))
+              ;; Insert new comment
+              (beginning-of-line)
+              (unless (search-forward ";" end t)
+                (end-of-line)
+                (insert ";"))
+              (insert (format "comment:%s;" comment)))
+          ;; File does not exist in database - add it.
+          (goto-char (point-max))
+          (insert (format "%s;comment:%s\n" file comment))))
+      (save-buffer))))
+
+(defun image-dired-update-property (prop value)
+  "Update text property PROP with value VALUE at point."
+  (let ((inhibit-read-only t))
+    (put-text-property
+     (point) (1+ (point))
+     prop
+     value)))
+
+;;;###autoload
+(defun image-dired-dired-comment-files ()
+  "Add comment to current or marked files in Dired."
+  (interactive nil dired-mode)
+  (let ((comment (image-dired-read-comment)))
+    (image-dired-write-comments
+     (mapcar
+      (lambda (curr-file)
+        (cons curr-file comment))
+      (dired-get-marked-files)))))
+
+(defun image-dired-read-comment (&optional file)
+  "Read comment for an image.
+Optionally use old comment from FILE as initial value."
+  (let ((comment
+         (read-string
+          "Comment: "
+          (if file (image-dired-get-comment file)))))
+    comment))
+
+(defun image-dired-get-comment (file)
+  "Get comment for file FILE."
+  (image-dired-sane-db-file)
+  (image-dired--with-db-file
+    (let (end comment-beg-pos comment-end-pos comment)
+      (when (search-forward-regexp (format "^%s" file) nil t)
+        (end-of-line)
+        (setq end (point))
+        (beginning-of-line)
+        (when (search-forward ";comment:" end t)
+          (setq comment-beg-pos (point))
+          (if (search-forward ";" end t)
+              (setq comment-end-pos (- (point) 1))
+            (setq comment-end-pos end))
+          (setq comment (buffer-substring
+                         comment-beg-pos comment-end-pos))))
+      comment)))
+
+
+;;; Tag support
+
+(defvar image-dired-widget-list nil
+  "List to keep track of meta data in edit buffer.")
+
+(declare-function widget-forward "wid-edit" (arg))
+
+;;;###autoload
+(defun image-dired-dired-edit-comment-and-tags ()
+  "Edit comment and tags of current or marked image files.
+Edit comment and tags for all marked image files in an
+easy-to-use form."
+  (interactive nil dired-mode)
+  (setq image-dired-widget-list nil)
+  ;; Setup buffer.
+  (let ((files (dired-get-marked-files)))
+    (pop-to-buffer-same-window "*Image-Dired Edit Meta Data*")
+    (kill-all-local-variables)
+    (let ((inhibit-read-only t))
+      (erase-buffer))
+    (remove-overlays)
+    ;; Some help for the user.
+    (widget-insert
+     (substitute-command-keys
+      "\\<widget-field-keymap>
+Edit comments and tags for each image.  Separate multiple tags
+with a comma.  Move forward between fields using \\[widget-forward] \
+or \\[widget-field-activate].
+Move to the previous field using \\[widget-backward].  Save by
+activating the \"Save\" button at the bottom of the form or
+cancel the operation by activating the \"Cancel\" button.\n\n"))
+    ;; Here comes all images and a comment and tag field for each
+    ;; image.
+    (let (thumb-file img comment-widget tag-widget)
+
+      (dolist (file files)
+
+        (setq thumb-file (image-dired-thumb-name file)
+              img (create-image thumb-file))
+
+        (insert-image img)
+        (widget-insert "\n\nComment: ")
+        (setq comment-widget
+              (widget-create 'editable-field
+                             :size 60
+                             :format "%v "
+                             :value (or (image-dired-get-comment file) "")))
+        (widget-insert "\nTags:    ")
+        (setq tag-widget
+              (widget-create 'editable-field
+                             :size 60
+                             :format "%v "
+                             :value (or (mapconcat
+                                         #'identity
+                                         (image-dired-list-tags file)
+                                         ",") "")))
+        ;; Save information in all widgets so that we can use it when
+        ;; the user saves the form.
+        (setq image-dired-widget-list
+              (append image-dired-widget-list
+                      (list (list file comment-widget tag-widget))))
+        (widget-insert "\n\n")))
+
+    ;; Footer with Save and Cancel button.
+    (widget-insert "\n")
+    (widget-create 'push-button
+                   :notify
+                   (lambda (&rest _ignore)
+                     (image-dired-save-information-from-widgets)
+                     (bury-buffer)
+                     (message "Done"))
+                   "Save")
+    (widget-insert " ")
+    (widget-create 'push-button
+                   :notify
+                   (lambda (&rest _ignore)
+                     (bury-buffer)
+                     (message "Operation canceled"))
+                   "Cancel")
+    (widget-insert "\n")
+    (use-local-map widget-keymap)
+    (widget-setup)
+    ;; Jump to the first widget.
+    (widget-forward 1)))
+
+(defun image-dired-save-information-from-widgets ()
+  "Save information found in `image-dired-widget-list'.
+Use the information in `image-dired-widget-list' to save comments and
+tags to their respective image file.  Internal function used by
+`image-dired-dired-edit-comment-and-tags'."
+  (let (file comment tag-string tag-list lst)
+    (image-dired-write-comments
+     (mapcar
+      (lambda (widget)
+        (setq file (car widget)
+              comment (widget-value (cadr widget)))
+        (cons file comment))
+      image-dired-widget-list))
+    (image-dired-write-tags
+     (dolist (widget image-dired-widget-list lst)
+       (setq file (car widget)
+             tag-string (widget-value (car (cddr widget)))
+             tag-list (split-string tag-string ","))
+       (dolist (tag tag-list)
+         (push (cons file tag) lst))))))
+
+(provide 'image-dired-tags)
+
+;; Local Variables:
+;; nameless-current-name: "image-dired"
+;; End:
+
+;;; image-dired-tags.el ends here
diff --git a/lisp/image/image-dired-util.el b/lisp/image/image-dired-util.el
new file mode 100644
index 0000000000..bc7a355262
--- /dev/null
+++ b/lisp/image/image-dired-util.el
@@ -0,0 +1,186 @@
+;;; image-dired-util.el --- util functions for Image-Dired  -*- 
lexical-binding: t -*-
+
+;; Copyright (C) 2005-2022 Free Software Foundation, Inc.
+
+;; Author: Mathias Dahl <mathias.rem0veth1s.dahl@gmail.com>
+;; Maintainer: Stefan Kangas <stefankangas@gmail.com>
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; See the description of the `image-dired' package.
+
+;;; Code:
+
+(require 'xdg)
+(eval-when-compile (require 'cl-lib))
+
+(defvar image-dired-dir)
+(defvar image-dired-thumbnail-storage)
+
+(defconst image-dired--thumbnail-standard-sizes
+  '( standard standard-large
+     standard-x-large standard-xx-large)
+  "List of symbols representing thumbnail sizes in Thumbnail Managing 
Standard.")
+
+(defvar image-dired-debug nil
+  "Non-nil means enable debug messages.")
+
+(defun image-dired-debug (&rest args)
+  "Display debug message ARGS when `image-dired-debug' is non-nil."
+  (when image-dired-debug
+    (apply #'message args)))
+
+(defun image-dired-dir ()
+  "Return the current thumbnail directory (from variable `image-dired-dir').
+Create the thumbnail directory if it does not exist."
+  (let ((image-dired-dir
+         (file-name-as-directory
+          (expand-file-name image-dired-dir))))
+    (unless (file-directory-p image-dired-dir)
+      (with-file-modes #o700
+        (make-directory image-dired-dir t))
+      (message "Thumbnail directory created: %s" image-dired-dir))
+    image-dired-dir))
+
+(defun image-dired-thumb-name (file)
+  "Return absolute file name for thumbnail FILE.
+Depending on the value of `image-dired-thumbnail-storage', the
+file name of the thumbnail will vary:
+- For `use-image-dired-dir', make a SHA1-hash of the image file's
+  directory name and add that to make the thumbnail file name
+  unique.
+- For `per-directory' storage, just add a subdirectory.
+- For `standard' storage, produce the file name according to the
+  Thumbnail Managing Standard.  Among other things, an MD5-hash
+  of the image file's directory name will be added to the
+  filename.
+See also `image-dired-thumbnail-storage'."
+  (let ((file (expand-file-name file)))
+    (cond ((memq image-dired-thumbnail-storage
+                 image-dired--thumbnail-standard-sizes)
+           (let ((thumbdir (cl-case image-dired-thumbnail-storage
+                             (standard "thumbnails/normal")
+                             (standard-large "thumbnails/large")
+                             (standard-x-large "thumbnails/x-large")
+                             (standard-xx-large "thumbnails/xx-large"))))
+             (expand-file-name
+              ;; MD5 is mandated by the Thumbnail Managing Standard.
+              (concat (md5 (concat "file://" file)) ".png")
+              (expand-file-name thumbdir (xdg-cache-home)))))
+          ((or (eq 'image-dired image-dired-thumbnail-storage)
+               ;; Maintained for backwards compatibility:
+               (eq 'use-image-dired-dir image-dired-thumbnail-storage))
+           (expand-file-name (format "%s.jpg" (sha1 file))
+                             (image-dired-dir)))
+          ((eq 'per-directory image-dired-thumbnail-storage)
+           (expand-file-name (format "%s.thumb.jpg"
+                                     (file-name-nondirectory file))
+                             (expand-file-name
+                              ".image-dired"
+                              (file-name-directory file)))))))
+
+(defvar image-dired-thumbnail-buffer "*image-dired*"
+  "Image-Dired's thumbnail buffer.")
+
+(defvar image-dired-display-image-buffer "*image-dired-display-image*"
+  "Where larger versions of the images are display.")
+
+(defun image-dired-original-file-name ()
+  "Get original file name for thumbnail or display image at point."
+  (get-text-property (point) 'original-file-name))
+
+(defun image-dired-file-name-at-point ()
+  "Get abbreviated file name for thumbnail or display image at point."
+  (when-let ((f (image-dired-original-file-name)))
+    (abbreviate-file-name f)))
+
+(defun image-dired-associated-dired-buffer ()
+  "Get associated Dired buffer at point."
+  (get-text-property (point) 'associated-dired-buffer))
+
+(defmacro image-dired--with-dired-buffer (&rest body)
+  "Run BODY in associated Dired buffer.
+Should be used by commands in `image-dired-thumbnail-mode'."
+  (declare (indent defun) (debug t))
+  (let ((file (make-symbol "file"))
+        (dired-buf (make-symbol "dired-buf")))
+    `(let ((,file (image-dired-original-file-name))
+           (,dired-buf (image-dired-associated-dired-buffer)))
+       (unless ,file
+         (error "No image at point"))
+       (unless (and ,dired-buf (buffer-live-p ,dired-buf))
+         (error "Cannot find associated Dired buffer for image: %s" ,file))
+       (with-current-buffer ,dired-buf
+         ,@body))))
+
+(defun image-dired-get-buffer-window (buf)
+  "Return window where buffer BUF is."
+  (get-window-with-predicate
+   (lambda (window)
+     (equal (window-buffer window) buf))
+   nil t))
+
+(defun image-dired-display-window ()
+  "Return window where `image-dired-display-image-buffer' is visible."
+  ;; This is obsolete as it is currently unused.  Once the window
+  ;; handling gets a rethink, there may or may not be a need to
+  ;; un-obsolete it again.
+  (declare (obsolete nil "29.1"))
+  (get-window-with-predicate
+   (lambda (window)
+     (equal (buffer-name (window-buffer window)) 
image-dired-display-image-buffer))
+   nil t))
+
+(defun image-dired-thumbnail-window ()
+  "Return window where `image-dired-thumbnail-buffer' is visible."
+  (get-window-with-predicate
+   (lambda (window)
+     (equal (buffer-name (window-buffer window)) image-dired-thumbnail-buffer))
+   nil t))
+
+(defun image-dired-associated-dired-buffer-window ()
+  "Return window where associated Dired buffer is visible."
+  ;; This is obsolete as it is currently unused.  Once the window
+  ;; handling gets a rethink, there may or may not be a need to
+  ;; un-obsolete it again.
+  (declare (obsolete nil "29.1"))
+  (let (buf)
+    (if (image-dired-image-at-point-p)
+        (progn
+          (setq buf (image-dired-associated-dired-buffer))
+          (get-window-with-predicate
+           (lambda (window)
+             (equal (window-buffer window) buf))))
+      (error "No thumbnail image at point"))))
+
+(defun image-dired-image-at-point-p ()
+  "Return non-nil if there is an `image-dired' thumbnail at point."
+  (get-text-property (point) 'image-dired-thumbnail))
+
+(defun image-dired-window-width-pixels (window)
+  "Calculate WINDOW width in pixels."
+  (declare (obsolete window-body-width "29.1"))
+  (* (window-width window) (frame-char-width)))
+
+(provide 'image-dired-util)
+
+;; Local Variables:
+;; nameless-current-name: "image-dired"
+;; End:
+
+;;; image-dired-util.el ends here
diff --git a/lisp/image/image-dired.el b/lisp/image/image-dired.el
new file mode 100644
index 0000000000..d4fd3c62db
--- /dev/null
+++ b/lisp/image/image-dired.el
@@ -0,0 +1,2013 @@
+;;; image-dired.el --- use dired to browse and manipulate your images -*- 
lexical-binding: t -*-
+
+;; Copyright (C) 2005-2022 Free Software Foundation, Inc.
+
+;; Author: Mathias Dahl <mathias.rem0veth1s.dahl@gmail.com>
+;; Maintainer: Stefan Kangas <stefankangas@gmail.com>
+;; Version: 0.5
+;; Keywords: multimedia
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; BACKGROUND
+;; ==========
+;;
+;;  I needed a program to browse, organize and tag my pictures.  I got
+;; tired of the old gallery program I used as it did not allow
+;; multi-file operations easily.  Also, it put things out of my
+;; control.  Image viewing programs I tested did not allow multi-file
+;; operations or did not do what I wanted it to.
+;;
+;;  So, I got the idea to use the wonderful functionality of Emacs and
+;; `dired' to do it.  It would allow me to do almost anything I wanted,
+;; which is basically just to browse all my pictures in an easy way,
+;; letting me manipulate and tag them in various ways.  `dired' already
+;; provide all the file handling and navigation facilities; I only
+;; needed to add some functions to display the images.
+;;
+;;  I briefly tried out thumbs.el, and although it seemed more
+;; powerful than this package, it did not work the way I wanted to.  It
+;; was too slow to create thumbnails of all files in a directory (I
+;; currently keep all my 2000+ images in the same directory) and
+;; browsing the thumbnail buffer was slow too.  image-dired.el will not
+;; create thumbnails until they are needed and the browsing is done
+;; quickly and easily in Dired.  I copied a great deal of ideas and
+;; code from there though...  :)
+;;
+;;  `image-dired' stores the thumbnail files in `image-dired-dir'
+;; using the file name format ORIGNAME.thumb.ORIGEXT.  For example
+;; ~/.emacs.d/image-dired/myimage01.thumb.jpg.  The "database" is for
+;; now just a plain text file with the following format:
+;;
+;; file-name-non-directory;comment:comment-text;tag1;tag2;tag3;...;tagN
+;;
+;; PREREQUISITES
+;; =============
+;;
+;; * The GraphicsMagick or ImageMagick package; Image-Dired uses
+;;   whichever is available.
+;;
+;;   A) For GraphicsMagick, `gm' is used.
+;;      Find it here:  http://www.graphicsmagick.org/
+;;
+;;   B) For ImageMagick, `convert' and `mogrify' are used.
+;;      Find it here:  https://www.imagemagick.org.
+;;
+;; * For non-lossy rotation of JPEG images, the JpegTRAN program is
+;;   needed.
+;;
+;; * For `image-dired-set-exif-data' to work, the command line tool `exiftool' 
is
+;;   needed.  It can be found here: https://exiftool.org/.  This
+;;   function is, among other things, used for writing comments to
+;;   image files using `image-dired-thumbnail-set-image-description'.
+;;
+;;
+;; USAGE
+;; =====
+;;
+;; This information has been moved to the manual.  Type `C-h r' to open
+;; the Emacs manual and go to the node Thumbnails by typing `g
+;; Image-Dired RET'.
+;;
+;; Quickstart: M-x image-dired RET DIRNAME RET
+;;
+;; where DIRNAME is a directory containing image files.
+;;
+;; LIMITATIONS
+;; ===========
+;;
+;; * Supports all image formats that Emacs and convert supports, but
+;;   the thumbnails are hard-coded to JPEG or PNG format.  It uses
+;;   JPEG by default, but can optionally follow the Thumbnail Managing
+;;   Standard (v0.9.0, Dec 2020), which mandates PNG.  See the user
+;;   option `image-dired-thumbnail-storage'.
+;;
+;; * WARNING: The "database" format used might be changed so keep a
+;;   backup of `image-dired-tags-db-file' when testing new versions.
+;;
+;; TODO
+;; ====
+;;
+;; * Investigate if it is possible to also write the tags to the image
+;;   files.
+;;
+;; * From thumbs.el: Add an option for clean-up/max-size functionality
+;;   for thumbnail directory.
+;;
+;; * Add `image-dired-display-thumbs-ring' and functions to cycle that.  Find 
out
+;;   which is best, saving old batch just before inserting new, or
+;;   saving the current batch in the ring when inserting it.  Adding
+;;   it probably needs rewriting `image-dired-display-thumbs' to be more 
general.
+;;
+;; * Find some way of toggling on and off really nice keybindings in
+;;   Dired (for example, using C-n or <down> instead of C-S-n).
+;;   Richard suggested that we could keep C-t as prefix for
+;;   image-dired commands as it is currently not used in Dired.  He
+;;   also suggested that `dired-next-line' and `dired-previous-line'
+;;   figure out if image-dired is enabled in the current buffer and,
+;;   if it is, call `image-dired-dired-next-line' and 
`image-dired-dired-previous-line',
+;;   respectively.  Update: This is partly done; some bindings have
+;;   now been added to Dired.
+;;
+;; * In some way keep track of buffers and windows and stuff so that
+;;   it works as the user expects.
+;;
+;; * More/better documentation.
+
+;;; Code:
+
+(require 'dired)
+(require 'image-mode)
+(require 'widget)
+(require 'xdg)
+
+(eval-when-compile
+  (require 'cl-lib)
+  (require 'wid-edit))
+
+(require 'image-dired-external)
+(require 'image-dired-tags)
+(require 'image-dired-util)
+
+
+;;; Customizable variables
+
+(defgroup image-dired nil
+  "Use Dired to browse your images as thumbnails, and more."
+  :prefix "image-dired-"
+  :link '(info-link "(emacs) Image-Dired")
+  :group 'multimedia)
+
+(defcustom image-dired-dir (locate-user-emacs-file "image-dired/")
+  "Directory where thumbnail images are stored.
+
+The value of this option is ignored if Image-Dired is customized
+to use the Thumbnail Managing Standard; they will be saved in
+\"$XDG_CACHE_HOME/thumbnails/\" instead.  See
+`image-dired-thumbnail-storage'."
+  :type 'directory)
+
+(defcustom image-dired-thumbnail-storage 'image-dired
+  "How `image-dired' stores thumbnail files.
+There are three ways that Image-Dired can store and generate
+thumbnails:
+
+ 1. According to the \"Thumbnail Managing Standard\", which allows
+    sharing of thumbnails across different programs.  Thumbnails
+    will be stored in \"$XDG_CACHE_HOME/thumbnails/\"
+
+    Set this user option to one of the following values:
+
+    - `standard' means use thumbnails sized 128x128.
+    - `standard-large' means use thumbnails sized 256x256.
+    - `standard-x-large' means use thumbnails sized 512x512.
+    - `standard-xx-large' means use thumbnails sized 1024x1024.
+
+ 2. In the Image-Dired specific directory indicated by
+    `image-dired-dir'.
+
+    Set this user option to `image-dired' to use it (or
+    `use-image-dired-dir', which means the same thing for
+    backwards-compatibility reasons).
+
+ 3. In a subdirectory \".image-dired\" in the same directory
+    where the image files are.
+
+    Set this user option to `per-directory' to use it.
+
+To change the default size of thumbnails with (2) and (3) above,
+customize `image-dired-thumb-size'.
+
+With Thumbnail Managing Standard, save thumbnails in the PNG
+format, as mandated by that standard, and otherwise as JPEG.
+
+For more information on the Thumbnail Managing Standard, see:
+https://specifications.freedesktop.org/thumbnail-spec/thumbnail-spec-latest.html";
+  :type '(choice :tag "How to store thumbnail files"
+                 (const :tag "Use image-dired-dir" image-dired)
+                 (const :tag "Thumbnail Managing Standard (normal 128x128)"
+                        standard)
+                 (const :tag "Thumbnail Managing Standard (large 256x256)"
+                        standard-large)
+                 (const :tag "Thumbnail Managing Standard (larger 512x512)"
+                        standard-x-large)
+                 (const :tag "Thumbnail Managing Standard (extra large 
1024x1024)"
+                        standard-xx-large)
+                 (const :tag "Per-directory" per-directory))
+  :version "29.1")
+;;;###autoload(put 'image-dired-thumbnail-storage 'safe-local-variable (lambda 
(x) (eq x 'per-directory)))
+
+(define-obsolete-variable-alias 'image-dired-db-file
+  'image-dired-tags-db-file "29.1")
+(defcustom image-dired-tags-db-file
+  (expand-file-name ".image-dired_db" image-dired-dir)
+  "Database file where file names and their associated tags are stored."
+  :type 'file)
+
+(defcustom image-dired-rotate-original-ask-before-overwrite t
+  "Confirm overwrite of original file after rotate operation.
+If non-nil, ask user for confirmation before overwriting the
+original file with `image-dired-temp-rotate-image-file'."
+  :type 'boolean)
+
+(defcustom image-dired-thumb-size
+  ;; This is ignored when using the Thumbnail Managing Standard, but
+  ;; this provides a better default (e.g., when 'image-dired-thumbnail-storage'
+  ;; is `image-dired' in a directory local variables).
+  (pcase image-dired-thumbnail-storage
+    ('standard 128)
+    ('standard-large 256)
+    ('standard-x-large 512)
+    ('standard-xx-large 1024)
+    (_ 128))
+  "Default size of thumbnails in pixels.
+The value of this option is ignored if Image-Dired is customized
+to use the Thumbnail Managing Standard; the standard sizes will
+be used instead.  See `image-dired-thumbnail-storage'."
+  :type 'natnum
+  :version "29.1")
+
+(defcustom image-dired-thumb-relief 2
+  "Size of button-like border around thumbnails."
+  :type 'natnum)
+
+(defcustom image-dired-thumb-margin 2
+  "Size of the margin around thumbnails.
+This is where you see the cursor."
+  :type 'natnum)
+
+(defcustom image-dired-thumb-visible-marks t
+  "Make marks and flags visible in thumbnail buffer.
+If non-nil, apply the `image-dired-thumb-mark' face to marked
+images and `image-dired-thumb-flagged' to images flagged for
+deletion."
+  :type 'boolean
+  :version "28.1")
+
+(defcustom image-dired-line-up-method 'dynamic
+  "Default method for line-up of thumbnails in thumbnail buffer.
+Used by `image-dired-display-thumbs' and other functions that needs
+to line-up thumbnails.  Dynamic means to use the available width of
+the window containing the thumbnail buffer, Fixed means to use
+`image-dired-thumbs-per-row', Interactive is for asking the user,
+and No line-up means that no automatic line-up will be done."
+  :type '(choice :tag "Default line-up method"
+                 (const :tag "Dynamic" dynamic)
+                 (const :tag "Fixed" fixed)
+                 (const :tag "Interactive" interactive)
+                 (const :tag "No line-up" none)))
+
+(defcustom image-dired-thumbs-per-row 3
+  "Number of thumbnails to display per row in thumb buffer."
+  :type 'natnum)
+
+(defcustom image-dired-track-movement t
+  "The current state of the tracking and mirroring.
+For more information, see the documentation for
+`image-dired-toggle-movement-tracking'."
+  :type 'boolean)
+
+(defcustom image-dired-display-properties-format "%n %d/%f %s %t %c"
+  "Display format for thumbnail properties.
+This is used for the header line in the Image-Dired buffer.
+
+The following %-specs are replaced by `format-spec' before
+displaying:
+
+  \"%f\"  The file name (without a directory) of the
+          original image file.
+  \"%n\"  The number of this image out of the total (e.g. 1/10).
+  \"%b\"  The associated Dired buffer name.
+  \"%d\"  The name of the directory that the file is in.
+  \"%s\"  The image file size.
+  \"%t\"  The list of tags (from the Image-Dired database).
+  \"%c\"  The comment (from the Image-Dired database)."
+  :type 'string
+  :safe #'stringp
+  :version "29.1")
+
+(defcustom image-dired-external-viewer
+  ;; TODO: Use mailcap, dired-guess-shell-alist-default,
+  ;; dired-view-command-alist.
+  (cond ((executable-find "display") "display")
+        ((executable-find "feh") "feh")
+        ((executable-find "gm") "gm display")
+        ((executable-find "xli") "xli")
+        ((executable-find "qiv") "qiv -t")
+        ((executable-find "xloadimage") "xloadimage"))
+  "Name of external viewer.
+Including parameters.  Used when displaying original image from
+`image-dired-thumbnail-mode'."
+  :version "29.1"
+  :type '(choice string
+                 (const :tag "Not Set" nil)))
+
+(defcustom image-dired-main-image-directory
+  (or (xdg-user-dir "PICTURES") "~/pics/")
+  "Name of main image directory, if any.
+Used by `image-dired-copy-with-exif-file-name'."
+  :type 'string
+  :version "29.1")
+
+(defcustom image-dired-show-all-from-dir-max-files 1000
+  "Maximum number of files in directory before prompting.
+
+If there are more image files than this in a selected directory,
+the `image-dired-show-all-from-dir' command will ask for
+confirmation before creating the thumbnail buffer.  If this
+variable is nil, it will never ask."
+  :type '(choice integer
+                 (const :tag "Disable warning" nil))
+  :version "29.1")
+
+(defcustom image-dired-marking-shows-next t
+  "If non-nil, marking, unmarking or flagging an image shows the next image.
+
+This affects the following commands:
+\\<image-dired-thumbnail-mode-map>
+    `image-dired-flag-thumb-original-file'   (bound to 
\\[image-dired-flag-thumb-original-file])
+    `image-dired-mark-thumb-original-file'   (bound to 
\\[image-dired-mark-thumb-original-file])
+    `image-dired-unmark-thumb-original-file' (bound to 
\\[image-dired-unmark-thumb-original-file])"
+  :type 'boolean
+  :version "29.1")
+
+
+;;; Faces
+
+;;;; Header line
+
+(defface image-dired-thumb-header-file-name
+  '((default :weight bold))
+  "Face for the file name in the header line of the thumbnail buffer."
+  :version "29.1")
+
+(defface image-dired-thumb-header-directory-name
+  '((default :inherit header-line))
+  "Face for the directory name in the header line of the thumbnail buffer."
+  :version "29.1")
+
+(defface image-dired-thumb-header-file-size
+  '((((class color) (min-colors 88)) :foreground "cadet blue")
+    (((class color) (min-colors 16)) :foreground "black")
+    (default :inherit header-line))
+  "Face for the file size in the header line of the thumbnail buffer."
+  :version "29.1")
+
+(defface image-dired-thumb-header-image-count
+  '((default :inherit header-line))
+  "Face for the image count in the header line of the thumbnail buffer."
+  :version "29.1")
+
+;;;; Thumbnail buffer
+
+(defface image-dired-thumb-mark
+  '((((class color) (min-colors 16)) :background "DarkOrange")
+    (((class color)) :foreground "yellow")
+    (default :inherit header-line))
+  "Face for marked images in thumbnail buffer."
+  :version "29.1")
+
+(defface image-dired-thumb-flagged
+  '((((class color) (min-colors 88) (background light)) :background "Red3")
+    (((class color) (min-colors 88) (background dark))  :background "Pink")
+    (((class color) (min-colors 16) (background light)) :background "Red3")
+    (((class color) (min-colors 16) (background dark))  :background "Pink")
+    (((class color) (min-colors 8)) :background "red")
+    (t :inverse-video t))
+  "Face for images flagged for deletion in thumbnail buffer."
+  :version "29.1")
+
+
+;;; Util functions
+
+(defun image-dired--file-name-regexp ()
+  (let ((image-file-name-extensions
+         (append '("pdf") image-file-name-extensions)))
+    (image-file-name-regexp)))
+
+(defun image-dired-insert-image (file type relief margin)
+  "Insert image FILE of image TYPE, using RELIEF and MARGIN, at point."
+  (let ((i `(image :type ,type
+                   :file ,file
+                   :relief ,relief
+                   :margin ,margin)))
+    (insert-image i)))
+
+(defun image-dired--get-create-thumbnail-file (file)
+  "Return the image descriptor for a thumbnail of image file FILE."
+  (unless (string-match-p (image-dired--file-name-regexp) file)
+    (error "%s is not a valid image file" file))
+  (let* ((thumb-file (image-dired-thumb-name file))
+         (thumb-attr (file-attributes thumb-file)))
+    (if (or (not thumb-attr)
+            (time-less-p (file-attribute-modification-time thumb-attr)
+                         (file-attribute-modification-time
+                          (file-attributes file))))
+        (image-dired-create-thumb file thumb-file)
+      (image-dired-debug "Found thumb for %s: %s"
+                         (file-name-nondirectory file)
+                         (file-name-nondirectory thumb-file)))
+    thumb-file))
+
+(defun image-dired-insert-thumbnail ( file original-file-name
+                           associated-dired-buffer image-number)
+  "Insert thumbnail image FILE.
+Add text properties ORIGINAL-FILE-NAME, ASSOCIATED-DIRED-BUFFER
+and IMAGE-NUMBER."
+  (let (beg end)
+    (setq beg (point))
+    (image-dired-insert-image
+     file
+     ;; Thumbnails are created asynchronously, so we might not yet
+     ;; have a file.  But if it exists, it might have been cached from
+     ;; before and we should use it instead of our current settings.
+     (or (and (file-exists-p file)
+              (image-type-from-file-header file))
+         (and (memq image-dired-thumbnail-storage
+                    image-dired--thumbnail-standard-sizes)
+              'png)
+         'jpeg)
+     image-dired-thumb-relief
+     image-dired-thumb-margin)
+    (setq end (point))
+    (add-text-properties
+     beg end
+     (list 'image-dired-thumbnail t
+           ;; Disable `image-map' on thumbnails.
+           'keymap nil
+           'original-file-name original-file-name
+           'associated-dired-buffer associated-dired-buffer
+           'image-number image-number
+           'tags (image-dired-list-tags original-file-name)
+           'mouse-face 'highlight
+           'comment (image-dired-get-comment original-file-name)))))
+
+(defmacro image-dired--with-marked (&rest body)
+  "Eval BODY with point on each marked thumbnail.
+If no marked file could be found, execute BODY on the current
+thumbnail.  It's expected that a thumbnail is always followed
+by exactly one space or one newline character."
+  `(with-current-buffer image-dired-thumbnail-buffer
+     (let (found)
+       (save-mark-and-excursion
+         (goto-char (point-min))
+         (while (not (eobp))
+           (when (image-dired-thumb-file-marked-p)
+             (setq found t)
+             ,@body)
+           (forward-char 2)))
+       (unless found
+         ,@body))))
+
+(defun image-dired-create-thumbnail-buffer ()
+  "Create thumb buffer and set `image-dired-thumbnail-mode'."
+  (let ((buf (get-buffer-create image-dired-thumbnail-buffer)))
+    (with-current-buffer buf
+      (setq buffer-read-only t)
+      (if (not (eq major-mode 'image-dired-thumbnail-mode))
+          (image-dired-thumbnail-mode)))
+    buf))
+
+(defvar image-dired-saved-window-configuration nil
+  "Saved window configuration.")
+
+
+;;; Starting Image-Dired
+
+;;;###autoload
+(defun image-dired-dired-with-window-configuration (dir &optional arg)
+  "Open directory DIR and create a default window configuration.
+
+Convenience command that:
+
+ - Opens Dired in folder DIR
+ - Splits windows in most useful (?) way
+ - Sets `truncate-lines' to t
+
+After the command has finished, you would typically mark some
+image files in Dired and type
+\\[image-dired-display-thumbs] (`image-dired-display-thumbs').
+
+If called with prefix argument ARG, skip splitting of windows.
+
+The current window configuration is saved and can be restored by
+calling `image-dired-restore-window-configuration'."
+  (interactive "DDirectory: \nP")
+  (let ((buf (image-dired-create-thumbnail-buffer))
+        (buf2 (get-buffer-create image-dired-display-image-buffer)))
+    (setq image-dired-saved-window-configuration
+          (current-window-configuration))
+    (dired dir)
+    (delete-other-windows)
+    (when (not arg)
+      (split-window-right)
+      (setq truncate-lines t)
+      (save-excursion
+        (other-window 1)
+        (pop-to-buffer-same-window buf)
+        (select-window (split-window-below))
+        (pop-to-buffer-same-window buf2)
+        (other-window -2)))))
+
+(defun image-dired-restore-window-configuration ()
+  "Restore window configuration.
+Restore any changes to the window configuration made by calling
+`image-dired-dired-with-window-configuration'."
+  (interactive nil image-dired-thumbnail-mode)
+  (if image-dired-saved-window-configuration
+      (set-window-configuration image-dired-saved-window-configuration)
+    (message "No saved window configuration")))
+
+(defun image-dired--line-up-with-method ()
+  "Line up thumbnails according to `image-dired-line-up-method'."
+  (cond ((eq 'dynamic image-dired-line-up-method)
+         (image-dired-line-up-dynamic))
+        ((eq 'fixed image-dired-line-up-method)
+         (image-dired-line-up))
+        ((eq 'interactive image-dired-line-up-method)
+         (image-dired-line-up-interactive))
+        ((eq 'none image-dired-line-up-method)
+         nil)
+        (t
+         (image-dired-line-up-dynamic))))
+
+(defvar-local image-dired--number-of-thumbnails nil)
+
+;;;###autoload
+(defun image-dired-display-thumbs (&optional arg append do-not-pop)
+  "Display thumbnails of all marked files, in `image-dired-thumbnail-buffer'.
+If a thumbnail image does not exist for a file, it is created on the
+fly.  With prefix argument ARG, display only thumbnail for file at
+point (this is useful if you have marked some files but want to show
+another one).
+
+Recommended usage is to split the current frame horizontally so that
+you have the Dired buffer in the left window and the
+`image-dired-thumbnail-buffer' buffer in the right window.
+
+With optional argument APPEND, append thumbnail to thumbnail buffer
+instead of erasing it first.
+
+Optional argument DO-NOT-POP controls if `pop-to-buffer' should be
+used or not.  If non-nil, use `display-buffer' instead of
+`pop-to-buffer'.  This is used from functions like
+`image-dired-next-line-and-display' and
+`image-dired-previous-line-and-display' where we do not want the
+thumbnail buffer to be selected."
+  (interactive "P" nil dired-mode)
+  (setq image-dired--generate-thumbs-start  (current-time))
+  (let ((buf (image-dired-create-thumbnail-buffer))
+        files dired-buf)
+    (if arg
+        (setq files (list (dired-get-filename)))
+      (setq files (dired-get-marked-files)))
+    (setq dired-buf (current-buffer))
+    (with-current-buffer buf
+      (let ((inhibit-read-only t))
+        (if (not append)
+            (progn
+              (setq image-dired--number-of-thumbnails 0)
+              (erase-buffer))
+          (goto-char (point-max)))
+        (dolist (file files)
+          (let ((thumb (image-dired--get-create-thumbnail-file file)))
+            (image-dired-insert-thumbnail
+             thumb file dired-buf
+             (cl-incf image-dired--number-of-thumbnails)))))
+      (if do-not-pop
+          (display-buffer buf)
+        (pop-to-buffer buf))
+      (image-dired--line-up-with-method))))
+
+;;;###autoload
+(defun image-dired-show-all-from-dir (dir)
+  "Make a thumbnail buffer for all images in DIR and display it.
+Any file matching `image-dired--file-name-regexp' is considered an
+image file.
+
+If the number of image files in DIR exceeds
+`image-dired-show-all-from-dir-max-files', ask for confirmation
+before creating the thumbnail buffer.  If that variable is nil,
+never ask for confirmation."
+  (interactive "DShow thumbnails for directory: ")
+  (dired dir)
+  (dired-mark-files-regexp (image-dired--file-name-regexp))
+  (let ((files (dired-get-marked-files nil nil nil t)))
+    (cond ((and (null (cdr files)))
+           (message "No image files in directory"))
+          ((or (not image-dired-show-all-from-dir-max-files)
+               (<= (length (cdr files)) 
image-dired-show-all-from-dir-max-files)
+               (and (> (length (cdr files)) 
image-dired-show-all-from-dir-max-files)
+                    (y-or-n-p
+                     (format
+                      "Directory contains more than %d image files.  Proceed?"
+                      image-dired-show-all-from-dir-max-files))))
+           (image-dired-display-thumbs)
+           (let ((inhibit-message t))
+             (dired-unmark-all-marks))
+           (pop-to-buffer image-dired-thumbnail-buffer)
+           (setq default-directory dir)
+           (image-dired--update-header-line))
+          (t (message "Image-Dired canceled")))))
+
+;;;###autoload
+(defalias 'image-dired 'image-dired-show-all-from-dir)
+
+
+;;; Movement tracking
+
+(defun image-dired-track-original-file ()
+  "Track the original file in the associated Dired buffer.
+See `image-dired-toggle-movement-tracking'.  Interactive use is
+only useful if `image-dired-track-movement' is nil."
+  (interactive nil image-dired-thumbnail-mode image-dired-image-mode)
+  (let ((file-name (image-dired-original-file-name)))
+    (image-dired--with-dired-buffer
+      (if (not (dired-goto-file file-name))
+          (message "Could not find image in Dired buffer for tracking")
+        (when-let (window (image-dired-get-buffer-window (current-buffer)))
+          (set-window-point window (point)))))))
+
+(defun image-dired-toggle-movement-tracking ()
+  "Turn on and off `image-dired-track-movement'.
+Tracking of the movements between thumbnail and Dired buffer so that
+they are \"mirrored\" in the dired buffer.  When this is on, moving
+around in the thumbnail or dired buffer will find the matching
+position in the other buffer."
+  (interactive nil image-dired-thumbnail-mode image-dired-image-mode)
+  (setq image-dired-track-movement (not image-dired-track-movement))
+  (message "Movement tracking %s" (if image-dired-track-movement "on" "off")))
+
+
+;;; Navigation
+
+(defun image-dired-forward-image (&optional arg wrap-around)
+  "Move to next image in the thumbnail buffer.
+Optional prefix ARG says how many images to move; the default is
+one image.  Negative means move backwards.
+On reaching end or beginning of buffer, stop and show a message.
+
+If optional argument WRAP-AROUND is non-nil, wrap around: if
+point is on the last image, move to the last one and vice versa."
+  (interactive "p" image-dired-thumbnail-mode)
+  (setq arg (or arg 1))
+  (let (pos)
+    (dotimes (_ (abs arg))
+      (if (and (not (if (> arg 0) (eobp) (bobp)))
+               (save-excursion
+                 (forward-char (if (> arg 0) 1 -1))
+                 (while (and (not (if (> arg 0) (eobp) (bobp)))
+                             (not (image-dired-image-at-point-p)))
+                   (forward-char (if (> arg 0) 1 -1)))
+                 (setq pos (point))
+                 (image-dired-image-at-point-p)))
+          (goto-char pos)
+        (if wrap-around
+            (goto-char (if (> arg 0)
+                           (point-min)
+                         ;; There are two spaces after the last image.
+                         (- (point-max) 2)))
+          (message "At %s image" (if (> arg 0) "last" "first"))))))
+  (image-dired--update-header-line)
+  (when image-dired-track-movement
+    (image-dired-track-original-file)))
+
+(defun image-dired-backward-image (&optional arg)
+  "Move to previous image in the thumbnail buffer.
+Optional prefix ARG says how many images to move; the default is
+one image.  Negative means move forward.
+On reaching end or beginning of buffer, stop and show a message."
+  (interactive "p" image-dired-thumbnail-mode)
+  (image-dired-forward-image (- (or arg 1))))
+
+(defun image-dired--movement-ensure-point-pos (&optional reverse)
+  "Ensure point is on an image."
+  (while (and (not (image-at-point-p))
+              (not (if reverse (bobp) (eobp))))
+    (forward-char (if reverse -1 1))))
+
+(defmacro image-dired--movement-command (to &optional reverse)
+  `(progn
+     (goto-char ,to)
+     (image-dired--movement-ensure-point-pos ,reverse)
+     (when image-dired-track-movement
+       (image-dired-track-original-file))
+     (image-dired--update-header-line)))
+
+(defmacro image-dired--movement-command-line (&optional reverse)
+  `(image-dired--movement-command
+     (let ((goal-column (current-column)))
+       (forward-line ,(if reverse -1 1))
+       (move-to-column goal-column)
+       (point))
+     ,reverse))
+
+(defun image-dired-next-line ()
+  "Move to next line in the thumbnail buffer."
+  (interactive nil image-dired-thumbnail-mode)
+  (image-dired--movement-command-line))
+
+(defun image-dired-previous-line ()
+  "Move to previous line in the thumbnail buffer."
+  (interactive nil image-dired-thumbnail-mode)
+  (image-dired--movement-command-line 'reverse))
+
+(defun image-dired-beginning-of-buffer ()
+  "Move to the first image in the thumbnail buffer."
+  (interactive nil image-dired-thumbnail-mode)
+  (image-dired--movement-command (point-min)))
+
+(defun image-dired-end-of-buffer ()
+  "Move to the last image in the thumbnail buffer."
+  (interactive nil image-dired-thumbnail-mode)
+  (image-dired--movement-command (point-max) 'reverse))
+
+(defun image-dired-move-beginning-of-line ()
+  "Move to the beginning of current line in thumbnail buffer."
+  (interactive nil image-dired-thumbnail-mode)
+  (image-dired--movement-command (pos-bol)))
+
+(defun image-dired-move-end-of-line ()
+  "Move to the end of current line in thumbnail buffer."
+  (interactive nil image-dired-thumbnail-mode)
+  (image-dired--movement-command (pos-eol) 'reverse))
+
+
+;;; Header line
+
+(defun image-dired-format-properties-string (buf file image-count props 
comment)
+  "Format display properties.
+BUF is the associated Dired buffer, FILE is the original image
+file name, IMAGE-COUNT is a string like \"N/M\" where N is the
+number of this image and M is the total number of images, PROPS
+is a stringified list of tags, and COMMENT is the image file's
+comment."
+  (format-spec
+   image-dired-display-properties-format
+   `((?b . ,(or buf ""))
+     (?d . ,(propertize
+             (file-name-nondirectory
+              (directory-file-name
+               (file-name-directory file)))
+             'face 'image-dired-thumb-header-directory-name))
+     (?f . ,(propertize (file-name-nondirectory file)
+                        'face 'image-dired-thumb-header-file-name))
+     (?n . ,(propertize image-count
+                        'face 'image-dired-thumb-header-image-count))
+     (?s . ,(propertize (if (file-exists-p file)
+                            (file-size-human-readable
+                             (file-attribute-size
+                              (file-attributes file)))
+                          "<File missing>")
+                        'face 'image-dired-thumb-header-file-size))
+     (?t . ,(or props ""))
+     (?c . ,(or comment "")))))
+
+(defun image-dired--update-header-line ()
+  "Update image information in the header line."
+  (when (derived-mode-p 'image-dired-thumbnail-mode)
+    (let ((file-name (image-dired-original-file-name))
+          (dired-buf (buffer-name (image-dired-associated-dired-buffer)))
+          (image-count (format "%s/%s"
+                               (get-text-property (point) 'image-number)
+                               image-dired--number-of-thumbnails))
+          (props (string-join (get-text-property (point) 'tags) ", "))
+          (comment (get-text-property (point) 'comment))
+          (message-log-max nil))
+      (when file-name
+        (setq header-line-format
+              (image-dired-format-properties-string
+               dired-buf
+               file-name
+               image-count
+               props
+               comment))))))
+
+
+;;; Marking and flagging
+
+(defun image-dired-dired-file-marked-p (&optional marker)
+  "In Dired, return t if file on current line is marked.
+If optional argument MARKER is non-nil, it is a character to look
+for.  The default is to look for `dired-marker-char'."
+  (setq marker (or marker dired-marker-char))
+  (save-excursion
+    (beginning-of-line)
+    (and (looking-at dired-re-mark)
+         (= (aref (match-string 0) 0) marker))))
+
+(defun image-dired-dired-file-flagged-p ()
+  "In Dired, return t if file on current line is flagged for deletion."
+  (image-dired-dired-file-marked-p dired-del-marker))
+
+(defmacro image-dired--on-file-in-dired-buffer (&rest body)
+  "Run BODY with point on file at point in Dired buffer.
+Should be called from commands in `image-dired-thumbnail-mode'."
+  (declare (indent defun) (debug t))
+  `(if-let ((file-name (image-dired-original-file-name)))
+       (image-dired--with-dired-buffer
+         (when (dired-goto-file file-name)
+           ,@body))
+     (message "No image with correct properties at point")))
+
+(defmacro image-dired--with-thumbnail-buffer (&rest body)
+  (declare (indent defun) (debug t))
+  `(if-let ((buf (get-buffer image-dired-thumbnail-buffer)))
+       (with-current-buffer buf
+         (if-let ((win (get-buffer-window buf)))
+             (with-selected-window win
+               ,@body)
+           ,@body))
+     (user-error "No such buffer: %s" image-dired-thumbnail-buffer)))
+
+(defmacro image-dired--do-mark-command (maybe-next update-mark &rest body)
+  "Run BODY in Dired buffer.
+Helper macro for the mark, unmark and flag commands.
+
+If MAYBE-NEXT is non-nil, show next image according to
+`image-dired-marking-shows-next'.
+
+If UPDATE-MARK is non-nil, also update the mark in the thumbnail
+buffer with `image-dired--thumb-update-mark-at-point'."
+  (declare (indent defun) (debug t))
+  `(image-dired--with-thumbnail-buffer
+     (image-dired--on-file-in-dired-buffer
+       ,@body)
+     ,(when update-mark
+        '(image-dired--thumb-update-mark-at-point))
+     ,(when maybe-next
+        '(if image-dired-marking-shows-next
+             (image-dired-display-next)
+           (image-dired-forward-image)))))
+
+(defun image-dired-mark-thumb-original-file ()
+  "Mark original image file in associated Dired buffer."
+  (interactive nil image-dired-thumbnail-mode image-dired-image-mode)
+  (image-dired--do-mark-command t t
+    (dired-mark 1)))
+
+(defun image-dired-unmark-thumb-original-file ()
+  "Unmark original image file in associated Dired buffer."
+  (interactive nil image-dired-thumbnail-mode image-dired-image-mode)
+  (image-dired--do-mark-command t t
+    (dired-unmark 1)))
+
+(defun image-dired-flag-thumb-original-file ()
+  "Flag original image file for deletion in associated Dired buffer."
+  (interactive nil image-dired-thumbnail-mode image-dired-image-mode)
+  (image-dired--do-mark-command t t
+    (dired-flag-file-deletion 1)))
+
+(defun image-dired-unmark-all-marks ()
+  "Remove all marks from all files in associated Dired buffer.
+Also update the marks in the thumbnail buffer."
+  (interactive nil image-dired-thumbnail-mode image-dired-image-mode)
+  (image-dired--do-mark-command nil t
+    (dired-unmark-all-marks))
+  (image-dired--with-thumbnail-buffer
+    (image-dired--thumb-update-marks)))
+
+(defun image-dired-jump-original-dired-buffer ()
+  "Jump to the Dired buffer associated with the current image file.
+You probably want to use this together with
+`image-dired-track-original-file'."
+  (interactive nil image-dired-thumbnail-mode)
+  (image-dired--with-dired-buffer
+    (if-let ((window (image-dired-get-buffer-window (current-buffer))))
+        (progn
+          (if (not (equal (selected-frame) (window-frame window)))
+              (select-frame-set-input-focus (window-frame window)))
+          (select-window window))
+      (message "Associated Dired buffer not visible"))))
+
+
+;;; Major modes
+
+(defvar-keymap image-dired-thumbnail-mode-map
+  :doc "Keymap for `image-dired-thumbnail-mode'."
+  "d"          #'image-dired-flag-thumb-original-file
+  "<delete>"   #'image-dired-flag-thumb-original-file
+  "m"          #'image-dired-mark-thumb-original-file
+  "u"          #'image-dired-unmark-thumb-original-file
+  "U"          #'image-dired-unmark-all-marks
+  "x"          #'image-dired-do-flagged-delete
+  "."          #'image-dired-track-original-file
+  "<tab>"      #'image-dired-jump-original-dired-buffer
+
+  "g g"        #'image-dired-line-up-dynamic
+  "g f"        #'image-dired-line-up
+  "g i"        #'image-dired-line-up-interactive
+
+  "t t"        #'image-dired-tag-thumbnail
+  "t r"        #'image-dired-tag-thumbnail-remove
+
+  "RET"        #'image-dired-display-this
+  "C-<return>" #'image-dired-thumbnail-display-external
+
+  "L"          #'image-dired-rotate-original-left
+  "R"          #'image-dired-rotate-original-right
+
+  "D"          #'image-dired-thumbnail-set-image-description
+  "S"          #'image-dired-slideshow-start
+  "C-d"        #'image-dired-delete-char
+  "SPC"        #'image-dired-display-next
+  "DEL"        #'image-dired-display-previous
+  "c"          #'image-dired-comment-thumbnail
+  "w"          #'image-dired-copy-filename-as-kill
+  "W"          #'image-dired-wallpaper-set
+
+  ;; Mouse
+  "<mouse-2>"        #'image-dired-mouse-display-image
+  "<double-mouse-1>" #'image-dired-mouse-display-image
+  "<mouse-1>"        #'image-dired-mouse-select-thumbnail
+  "<mouse-3>"        #'image-dired-mouse-select-thumbnail
+  "<down-mouse-1>"   #'image-dired-mouse-select-thumbnail
+  "<down-mouse-2>"   #'image-dired-mouse-select-thumbnail
+  "<down-mouse-3>"   #'image-dired-mouse-select-thumbnail
+  "C-<down-mouse-1>" #'ignore           ; Don't open the buffer menu.
+  "C-<mouse-1>"      #'image-dired-mouse-toggle-mark
+
+  "<remap> <forward-char>"           #'image-dired-forward-image
+  "<remap> <backward-char>"          #'image-dired-backward-image
+  "<remap> <next-line>"              #'image-dired-next-line
+  "<remap> <previous-line>"          #'image-dired-previous-line
+  "<remap> <left-char>"              #'image-dired-backward-image
+  "<remap> <right-char>"             #'image-dired-forward-image
+  "<remap> <beginning-of-buffer>"    #'image-dired-beginning-of-buffer
+  "<remap> <end-of-buffer>"          #'image-dired-end-of-buffer
+  "<remap> <move-beginning-of-line>" #'image-dired-move-beginning-of-line
+  "<remap> <move-end-of-line>"       #'image-dired-move-end-of-line
+
+  :menu
+  '("Image-Dired"
+    ["Display image" image-dired-display-this]
+    ["Display in external viewer" image-dired-thumbnail-display-external]
+    ["Jump to Dired buffer" image-dired-jump-original-dired-buffer]
+    "---"
+    ["Mark image" image-dired-mark-thumb-original-file]
+    ["Unmark image" image-dired-unmark-thumb-original-file]
+    ["Unmark all images" image-dired-unmark-all-marks]
+    ["Flag for deletion" image-dired-flag-thumb-original-file]
+    ["Delete flagged images" image-dired-do-flagged-delete]
+    "---"
+    ["Rotate original right" image-dired-rotate-original-right]
+    ["Rotate original left" image-dired-rotate-original-left]
+    "---"
+    ["Comment thumbnail" image-dired-comment-thumbnail]
+    ["Tag current or marked thumbnails" image-dired-tag-thumbnail]
+    ["Remove tag from current or marked thumbnails"
+     image-dired-tag-thumbnail-remove]
+    ["Start slideshow" image-dired-slideshow-start]
+    "---"
+    ("View Options"
+     ["Toggle movement tracking" image-dired-toggle-movement-tracking
+      :style toggle
+      :selected image-dired-track-movement]
+     "---"
+     ["Line up thumbnails" image-dired-line-up]
+     ["Dynamic line up" image-dired-line-up-dynamic]
+     ["Refresh thumb" image-dired-refresh-thumb])
+    ["Quit" quit-window]))
+
+(define-derived-mode image-dired-thumbnail-mode
+  special-mode "image-dired-thumbnail"
+  "Browse and manipulate thumbnail images using Dired.
+Use `image-dired-minor-mode' to get a nice setup."
+  :interactive nil
+  :group 'image-dired
+  (buffer-disable-undo)
+  (add-hook 'file-name-at-point-functions 'image-dired-file-name-at-point nil 
t)
+  (setq-local window-resize-pixelwise t)
+  (setq-local bookmark-make-record-function #'image-dired-bookmark-make-record)
+  ;; Use approximately as much vertical spacing as horizontal.
+  (setq-local line-spacing (frame-char-width)))
+
+
+;;; image-dired-image-mode
+
+(define-obsolete-variable-alias 'image-dired-display-image-mode-map
+  'image-dired-image-mode-map "29.1")
+(defvar-keymap image-dired-image-mode-map
+  :doc "Keymap for `image-dired-image-mode'."
+  "S"   #'image-dired-slideshow-start
+  "SPC" #'image-dired-display-next
+  "DEL" #'image-dired-display-previous
+  "n"   #'image-dired-display-next
+  "p"   #'image-dired-display-previous
+  "m"   #'image-dired-mark-thumb-original-file
+  "d"   #'image-dired-flag-thumb-original-file
+  "u"   #'image-dired-unmark-thumb-original-file
+  "U"   #'image-dired-unmark-all-marks
+  ;; Disable keybindings from `image-mode-map' that doesn't make sense here.
+  "o" nil)   ; image-save
+
+(define-derived-mode image-dired-image-mode
+  image-mode "image-dired-image-display"
+  "Mode for displaying and manipulating original image.
+Resized or in full-size."
+  :interactive nil
+  :group 'image-dired
+  (setq-local column-number-mode nil)
+  (setq-local line-number-mode nil)
+  (add-hook 'file-name-at-point-functions #'image-dired-file-name-at-point nil 
t))
+
+
+;;; Slideshow
+
+(defcustom image-dired-slideshow-delay 5.0
+  "Seconds to wait before showing the next image in a slideshow.
+This is used by `image-dired-slideshow-start'."
+  :type 'float
+  :version "29.1")
+
+(define-obsolete-variable-alias 'image-dired-slideshow-timer
+  'image-dired--slideshow-timer "29.1")
+(defvar image-dired--slideshow-timer nil
+  "Slideshow timer.")
+
+(defvar image-dired--slideshow-current-delay image-dired-slideshow-delay)
+
+(defun image-dired--slideshow-step ()
+  "Step to the next image in a slideshow."
+  (if-let ((buf (get-buffer image-dired-thumbnail-buffer)))
+      (with-current-buffer buf
+        (image-dired-display-next))
+    (image-dired--slideshow-stop)))
+
+(defun image-dired--slideshow-start-timer ()
+  (image-dired--slideshow-stop-timer)
+  (setq image-dired--slideshow-timer
+        (run-with-timer image-dired--slideshow-current-delay
+                        image-dired--slideshow-current-delay
+                        'image-dired--slideshow-step)))
+
+(defun image-dired--slideshow-stop-timer ()
+  (when image-dired--slideshow-timer
+    (cancel-timer image-dired--slideshow-timer)
+    (setq image-dired--slideshow-timer nil)))
+
+(defun image-dired-slideshow-start (&optional arg)
+  "Start a slideshow, waiting `image-dired-slideshow-delay' seconds between 
images.
+
+With prefix argument ARG, wait that many seconds before going to
+the next image.
+
+With a negative prefix argument, prompt user for the delay."
+  (interactive "P" image-dired-thumbnail-mode image-dired-image-mode)
+  (let ((delay
+         (cond ((not arg)
+                image-dired-slideshow-delay)
+               ((> arg 0)
+                arg)
+               ((<= arg 0)
+                (string-to-number
+                 (let ((delay (number-to-string image-dired-slideshow-delay)))
+                   (read-string
+                    (format-prompt "Delay, in seconds.  Decimals are accepted"
+                                   delay))
+                   delay))))))
+    (image-dired-display-this)
+    (setq image-dired--slideshow-current-delay delay)
+    (add-hook 'post-command-hook 'image-dired--slideshow-stop)))
+
+(defun image-dired--slideshow-show-message (&optional suffix)
+  "Helper function for `image-dired--slideshow-stop'."
+  (message (substitute-command-keys
+            (format
+             (concat
+              "\\[image-dired-display-next] next, "
+              "\\[image-dired-display-previous] previous, "
+              "\\[image-dired-display-this] pause/unpause, "
+              "any other command to stop%s")
+             (or suffix "")))))
+
+(defun image-dired--slideshow-stop ()
+  "Cancel the currently active slideshow."
+  (cond
+   ((memq this-command
+          '( image-dired-slideshow-start
+             image-dired-display-next
+             image-dired-display-previous))
+    (image-dired--slideshow-start-timer)
+    (image-dired--slideshow-show-message))
+   ((eq this-command 'image-dired-display-this)
+    (let ((pause image-dired--slideshow-timer))
+      (if pause
+          (image-dired--slideshow-stop-timer)
+        (image-dired--slideshow-start-timer))
+      (image-dired--slideshow-show-message (and pause "  [PAUSED]"))))
+   (t
+    (image-dired--slideshow-stop-timer)
+    (remove-hook 'post-command-hook 'image-dired--slideshow-stop))))
+
+
+;;; Thumbnail layout and display
+
+(defun image-dired-delete-char ()
+  "Remove current thumbnail from thumbnail buffer and line up."
+  (interactive nil image-dired-thumbnail-mode)
+  (let ((inhibit-read-only t))
+    (delete-char 1))
+  (let ((pos (point)))
+    (image-dired--line-up-with-method)
+    (goto-char pos)))
+
+(defun image-dired-line-up ()
+  "Line up thumbnails according to `image-dired-thumbs-per-row'.
+See also `image-dired-line-up-dynamic'."
+  (interactive nil image-dired-thumbnail-mode)
+  (let ((inhibit-read-only t))
+    (goto-char (point-min))
+    (while (and (not (image-dired-image-at-point-p))
+                (not (eobp)))
+      (delete-char 1))
+    (while (not (eobp))
+      (forward-char)
+      (while (and (not (image-dired-image-at-point-p))
+                  (not (eobp)))
+        (delete-char 1)))
+    (goto-char (point-min))
+    (let ((seen 0)
+          (thumb-prev-pos 0)
+          (thumb-width-chars
+           (ceiling (/ (+ (* 2 image-dired-thumb-relief)
+                          (* 2 image-dired-thumb-margin)
+                          (image-dired--thumb-size))
+                       (float (frame-char-width))))))
+      (while (not (eobp))
+        (forward-char)
+        (if (= image-dired-thumbs-per-row 1)
+            (insert "\n")
+          (cl-incf thumb-prev-pos thumb-width-chars)
+          (insert (propertize " " 'display `(space :align-to ,thumb-prev-pos)))
+          (cl-incf seen)
+          (when (and (= seen (- image-dired-thumbs-per-row 1))
+                     (not (eobp)))
+            (forward-char)
+            (insert "\n")
+            (setq seen 0)
+            (setq thumb-prev-pos 0)))))
+    (goto-char (point-min))))
+
+(defun image-dired-line-up-dynamic ()
+  "Line up thumbnails images dynamically.
+Calculate how many thumbnails fit."
+  (interactive nil image-dired-thumbnail-mode)
+  (let* ((char-width (frame-char-width))
+         (width (window-body-width (image-dired-thumbnail-window) t))
+         (image-dired-thumbs-per-row
+          (/ width
+             (+ (* 2 image-dired-thumb-relief)
+                (* 2 image-dired-thumb-margin)
+                (image-dired--thumb-size)
+                char-width))))
+    (image-dired-line-up)))
+
+(defun image-dired-line-up-interactive ()
+  "Line up thumbnails interactively.
+Ask user how many thumbnails should be displayed per row."
+  (interactive nil image-dired-thumbnail-mode)
+  (let ((image-dired-thumbs-per-row
+         (string-to-number (read-string "How many thumbs per row: "))))
+    (if (not (> image-dired-thumbs-per-row 0))
+        (message "Number must be greater than 0")
+      (image-dired-line-up))))
+
+
+;;; Display image from thumbnail buffer
+
+(defun image-dired-thumbnail-display-external ()
+  "Display original image for thumbnail at point using external viewer."
+  (interactive nil image-dired-thumbnail-mode)
+  (let ((file (image-dired-original-file-name)))
+    (if (not (image-dired-image-at-point-p))
+        (message "No thumbnail at point")
+      (if (not file)
+          (message "No original file name found")
+        (apply #'start-process "image-dired-thumb-external" nil
+               (append (string-split image-dired-external-viewer " ")
+                       (list file)))))))
+
+(defun image-dired-display-image (file &optional _ignored)
+  "Display image FILE in the image buffer window.
+If it is an image, the window will use `image-dired-image-mode'
+which is based on `image-mode'."
+  (declare (advertised-calling-convention (file) "29.1"))
+  (setq file (expand-file-name file))
+  (when (not (file-exists-p file))
+    (error "No such file: %s" file))
+  (let ((buf (get-buffer image-dired-display-image-buffer))
+        (cur-win (selected-window)))
+    (when buf
+      (kill-buffer buf))
+    (when-let ((buf (find-file-noselect file nil t)))
+      (pop-to-buffer buf)
+      (rename-buffer image-dired-display-image-buffer)
+      (if (string-match (image-file-name-regexp) file)
+          (image-dired-image-mode)
+        ;; Support visiting PDF files.
+        (normal-mode))
+      (select-window cur-win))))
+
+(defun image-dired-display-this (&optional arg)
+  "Display current thumbnail's original image in display buffer.
+See documentation for `image-dired-display-image' for more information.
+With prefix argument ARG, display image in its original size."
+  (interactive "P" image-dired-thumbnail-mode)
+  (unless (string-equal major-mode "image-dired-thumbnail-mode")
+    (user-error "Not in `image-dired-thumbnail-mode'"))
+  (let ((file (image-dired-original-file-name)))
+    (cond ((not (image-dired-image-at-point-p))
+           (message "No thumbnail at point"))
+          ((not file)
+           (message "No original file name found"))
+          (t
+           (image-dired-display-image file arg)))))
+
+(defun image-dired-display-next (&optional arg)
+  "Move to the next image in the thumbnail buffer and display it.
+With prefix ARG, move that many thumbnails."
+  (interactive "p" image-dired-thumbnail-mode image-dired-image-mode)
+  (image-dired--with-thumbnail-buffer
+    (image-dired-forward-image arg t)
+    (image-dired-display-this)))
+
+(defun image-dired-display-previous (arg)
+  "Move to the previous image in the thumbnail buffer and display it.
+With prefix ARG, move that many thumbnails."
+  (interactive "p" image-dired-thumbnail-mode image-dired-image-mode)
+  (image-dired-display-next (- arg)))
+
+
+;;; Misc commands
+
+(defun image-dired-rotate-original-left ()
+  "Rotate original image left (counter clockwise) 90 degrees.
+The result of the rotation is displayed in the image display area
+and a confirmation is needed before the original image files is
+overwritten.  This confirmation can be turned off using
+`image-dired-rotate-original-ask-before-overwrite'."
+  (interactive nil image-dired-thumbnail-mode)
+  (image-dired--with-marked
+   (image-dired-rotate-original "270")))
+
+(defun image-dired-rotate-original-right ()
+  "Rotate original image right (clockwise) 90 degrees.
+The result of the rotation is displayed in the image display area
+and a confirmation is needed before the original image files is
+overwritten.  This confirmation can be turned off using
+`image-dired-rotate-original-ask-before-overwrite'."
+  (interactive nil image-dired-thumbnail-mode)
+  (image-dired--with-marked
+   (image-dired-rotate-original "90")))
+
+(defun image-dired-wallpaper-set (file)
+  "Set the wallpaper to FILE in a graphical environment."
+  (interactive (list (image-dired-original-file-name))
+               image-dired-thumbnail-mode)
+  (wallpaper-set file))
+
+(defun image-dired-comment-thumbnail ()
+  "Add comment to current thumbnail in thumbnail buffer."
+  (interactive nil image-dired-thumbnail-mode)
+  (let* ((file (image-dired-original-file-name))
+         (comment (image-dired-read-comment file)))
+    (image-dired-write-comments (list (cons file comment)))
+    (image-dired-update-property 'comment comment))
+  (image-dired--update-header-line))
+
+(defun image-dired-copy-filename-as-kill (&optional arg)
+  "Copy names of marked (or next ARG) files into the kill ring.
+This works as `dired-copy-filename-as-kill' (which see)."
+  (interactive "P" image-dired-thumbnail-mode)
+  (image-dired--with-dired-buffer
+    (dired-copy-filename-as-kill arg)))
+
+
+;;; Mouse support
+
+(defun image-dired-mouse-display-image (event)
+  "Use mouse EVENT, call `image-dired-display-image' to display image.
+Track this in associated Dired buffer if `image-dired-track-movement' is
+non-nil."
+  (interactive "e")
+  (mouse-set-point event)
+  (goto-char (posn-point (event-end event)))
+  (unless (image-at-point-p)
+    (image-dired-backward-image))
+  (let ((file (image-dired-original-file-name)))
+    (when file
+      (if image-dired-track-movement
+          (image-dired-track-original-file))
+      (image-dired-display-image file))))
+
+(defun image-dired-mouse-select-thumbnail (event)
+  "Use mouse EVENT to select thumbnail image.
+Track this in associated Dired buffer if `image-dired-track-movement' is
+non-nil."
+  (interactive "e")
+  (mouse-set-point event)
+  (goto-char (posn-point (event-end event)))
+  (unless (image-at-point-p)
+    (image-dired-backward-image))
+  (if image-dired-track-movement
+      (image-dired-track-original-file))
+  (image-dired--update-header-line))
+
+
+
+;;; Dired marks and tags
+
+(defun image-dired-thumb-file-marked-p (&optional flagged)
+  "Check if file is marked in associated Dired buffer.
+If optional argument FLAGGED is non-nil, check if file is flagged
+for deletion instead."
+  (let ((file-name (image-dired-original-file-name)))
+    (image-dired--with-dired-buffer
+      (save-excursion
+        (when (dired-goto-file file-name)
+          (if flagged
+              (image-dired-dired-file-flagged-p)
+            (image-dired-dired-file-marked-p)))))))
+
+(defun image-dired-thumb-file-flagged-p ()
+  "Check if file is flagged for deletion in associated Dired buffer."
+  (image-dired-thumb-file-marked-p t))
+
+(defun image-dired-do-flagged-delete ()
+  "Delete flagged thumbnails and associated images."
+  (interactive nil image-dired-thumbnail-mode)
+  (unless (derived-mode-p 'image-dired-thumbnail-mode)
+    (user-error "Not in `image-dired-thumbnail-mode'"))
+  (image-dired--with-dired-buffer
+    (dired-do-flagged-delete))
+  (let (deletions)
+    (save-excursion
+      (let ((inhibit-read-only t))
+        (goto-char (point-min))
+        (while (not (eobp))
+          (let ((file-name (image-dired-original-file-name)))
+            (if (image-dired--with-dired-buffer (dired-goto-file file-name))
+                (forward-char 2)
+              (delete-char 1)
+              (forward-char)
+              (setq deletions t))))))
+    (if deletions
+        (image-dired--line-up-with-method))))
+
+(defun image-dired--thumb-update-mark-at-point ()
+  (with-silent-modifications
+    (cond ((image-dired-thumb-file-marked-p)
+           (add-face-text-property (point) (1+ (point))
+                                   'image-dired-thumb-mark))
+          ((image-dired-thumb-file-flagged-p)
+           (add-face-text-property (point) (1+ (point))
+                                   'image-dired-thumb-flagged))
+          (t (remove-text-properties (point) (1+ (point))
+                                     '(face image-dired-thumb-mark))))))
+
+(defun image-dired--thumb-update-marks ()
+  "Update the marks in the thumbnail buffer.
+It's expected, that a thumbnail is always followed
+by exactly one space or one newline character."
+  (when image-dired-thumb-visible-marks
+    (with-current-buffer image-dired-thumbnail-buffer
+      (save-mark-and-excursion
+        (goto-char (point-min))
+        (let ((inhibit-read-only t))
+          (while (not (eobp))
+            (image-dired--thumb-update-mark-at-point)
+            (forward-char 2)))))))
+
+(defun image-dired-mouse-toggle-mark-1 ()
+  "Toggle Dired mark for current thumbnail.
+Track this in associated Dired buffer if
+`image-dired-track-movement' is non-nil."
+  (when image-dired-track-movement
+    (image-dired-track-original-file))
+  (image-dired--do-mark-command nil nil
+    (if (image-dired-dired-file-marked-p)
+        (dired-unmark 1)
+      (dired-mark 1))))
+
+(defun image-dired-mouse-toggle-mark (event)
+  "Use mouse EVENT to toggle Dired mark for thumbnail.
+Toggle marks of all thumbnails in region, if it's active.
+Track this in associated Dired buffer if
+`image-dired-track-movement' is non-nil."
+  (interactive "e")
+  (if (use-region-p)
+      (let ((end (region-end)))
+        (save-excursion
+          (goto-char (region-beginning))
+          (while (<= (point) end)
+            (when (image-dired-image-at-point-p)
+              (image-dired-mouse-toggle-mark-1))
+            (forward-char))))
+    (mouse-set-point event)
+    (goto-char (posn-point (event-end event)))
+    (image-dired-mouse-toggle-mark-1))
+  (image-dired--thumb-update-marks))
+
+
+;;; bookmark.el support
+
+(declare-function bookmark-make-record-default
+                  "bookmark" (&optional no-file no-context posn))
+(declare-function bookmark-prop-get "bookmark" (bookmark prop))
+
+(defun image-dired-bookmark-name ()
+  "Create a default bookmark name for the current EWW buffer."
+  (file-name-nondirectory
+   (directory-file-name
+    (file-name-directory (image-dired-original-file-name)))))
+
+(defun image-dired-bookmark-make-record ()
+  "Create a bookmark for the current EWW buffer."
+  `(,(image-dired-bookmark-name)
+    ,@(bookmark-make-record-default t)
+    (location . ,(file-name-directory (image-dired-original-file-name)))
+    (image-dired-file . ,(file-name-nondirectory 
(image-dired-original-file-name)))
+    (handler . image-dired-bookmark-jump)))
+
+;;;###autoload
+(defun image-dired-bookmark-jump (bookmark)
+  "Default bookmark handler for Image-Dired buffers."
+  ;; User already cached thumbnails, so disable any checking.
+  (let ((image-dired-show-all-from-dir-max-files nil))
+    (image-dired (bookmark-prop-get bookmark 'location))
+    ;; TODO: Go to the bookmarked file, if it exists.
+    ;; (bookmark-prop-get bookmark 'image-dired-file)
+    (goto-char (point-min))))
+
+(put 'image-dired-bookmark-jump 'bookmark-handler-type "Image-Dired")
+
+
+;;; Obsolete
+
+;;;###autoload
+(define-obsolete-function-alias 'tumme #'image-dired "24.4")
+
+;;;###autoload
+(define-obsolete-function-alias 'image-dired-setup-dired-keybindings
+  #'image-dired-minor-mode "26.1")
+
+(make-obsolete-variable 'image-dired-thumb-width
+                        'image-dired-thumb-size "29.1")
+(defcustom image-dired-thumb-width image-dired-thumb-size
+  "Width of thumbnails, in pixels."
+  :type 'natnum)
+
+(make-obsolete-variable 'image-dired-thumb-height
+                        'image-dired-thumb-size "29.1")
+(defcustom image-dired-thumb-height image-dired-thumb-size
+  "Height of thumbnails, in pixels."
+  :type 'natnum)
+
+(defcustom image-dired-temp-image-file
+  (expand-file-name ".image-dired_temp" image-dired-dir)
+  "Name of temporary image file used by various commands."
+  :type 'file)
+(make-obsolete-variable 'image-dired-temp-image-file
+                        "no longer used." "29.1")
+
+(defcustom image-dired-cmd-create-temp-image-program
+  (if (executable-find "gm") "gm" "convert")
+  "Executable used to create temporary image.
+Used together with `image-dired-cmd-create-temp-image-options'."
+  :type 'file
+  :version "29.1")
+(make-obsolete-variable 'image-dired-cmd-create-temp-image-program
+                        "no longer used." "29.1")
+
+(defcustom image-dired-cmd-create-temp-image-options
+  (let ((opts '("-size" "%wx%h" "%f[0]"
+                "-resize" "%wx%h>"
+                "-strip" "jpeg:%t")))
+    (if (executable-find "gm") (cons "convert" opts) opts))
+  "Options of command used to create temporary image for display window.
+Used together with `image-dired-cmd-create-temp-image-program',
+Available format specifiers are: %w and %h which are replaced by
+the calculated max size for width and height in the image display window,
+%f which is replaced by the file name of the original image and %t which
+is replaced by the file name of the temporary file."
+  :version "29.1"
+  :type '(repeat (string :tag "Argument")))
+(make-obsolete-variable 'image-dired-cmd-create-temp-image-options
+                        "no longer used." "29.1")
+
+(defcustom image-dired-display-window-width-correction 1
+  "Number to be used to correct image display window width.
+Change if the default (1) does not work (i.e. if the image does not
+completely fit)."
+  :type 'integer)
+(make-obsolete-variable 'image-dired-display-window-width-correction
+                        "no longer used." "29.1")
+
+(defcustom image-dired-display-window-height-correction 0
+  "Number to be used to correct image display window height.
+Change if the default (0) does not work (i.e. if the image does not
+completely fit)."
+  :type 'integer)
+(make-obsolete-variable 'image-dired-display-window-height-correction
+                        "no longer used." "29.1")
+
+(defun image-dired-toggle-mark-thumb-original-file ()
+  "Toggle mark on original image file in associated Dired buffer."
+  (declare (obsolete nil "29.1"))
+  (interactive nil image-dired-thumbnail-mode image-dired-image-mode)
+  (image-dired--do-mark-command nil t
+    (if (image-dired-dired-file-marked-p)
+        (dired-unmark 1)
+      (dired-mark 1))))
+
+(defun image-dired-display-window-width (window)
+  "Return width, in pixels, of WINDOW."
+  (declare (obsolete nil "29.1"))
+  (- (window-body-width window t)
+     image-dired-display-window-width-correction))
+
+(defun image-dired-display-window-height (window)
+  "Return height, in pixels, of WINDOW."
+  (declare (obsolete nil "29.1"))
+  (- (image-dired-window-height-pixels window)
+     image-dired-display-window-height-correction))
+
+(defun image-dired-window-height-pixels (window)
+  "Calculate WINDOW height in pixels."
+  (declare (obsolete nil "29.1"))
+  ;; Note: The mode-line consumes one line
+  (* (- (window-height window) 1) (frame-char-height)))
+
+(defcustom image-dired-cmd-read-exif-data-program "exiftool"
+  "Program used to read EXIF data to image.
+Used together with `image-dired-cmd-read-exif-data-options'."
+  :type 'file)
+(make-obsolete-variable 'image-dired-cmd-read-exif-data-program
+                        "use `exif-parse-file' and `exif-field' instead." 
"29.1")
+
+(defcustom image-dired-cmd-read-exif-data-options '("-s" "-s" "-s" "-%t" "%f")
+  "Arguments of command used to read EXIF data.
+Used with `image-dired-cmd-read-exif-data-program'.
+Available format specifiers are: %f which is replaced
+by the image file name and %t which is replaced by the tag name."
+  :version "26.1"
+  :type '(repeat (string :tag "Argument")))
+(make-obsolete-variable 'image-dired-cmd-read-exif-data-options
+                        "use `exif-parse-file' and `exif-field' instead." 
"29.1")
+
+(defun image-dired-get-exif-data (file tag-name)
+  "From FILE, return EXIF tag TAG-NAME."
+  (declare (obsolete "use `exif-parse-file' and `exif-field' instead."  
"29.1"))
+  (image-dired--check-executable-exists
+   'image-dired-cmd-read-exif-data-program)
+  (let ((buf (get-buffer-create "*image-dired-get-exif-data*"))
+        (spec (list (cons ?f file) (cons ?t tag-name)))
+        tag-value)
+    (with-current-buffer buf
+      (delete-region (point-min) (point-max))
+      (if (not (eq (apply #'call-process image-dired-cmd-read-exif-data-program
+                          nil t nil
+                          (mapcar
+                           (lambda (arg) (format-spec arg spec))
+                           image-dired-cmd-read-exif-data-options))
+                   0))
+          (error "Could not get EXIF tag")
+        (goto-char (point-min))
+        ;; Clean buffer from newlines and carriage returns before
+        ;; getting final info
+        (while (search-forward-regexp "[\n\r]" nil t)
+          (replace-match "" nil t))
+        (setq tag-value (buffer-substring (point-min) (point-max)))))
+    tag-value))
+
+(defcustom image-dired-cmd-rotate-thumbnail-program
+  (if (executable-find "gm") "gm" "mogrify")
+  "Executable used to rotate thumbnail.
+Used together with `image-dired-cmd-rotate-thumbnail-options'."
+  :type 'file
+  :version "29.1")
+(make-obsolete-variable 'image-dired-cmd-rotate-thumbnail-program nil "29.1")
+
+(defcustom image-dired-cmd-rotate-thumbnail-options
+  (let ((opts '("-rotate" "%d" "%t")))
+    (if (executable-find "gm") (cons "mogrify" opts) opts))
+  "Arguments of command used to rotate thumbnail image.
+Used with `image-dired-cmd-rotate-thumbnail-program'.
+Available format specifiers are: %d which is replaced by the
+number of (positive) degrees to rotate the image, normally 90 or 270
+\(for 90 degrees right and left), %t which is replaced by the file name
+of the thumbnail file."
+  :version "29.1"
+  :type '(repeat (string :tag "Argument")))
+(make-obsolete-variable 'image-dired-cmd-rotate-thumbnail-options nil "29.1")
+
+(declare-function clear-image-cache "image.c" (&optional filter))
+
+(defun image-dired-rotate-thumbnail (degrees)
+  "Rotate thumbnail DEGREES degrees."
+  (declare (obsolete image-dired-refresh-thumb "29.1"))
+  (image-dired--check-executable-exists
+   'image-dired-cmd-rotate-thumbnail-program)
+  (if (not (image-dired-image-at-point-p))
+      (message "No thumbnail at point")
+    (let* ((file (image-dired-thumb-name (image-dired-original-file-name)))
+           (thumb (expand-file-name file))
+           (spec (list (cons ?d degrees) (cons ?t thumb))))
+      (apply #'call-process image-dired-cmd-rotate-thumbnail-program nil nil 
nil
+             (mapcar (lambda (arg) (format-spec arg spec))
+                     image-dired-cmd-rotate-thumbnail-options))
+      (clear-image-cache thumb))))
+
+(defun image-dired-rotate-thumbnail-left ()
+  "Rotate thumbnail left (counter clockwise) 90 degrees."
+  (declare (obsolete image-dired-refresh-thumb "29.1"))
+  (interactive nil image-dired-thumbnail-mode)
+  (with-suppressed-warnings ((obsolete image-dired-rotate-thumbnail))
+    (image-dired-rotate-thumbnail "270")))
+
+(defun image-dired-rotate-thumbnail-right ()
+  "Rotate thumbnail counter right (clockwise) 90 degrees."
+  (declare (obsolete image-dired-refresh-thumb "29.1"))
+  (interactive nil image-dired-thumbnail-mode)
+  (with-suppressed-warnings ((obsolete image-dired-rotate-thumbnail))
+    (image-dired-rotate-thumbnail "90")))
+
+(defun image-dired-modify-mark-on-thumb-original-file (command)
+  "Modify mark in Dired buffer.
+COMMAND is one of `mark' for marking file in Dired, `unmark' for
+unmarking file in Dired or `flag' for flagging file for delete in
+Dired."
+  (declare (obsolete image-dired--on-file-in-dired-buffer "29.1"))
+  (let ((file-name (image-dired-original-file-name))
+        (dired-buf (image-dired-associated-dired-buffer)))
+    (if (not (and dired-buf file-name))
+        (message "No image, or image with correct properties, at point")
+      (with-current-buffer dired-buf
+        (message "%s" file-name)
+        (when (dired-goto-file file-name)
+          (cond ((eq command 'mark) (dired-mark 1))
+                ((eq command 'unmark) (dired-unmark 1))
+                ((eq command 'toggle)
+                 (if (image-dired-dired-file-marked-p)
+                     (dired-unmark 1)
+                   (dired-mark 1)))
+                ((eq command 'flag) (dired-flag-file-deletion 1)))
+          (image-dired--thumb-update-marks))))))
+
+(defun image-dired-display-current-image-full ()
+  "Display current image in full size."
+  (declare (obsolete image-transform-reset-to-original "29.1"))
+  (interactive nil image-dired-thumbnail-mode)
+  (let ((file (image-dired-original-file-name)))
+    (if file
+        (progn
+          (image-dired-display-image file)
+          (with-current-buffer image-dired-display-image-buffer
+            (image-transform-reset-to-original)))
+      (error "No original file name at point"))))
+
+(defun image-dired-display-current-image-sized ()
+  "Display current image in sized to fit window dimensions."
+  (declare (obsolete image-mode-fit-frame "29.1"))
+  (interactive nil image-dired-thumbnail-mode)
+  (let ((file (image-dired-original-file-name)))
+    (if file
+        (progn
+          (image-dired-display-image file))
+      (error "No original file name at point"))))
+
+(make-obsolete-variable 'image-dired-tag-file-list nil "29.1")
+(defvar image-dired-tag-file-list nil
+  "List to store tag-file structure.")
+
+(defun image-dired-add-to-tag-file-list (tag file)
+  "Add relation between TAG and FILE."
+  (declare (obsolete nil "29.1"))
+  (let (curr)
+    (if image-dired-tag-file-list
+        (if (setq curr (assoc tag image-dired-tag-file-list))
+            (if (not (member file curr))
+                (setcdr curr (cons file (cdr curr))))
+          (setcdr image-dired-tag-file-list
+                  (cons (list tag file) (cdr image-dired-tag-file-list))))
+      (setq image-dired-tag-file-list (list (list tag file))))))
+
+(defvar image-dired-slideshow-count 0
+  "Keeping track on number of images in slideshow.")
+(make-obsolete-variable 'image-dired-slideshow-count "no longer used." "29.1")
+
+(defvar image-dired-slideshow-times 0
+  "Number of pictures to display in slideshow.")
+(make-obsolete-variable 'image-dired-slideshow-times "no longer used." "29.1")
+
+(make-obsolete-variable 'image-dired-gallery-dir nil "29.1")
+(defcustom image-dired-gallery-dir
+  (expand-file-name ".image-dired_gallery" image-dired-dir)
+  "Directory to store generated gallery html pages.
+The name of this directory needs to be \"shared\" to the public
+so that it can access the index.html page that image-dired creates."
+  :type 'directory)
+
+(make-obsolete-variable 'image-dired-gallery-image-root-url nil "29.1")
+(defcustom image-dired-gallery-image-root-url
+  "https://example.org/image-diredpics";
+  "URL where the full size images are to be found on your web server.
+Note that this URL has to be configured on your web server.
+Image-Dired expects to find pictures in this directory.
+This is used by `image-dired-gallery-generate'."
+  :type 'string
+  :version "29.1")
+
+(make-obsolete-variable 'image-dired-gallery-thumb-image-root-url nil "29.1")
+(defcustom image-dired-gallery-thumb-image-root-url
+  "https://example.org/image-diredthumbs";
+  "URL where the thumbnail images are to be found on your web server.
+Note that URL path has to be configured on your web server.
+Image-Dired expects to find pictures in this directory.
+This is used by `image-dired-gallery-generate'."
+  :type 'string
+  :version "29.1")
+
+(make-obsolete-variable 'image-dired-gallery-hidden-tags nil "29.1")
+(defcustom image-dired-gallery-hidden-tags
+  (list "private" "hidden" "pending")
+  "List of \"hidden\" tags.
+Used by `image-dired-gallery-generate' to leave out \"hidden\" images."
+  :type '(repeat string))
+
+(make-obsolete-variable 'image-dired-file-tag-list nil "29.1")
+(defvar image-dired-file-tag-list nil
+  "List to store file-tag structure.")
+
+(make-obsolete-variable 'image-dired-file-comment-list nil "29.1")
+(defvar image-dired-file-comment-list nil
+  "List to store file comments.")
+
+(defun image-dired--add-to-tag-file-lists (tag file)
+  "Helper function used from `image-dired--create-gallery-lists'.
+
+Add TAG to FILE in one list and FILE to TAG in the other.
+
+Lisp structures look like the following:
+
+image-dired-file-tag-list:
+
+  ((\"filename1\" \"tag1\" \"tag2\" \"tag3\" ...)
+   (\"filename2\" \"tag1\" \"tag2\" \"tag3\" ...)
+   ...)
+
+image-dired-tag-file-list:
+
+ ((\"tag1\" \"filename1\" \"filename2\" \"filename3\" ...)
+  (\"tag2\" \"filename1\" \"filename2\" \"filename3\" ...)
+  ...)"
+  (declare (obsolete nil "29.1"))
+  ;; Add tag to file list
+  (let (curr)
+    (if image-dired-file-tag-list
+        (if (setq curr (assoc file image-dired-file-tag-list))
+            (setcdr curr (cons tag (cdr curr)))
+          (setcdr image-dired-file-tag-list
+                  (cons (list file tag) (cdr image-dired-file-tag-list))))
+      (setq image-dired-file-tag-list (list (list file tag))))
+    ;; Add file to tag list
+    (if image-dired-tag-file-list
+        (if (setq curr (assoc tag image-dired-tag-file-list))
+            (if (not (member file curr))
+                (setcdr curr (cons file (cdr curr))))
+          (setcdr image-dired-tag-file-list
+                  (cons (list tag file) (cdr image-dired-tag-file-list))))
+      (setq image-dired-tag-file-list (list (list tag file))))))
+
+(defun image-dired--add-to-file-comment-list (file comment)
+  "Helper function used from `image-dired--create-gallery-lists'.
+
+For FILE, add COMMENT to list.
+
+Lisp structure looks like the following:
+
+image-dired-file-comment-list:
+
+  ((\"filename1\" .  \"comment1\")
+   (\"filename2\" .  \"comment2\")
+   ...)"
+  (declare (obsolete nil "29.1"))
+  (if image-dired-file-comment-list
+      (if (not (assoc file image-dired-file-comment-list))
+          (setcdr image-dired-file-comment-list
+                  (cons (cons file comment)
+                        (cdr image-dired-file-comment-list))))
+    (setq image-dired-file-comment-list (list (cons file comment)))))
+
+(defun image-dired--create-gallery-lists ()
+  "Create temporary lists used by `image-dired-gallery-generate'."
+  (declare (obsolete nil "29.1"))
+  (image-dired-sane-db-file)
+  (image-dired--with-db-file
+    (let (end beg file row-tags)
+      (setq image-dired-tag-file-list nil)
+      (setq image-dired-file-tag-list nil)
+      (setq image-dired-file-comment-list nil)
+      (goto-char (point-min))
+      (while (search-forward-regexp "^." nil t)
+        (end-of-line)
+        (setq end (point))
+        (beginning-of-line)
+        (setq beg (point))
+        (unless (search-forward ";" end nil)
+          (error "Something is really wrong, check format of database"))
+        (setq row-tags (split-string
+                        (buffer-substring beg end) ";"))
+        (setq file (car row-tags))
+        (dolist (x (cdr row-tags))
+          (with-suppressed-warnings
+              ((obsolete image-dired--add-to-tag-file-lists
+                         image-dired--add-to-file-comment-list))
+            (if (not (string-match "^comment:\\(.*\\)" x))
+                (image-dired--add-to-tag-file-lists x file)
+              (image-dired--add-to-file-comment-list file (match-string 1 
x))))))))
+  ;; Sort tag-file list
+  (setq image-dired-tag-file-list
+        (sort image-dired-tag-file-list
+              (lambda (x y)
+                (string< (car x) (car y))))))
+
+(defun image-dired--hidden-p (file)
+  "Return t if image FILE has a \"hidden\" tag."
+  (declare (obsolete nil "29.1"))
+  (cl-loop for tag in (cdr (assoc file image-dired-file-tag-list))
+           if (member tag image-dired-gallery-hidden-tags) return t))
+
+(defun image-dired-gallery-generate ()
+  "Generate gallery pages.
+First we create a couple of Lisp structures from the database to make
+it easier to generate, then HTML-files are created in
+`image-dired-gallery-dir'."
+  (declare (obsolete nil "29.1"))
+  (interactive)
+  (if (eq 'per-directory image-dired-thumbnail-storage)
+      (error "Currently, gallery generation is not supported \
+when using per-directory thumbnail file storage"))
+  (with-suppressed-warnings ((obsolete image-dired--create-gallery-lists))
+    (image-dired--create-gallery-lists))
+  (let ((tags image-dired-tag-file-list)
+        (index-file (format "%s/index.html" image-dired-gallery-dir))
+        count tag tag-file
+        comment file-tags tag-link tag-link-list)
+    ;; Make sure gallery root exist
+    (if (file-exists-p image-dired-gallery-dir)
+        (if (not (file-directory-p image-dired-gallery-dir))
+            (error "Variable image-dired-gallery-dir is not a directory"))
+      ;; FIXME: Should we set umask to 077 here, as we do for thumbnails?
+      (make-directory image-dired-gallery-dir))
+    ;; Open index file
+    (with-temp-file index-file
+      (if (file-exists-p index-file)
+          (insert-file-contents index-file))
+      (insert "<html>\n")
+      (insert "  <body>\n")
+      (insert "   <h2>Image-Dired Gallery</h2>\n")
+      (insert (format "<p>\n    Gallery generated %s\n   <p>\n"
+                      (current-time-string)))
+      (insert "   <h3>Tag index</h3>\n")
+      (setq count 1)
+      ;; Pre-generate list of all tag links
+      (dolist (curr tags)
+        (setq tag (car curr))
+        (when (not (member tag image-dired-gallery-hidden-tags))
+          (setq tag-link (format "<a href=\"%d.html\">%s</a>" count tag))
+          (if tag-link-list
+              (setq tag-link-list
+                    (append tag-link-list (list (cons tag tag-link))))
+            (setq tag-link-list (list (cons tag tag-link))))
+          (setq count (1+ count))))
+      (setq count 1)
+      ;; Main loop where we generated thumbnail pages per tag
+      (dolist (curr tags)
+        (setq tag (car curr))
+        ;; Don't display hidden tags
+        (when (not (member tag image-dired-gallery-hidden-tags))
+          ;; Insert link to tag page in index
+          (insert (format "    %s<br>\n" (cdr (assoc tag tag-link-list))))
+          ;; Open per-tag file
+          (setq tag-file (format "%s/%s.html" image-dired-gallery-dir count))
+          (with-temp-file tag-file
+            (if (file-exists-p tag-file)
+                (insert-file-contents tag-file))
+            (erase-buffer)
+            (insert "<html>\n")
+            (insert "  <body>\n")
+            (insert "  <p><a href=\"index.html\">Index</a></p>\n")
+            (insert (format "  <h2>Images with tag &quot;%s&quot;</h2>" tag))
+            ;; Main loop for files per tag page
+            (dolist (file (cdr curr))
+              (unless (image-dired-hidden-p file)
+                ;; Insert thumbnail with link to full image
+                (insert
+                 (format "<a href=\"%s/%s\"><img src=\"%s/%s\"%s></a>\n"
+                         image-dired-gallery-image-root-url
+                         (file-name-nondirectory file)
+                         image-dired-gallery-thumb-image-root-url
+                         (file-name-nondirectory (image-dired-thumb-name 
file)) file))
+                ;; Insert comment, if any
+                (if (setq comment (cdr (assoc file 
image-dired-file-comment-list)))
+                    (insert (format "<br>\n%s<br>\n" comment))
+                  (insert "<br>\n"))
+                ;; Insert links to other tags, if any
+                (when (> (length
+                          (setq file-tags (assoc file 
image-dired-file-tag-list))) 2)
+                  (insert "[ ")
+                  (dolist (extra-tag file-tags)
+                    ;; Only insert if not file name or the main tag
+                    (if (and (not (equal extra-tag tag))
+                             (not (equal extra-tag file)))
+                        (insert
+                         (format "%s " (cdr (assoc extra-tag 
tag-link-list))))))
+                  (insert "]<br>\n"))))
+            (insert "  <p><a href=\"index.html\">Index</a></p>\n")
+            (insert "  </body>\n")
+            (insert "</html>\n"))
+          (setq count (1+ count))))
+      (insert "  </body>\n")
+      (insert "</html>"))))
+
+(define-obsolete-function-alias 'image-dired-slideshow-step 
#'image-dired--slideshow-step "29.1")
+(define-obsolete-function-alias 'image-dired-slideshow-stop 
#'image-dired--slideshow-stop "29.1")
+(define-obsolete-function-alias 'image-dired-create-display-image-buffer
+  #'ignore "29.1")
+;; These can't use the #' quote as they point to obsolete names.
+(define-obsolete-function-alias 'image-dired-create-gallery-lists
+  'image-dired--create-gallery-lists "29.1")
+(define-obsolete-function-alias 'image-dired-add-to-file-comment-list
+  'image-dired--add-to-file-comment-list "29.1")
+(define-obsolete-function-alias 'image-dired-add-to-tag-file-lists
+  'image-dired--add-to-tag-file-lists "29.1")
+(define-obsolete-function-alias 'image-dired-hidden-p
+  'image-dired--hidden-p "29.1")
+(define-obsolete-function-alias 'image-dired-thumb-update-marks
+  #'image-dired--thumb-update-marks "29.1")
+(define-obsolete-function-alias 'image-dired-get-thumbnail-image
+  #'image-dired--get-create-thumbnail-file "29.1")
+(define-obsolete-function-alias 'image-dired-display-thumb-properties
+  #'image-dired--update-header-line "29.1")
+(define-obsolete-function-alias 'image-dired-delete-marked
+  #'image-dired-do-flagged-delete "29.1")
+(define-obsolete-function-alias 'image-dired-display-image-mode
+  #'image-dired-image-mode "29.1")
+(define-obsolete-function-alias 'image-dired-display-thumbnail-original-image
+  #'image-dired-display-this "29.1")
+(define-obsolete-function-alias 'image-dired-display-next-thumbnail-original
+  #'image-dired-display-next "29.1")
+(define-obsolete-function-alias 
'image-dired-display-previous-thumbnail-original
+  #'image-dired-display-previous "29.1")
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;;;;;;;; TEST-SECTION ;;;;;;;;;;;
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+;; (defvar image-dired-dir-max-size 12300000)
+
+;; (defun image-dired-test-clean-old-files ()
+;;   "Clean `image-dired-dir' from old thumbnail files.
+;; \"Oldness\" measured using last access time.  If the total size of all
+;; thumbnail files in `image-dired-dir' is larger than 
'image-dired-dir-max-size',
+;; old files are deleted until the max size is reached."
+;;   (let* ((files
+;;           (sort
+;;            (mapcar
+;;             (lambda (f)
+;;               (let ((fattribs (file-attributes f)))
+;;                 `(,(file-attribute-access-time fattribs)
+;;                   ,(file-attribute-size fattribs) ,f)))
+;;             (directory-files (image-dired-dir) t ".+\\.thumb\\..+$"))
+;;            ;; Sort function.  Compare time between two files.
+;;            (lambda (l1 l2)
+;;               (time-less-p (car l1) (car l2)))))
+;;          (dirsize (apply '+ (mapcar (lambda (x) (cadr x)) files))))
+;;     (while (> dirsize image-dired-dir-max-size)
+;;       (y-or-n-p
+;;        (format "Size of thumbnail directory: %d, delete old file %s? "
+;;                dirsize (cadr (cdar files))))
+;;       (delete-file (cadr (cdar files)))
+;;       (setq dirsize (- dirsize (car (cdar files))))
+;;       (setq files (cdr files)))))
+
+(provide 'image-dired)
+
+;;; image-dired.el ends here
diff --git a/lisp/image/wallpaper.el b/lisp/image/wallpaper.el
new file mode 100644
index 0000000000..e23b65d616
--- /dev/null
+++ b/lisp/image/wallpaper.el
@@ -0,0 +1,540 @@
+;;; wallpaper.el --- Change the desktop background  -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2022 Free Software Foundation, Inc.
+
+;; Author: Stefan Kangas <stefankangas@gmail.com>
+;; Keywords: images
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; This library provides the command `wallpaper-set', which sets the
+;; desktop background.
+;;
+;; On GNU/Linux and other Unix-like systems, it uses an external
+;; command to set the desktop background.
+;;
+;; Finding an external command to use is obviously a bit tricky to get
+;; right, as there is no lack of platforms, window managers, desktop
+;; environments and tools.  However, it should be detected
+;; automatically in most cases.  If it doesn't work in your
+;; environment, customize the user options `wallpaper-command' and
+;; `wallpaper-command-args'.
+;;
+;; On MS-Windows, it uses the `w32-set-wallpaper' function, and on
+;; Haiku the `haiku-set-wallpaper' function, neither of which relies
+;; on any external commands.  The value of `wallpaper-command' and
+;; `wallpaper-command-args' are ignored on such systems.
+;;
+;; On macOS, the "osascript" command is used.  You might need to
+;; disable the option "Change picture" in the "Desktop & Screensaver"
+;; preferences for this to work (this was seen with macOS 10.13).
+;; You might also have to tweak some permissions.
+;;
+;; Note: If you find that you need to use a command in your
+;; environment that was not automatically detected, we would love to
+;; hear about it!  Please send an email to bug-gnu-emacs@gnu.org and
+;; tell us the command (and all options) that worked for you.  You can
+;; also use `M-x report-emacs-bug'.
+
+;;; Code:
+
+(eval-when-compile (require 'subr-x))
+(require 'xdg)
+(require 'cl-macs)
+
+(defvar wallpaper-debug nil
+  "If non-nil, display debug messages.")
+
+(defun wallpaper-debug (&rest args)
+  (when wallpaper-debug
+    (apply #'message
+           (concat "wallpaper-debug: " (car args))
+           (cdr args))))
+
+(defvar wallpaper-set-function
+  (cond ((fboundp 'w32-set-wallpaper)
+         #'w32-set-wallpaper)
+        ((and (fboundp 'haiku-set-wallpaper)
+              (featurep 'haiku))
+         'haiku-set-wallpaper)
+        (#'wallpaper-default-set-function))
+  "Function used by `wallpaper-set' to set the wallpaper.
+The function takes one argument, FILE, which is the file name of
+the image file to set the wallpaper to.")
+
+(defun wallpaper--use-default-set-function-p ()
+  (eq wallpaper-set-function #'wallpaper-default-set-function))
+
+
+;;; Finding the wallpaper command
+
+(cl-defstruct (wallpaper-setter
+               ;; Get rid of the default constructor (`make-wallpaper-cmd').
+               (:constructor nil)
+               (:constructor
+                wallpaper-setter-create
+                ( name command args-raw
+                  &rest rest-plist
+                  &aux
+                  (args (if (or (listp args-raw) (symbolp args-raw))
+                            args-raw
+                          (string-split args-raw)))
+                  (predicate (plist-get rest-plist :predicate))))
+               (:copier wallpaper-setter-copy))
+  "Structure containing a command to set the wallpaper.
+
+NAME is a description of the setter (e.g. the name of the Desktop
+Environment).
+
+COMMAND is the executable to run to set the wallpaper.
+
+ARGS is the default list of command line arguments for COMMAND.
+
+PREDICATE is a function that will be called without any arguments
+and returns non-nil if this setter should be used."
+  name
+  command
+  args
+  (predicate #'always))
+
+;;;###autoload
+(put 'wallpaper-setter-create 'lisp-indent-function 1)
+
+(defmacro wallpaper--default-methods-create (&rest items)
+  "Helper macro for defining `wallpaper--default-setters'."
+  (cons 'list
+        (mapcar
+         (lambda (item)
+           `(wallpaper-setter-create ,@item))
+         items)))
+
+(defvar wallpaper--default-setters
+  (wallpaper--default-methods-create
+
+   ;; macOS.
+   ;; NB. Should come first to override everything else.
+   ("macOS"
+    "osascript"
+    '("-e" "tell application \"Finder\" to set desktop picture to POSIX file 
\"%f\"")
+    :predicate (lambda ()
+                 (eq system-type 'darwin)))
+
+   ;; Desktop environments.
+   ("Gnome"
+    "gsettings"
+    "set org.gnome.desktop.background picture-uri file://%F"
+    :predicate (lambda ()
+                 (or (and (getenv "DESKTOP_SESSION")
+                          (member (downcase (getenv "DESKTOP_SESSION"))
+                                  '("gnome" "gnome" "gnome-wayland" 
"gnome-xorg"
+                                    "unity" "ubuntu" "pantheon" 
"budgie-desktop"
+                                    "pop")))
+                     (member "GNOME" (xdg-current-desktop))
+                     (member "Budgie" (xdg-current-desktop))
+                     (member "GNOME-Classic" (xdg-current-desktop)))))
+
+   ("KDE Plasma"
+    "plasma-apply-wallpaperimage" "%f"
+    :predicate (lambda ()
+                 (member "KDE" (xdg-current-desktop))))
+
+   ("XFCE"
+    "xfconf-query" #'wallpaper-xfce-command-args
+    :predicate (lambda ()
+                 (or (and (getenv "DESKTOP_SESSION")
+                          (member (downcase (getenv "DESKTOP_SESSION"))
+                                  '("xubuntu" "ubuntustudio")))
+                     (member "XFCE" (xdg-current-desktop)))))
+
+   ("LXDE"
+    "pcmanfm" "--set-wallpaper=%f"
+    :predicate (lambda ()
+                 (member "LXDE" (xdg-current-desktop))))
+
+   ("LXQt"
+    "pcmanfm-qt" "--set-wallpaper=%f" ; "--wallpaper-mode=MODE"
+    :predicate (lambda ()
+                 (or (member (and (getenv "DESKTOP_SESSION")
+                                  (downcase (getenv "DESKTOP_SESSION")))
+                             '("lubuntu" "lxqt"))
+                     (member "LXQt" (xdg-current-desktop)))))
+
+   ("Mate"
+    "gsettings" "set org.mate.background picture-filename %f"
+    :predicate (lambda ()
+                 (or (and (getenv "DESKTOP_SESSION")
+                          (equal "mate" (downcase (getenv "DESKTOP_SESSION"))))
+                     (member "MATE" (xdg-current-desktop)))))
+
+   ("Cinnamon"
+    "gsettings" "set org.cinnamon.desktop.background picture-uri file://%F"
+    :predicate (lambda ()
+                 (or (equal "cinnamon" (and (getenv "DESKTOP_SESSION")
+                                            (downcase (getenv 
"DESKTOP_SESSION"))))
+                     (member "X-Cinnamon" (xdg-current-desktop)))))
+
+   ("Deepin"
+    "gsettings" "set com.deepin.wrap.gnome.desktop.background picture-uri 
file://%F"
+    :predicate (lambda ()
+                 (member "Deepin" (xdg-current-desktop))))
+
+   ;; Wayland general.
+   ("Sway (Wayland)"
+    "swaybg" "-o * -i %f -m fill"
+    :predicate (lambda ()
+                 (and (getenv "WAYLAND_DISPLAY")
+                      (getenv "SWAYSOCK"))))
+
+   ("wbg"
+    "wbg" "%f"
+    :predicate (lambda ()
+                 (getenv "WAYLAND_DISPLAY")))
+
+   ;; X general.
+   ("GraphicsMagick"
+    "gm" "display -size %wx%h -window root %f")
+
+   ("ImageMagick"
+    "display" "-resize %wx%h -window root %f")
+
+   ("feh"
+    "feh" "--bg-max %f")
+
+   ("fbsetbg"
+    "fbsetbg" "-a %f")
+
+   ("xwallpaper"
+    "xwallpaper" "--zoom %f")
+
+   ("hsetroot"
+    "hsetroot" "-full %f")
+
+   ("xloadimage"
+    "xloadimage" "-onroot -fullscreen %f")
+
+   ("xsetbg"
+    "xsetbg" "%f")
+   )
+  "List of setters used for setting the wallpaper.
+Every item in the list is a structure of type
+`wallpaper-setter' (which see).
+
+This is used by `wallpaper--find-command' to automatically set
+`wallpaper-command', and by `wallpaper--find-command-args' to set
+`wallpaper-command-args'.  The setters will be tested in the
+order in which they appear.")
+
+(defun wallpaper-xfce-command-args ()
+  (let ((info
+         (with-temp-buffer
+           (call-process "xfconf-query" nil t nil
+                         "-c" "xfce4-desktop"
+                         "-p" "/backdrop/single-workspace-mode")
+           (buffer-string))))
+    (list "-c" "xfce4-desktop"
+          "-p" (format "/backdrop/screen%%S/monitor%%M/workspace%s/last-image"
+                       (if (equal info "true")
+                           "0"
+                         "%W"))
+          "-s" "%f")))
+
+(defvar wallpaper--current-setter nil)
+
+(defun wallpaper--find-setter ()
+  (when (wallpaper--use-default-set-function-p)
+    (or wallpaper--current-setter
+        (setq wallpaper--current-setter
+              (catch 'found
+                (dolist (setter wallpaper--default-setters)
+                  (wallpaper-debug "Testing setter %s" (wallpaper-setter-name 
setter))
+                  (when (and (executable-find (wallpaper-setter-command 
setter))
+                             (if-let ((pred (wallpaper-setter-predicate 
setter)))
+                                 (funcall pred)
+                               t))
+                    (wallpaper-debug "Found setter %s" (wallpaper-setter-name 
setter))
+                    (throw 'found setter))))))))
+
+(defun wallpaper--find-command ()
+  "Return a valid command to set the wallpaper in this environment."
+  (when-let ((setter (wallpaper--find-setter)))
+    (wallpaper-setter-command setter)))
+
+(defun wallpaper--find-command-args ()
+  "Return command line arguments matching `wallpaper-command'."
+  (when-let ((setter (wallpaper--find-setter)))
+    (wallpaper-setter-args setter)))
+
+
+;;; Customizable variables
+
+(defvar wallpaper-command-args) ; silence byte-compiler
+(defun wallpaper--set-wallpaper-command (sym val)
+  "Set `wallpaper-command', and update `wallpaper-command-args'.
+Used to set `wallpaper-command'."
+  ;; Note: `wallpaper-command' is used by `wallpaper--find-command-args'.
+  (prog1 (set-default sym val)
+    (set-default 'wallpaper-command-args
+                 (wallpaper--find-command-args))))
+
+(defcustom wallpaper-command (wallpaper--find-command)
+  "Executable used by `wallpaper-set' for setting the wallpaper.
+A suitable command for your environment should be detected
+automatically, so there is usually no need to customize this.
+
+If you set this to any supported command using customize or
+`setopt', the user option `wallpaper-command-args' is
+automatically updated to match.  If you need to change this to an
+unsupported command, you will want to manually customize
+`wallpaper-command-args' to match.
+
+The value of this variable is ignored on MS-Windows and Haiku
+systems, where a native API is used instead."
+  :type
+  '(choice
+    (radio
+     (const :tag "gsettings                   (GNOME)"            "gsettings")
+     (const :tag "plasma-apply-wallpaperimage (KDE Plasma)"       
"plasma-apply-wallpaperimage")
+     (const :tag "xfconf-query                (XFCE)"             
"xfconf-query")
+     (const :tag "pcmanf                      (LXDE)"             "pcmanf")
+     (const :tag "pcmanf-qt                   (LXQt)"             "pcmanf-qt")
+     (const :tag "swaybg                      (Wayland/Sway)"     "swaybg")
+     (const :tag "wbg                         (Wayland)"          "wbg")
+     (const :tag "gm                          (X Window System)"  "gm")
+     (const :tag "display                     (X Window System)"  "display")
+     (const :tag "feh                         (X Window System)"  "feh")
+     (const :tag "fbsetbg                     (X Window System)"  "fbsetbg")
+     (const :tag "xwallpaper                  (X Window System)"  "xwallpaper")
+     (const :tag "hsetroot                    (X Window System)"  "hsetroot")
+     (const :tag "xloadimage                  (X Window System)"  "xloadimage")
+     (const :tag "xsetbg                      (X Window System)"  "xsetbg")
+     (const :tag "osascript                   (macOS)"            "osascript"))
+    (const :tag "Other (specify)"         string)
+    (const :tag "None" nil))
+  :set #'wallpaper--set-wallpaper-command
+  :group 'image
+  :version "29.1")
+
+(defcustom wallpaper-command-args (wallpaper--find-command-args)
+  "Command line arguments for `wallpaper-command'.
+A suitable command for your environment should be detected
+automatically, so there is usually no need to customize this.
+However, if you do need to change this, you might also want to
+customize `wallpaper-command' to match.
+
+The value is a list of command list arguments to use, or a
+function that returns a list of command line arguments.
+
+In each command line argument, these specifiers will be replaced:
+
+  %f   full file name
+  %h   height of the selected frame's display (as returned
+         by `display-pixel-height')
+  %w   the width of the selected frame's display (as returned
+         by `display-pixel-width').
+  %F   full file name URI-encoded
+  %S   current X screen (e.g. \"0\")
+  %W   current workspace (e.g., \"0\")
+  %M   name of the monitor (e.g., \"0\" or \"LVDS\")
+
+If `wallpaper-set' is run from a TTY frame, instead prompt for a
+height and width to use for %h and %w.
+
+The value of this variable is ignored on MS-Windows and Haiku
+systems, where a native API is used instead."
+  :type '(choice (repeat string)
+                 function)
+  :group 'image
+  :version "29.1")
+
+
+;;; Utility functions
+
+(defvar wallpaper-default-width 1080
+  "Default width used by `wallpaper-set'.
+This is only used when it can't be detected automatically.
+See also `wallpaper-default-height'.")
+
+(defvar wallpaper-default-height 1920
+  "Default height used by `wallpaper-set'.
+This is only used when it can't be detected automatically.
+See also `wallpaper-default-width'.")
+
+(defun wallpaper--get-height-or-width (desc fun default)
+  (cond ((display-graphic-p) (funcall fun))
+        (noninteractive default)
+        ((read-number (format "Wallpaper %s in pixels: " desc) default))))
+
+(autoload 'ffap-file-at-point "ffap")
+
+(defvar wallpaper-image-file-extensions
+  '("bmp" "gif" "heif" "jpeg" "jpg" "png" "tif" "tiff" "webp")
+  "List of file extensions that `wallpaper-set' will consider for completion.")
+
+(defun wallpaper--image-file-regexp ()
+  (rx-to-string '(: "." (eval `(or ,@wallpaper-image-file-extensions)) eos) t))
+
+(defun wallpaper--get-default-file ()
+  (catch 'found
+    (dolist (file (list buffer-file-name (ffap-file-at-point)))
+      (when (and file (string-match (wallpaper--image-file-regexp) file))
+        (throw 'found (abbreviate-file-name
+                       (expand-file-name file)))))))
+
+
+;;; wallpaper-set
+
+(defun wallpaper--x-monitor-name ()
+  "Get the monitor name for `wallpaper-set'.
+On a graphical display, try using the same monitor as the current
+frame.
+On a non-graphical display, try to get the name by connecting to
+the display server directly, and run \"xrandr\" if that doesn't
+work.  Prompt for the monitor name if neither method works.
+
+This function is meaningful only on X and is used only there."
+  (if (or (display-graphic-p)
+          noninteractive)
+      (let-alist (car (display-monitor-attributes-list))
+        (if (and .name (member .source '("XRandr" "XRandR 1.5" "Gdk")))
+            .name
+          "0"))
+    (if-let ((name
+              (and (getenv "DISPLAY")
+                   (or
+                    (cdr (assq 'name
+                               (progn
+                                 (x-open-connection (getenv "DISPLAY"))
+                                 (car (display-monitor-attributes-list
+                                       (car (last (terminal-list))))))))
+                    (and (executable-find "xrandr")
+                         (with-temp-buffer
+                           (call-process "xrandr" nil t nil)
+                           (goto-char (point-min))
+                           (re-search-forward (rx bol
+                                                  (group (+ (not (in " \n"))))
+                                                  " connected")
+                                              nil t)
+                           (match-string 1)))))))
+        ;; Prefer "0" to "default" as that works in XFCE.
+        (if (equal name "default") "0" name)
+      (read-string (format-prompt "Monitor name" nil)))))
+
+(defun wallpaper--format-arg (format file)
+  "Format a `wallpaper-command-args' argument ARG.
+FILE is the image file name."
+  (format-spec
+   format
+   `((?f . ,(expand-file-name file))
+     (?F . ,(mapconcat #'url-hexify-string
+                       (file-name-split file)
+                       "/"))
+     (?h . ,(lambda ()
+              (wallpaper--get-height-or-width
+               "height"
+               #'display-pixel-height
+               wallpaper-default-height)))
+     (?w . ,(lambda ()
+              (wallpaper--get-height-or-width
+               "width"
+               #'display-pixel-width
+               wallpaper-default-width)))
+     ;; screen number
+     (?S . ,(let ((display (frame-parameter (selected-frame) 'display)))
+              (if (and display
+                       (string-match (rx ":" (+ (in "0-9")) "."
+                                         (group (+ (in "0-9"))) eos)
+                                     display))
+                  (match-string 1 display)
+                "0")))
+     ;; monitor name
+     (?M . ,#'wallpaper--x-monitor-name)
+     ;; workspace
+     (?W . ,(or (and (fboundp 'x-window-property)
+                     (display-graphic-p)
+                     (number-to-string
+                      (or (x-window-property "_NET_CURRENT_DESKTOP" nil 
"CARDINAL" 0 nil t)
+                          (x-window-property "WIN_WORKSPACE" nil "CARDINAL" 0 
nil t))))
+                "0")))))
+
+(defun wallpaper-default-set-function (file)
+  "Set the wallpaper to FILE using a command.
+This is the default function for `wallpaper-set-function'."
+  (unless wallpaper-command
+    (error "Couldn't find a command to set the wallpaper with"))
+  (let* ((args (if (functionp wallpaper-command-args)
+                   (funcall wallpaper-command-args)
+                 wallpaper-command-args))
+         (real-args (mapcar (lambda (arg) (wallpaper--format-arg arg file))
+                            args))
+         (bufname (format " *wallpaper-%s*" (random)))
+         (process
+          (and wallpaper-command
+               (apply #'start-process "set-wallpaper" bufname
+                      wallpaper-command real-args))))
+    (unless wallpaper-command
+      (error "Couldn't find a suitable command for setting the wallpaper"))
+    (wallpaper-debug "Using command: \"%s %s\""
+            wallpaper-command (string-join real-args " "))
+    (setf (process-sentinel process)
+          (lambda (process status)
+            (unwind-protect
+                (if (and (eq (process-status process) 'exit)
+                         (zerop (process-exit-status process)))
+                    (message "Desktop wallpaper changed to %s"
+                             (abbreviate-file-name file))
+                  (message "command \"%s %s\": %S"
+                           (string-join (process-command process) " ")
+                           (string-replace "\n" "" status)
+                           (with-current-buffer (process-buffer process)
+                             (string-clean-whitespace (buffer-string)))))
+              (ignore-errors
+                (kill-buffer (process-buffer process))))))
+    process))
+
+;;;###autoload
+(defun wallpaper-set (file)
+  "Set the desktop background to FILE in a graphical environment.
+
+On GNU/Linux and other Unix-like systems, this relies on an
+external command.  Which command to use is automatically detected
+in most cases, but can be manually customized with the user
+options `wallpaper-command' and `wallpaper-command-args'.
+
+On MS-Windows and Haiku systems, no external command is needed,
+so the value of `wallpaper-commands' is ignored."
+  (interactive
+   (let ((default (wallpaper--get-default-file)))
+     (list (read-file-name (format-prompt "Set desktop background to" default)
+                           default-directory default
+                           t nil
+                           (let ((re (wallpaper--image-file-regexp)))
+                             (lambda (file-name)
+                               (or (file-directory-p file-name)
+                                   (string-match re file-name))))))))
+  (when (file-directory-p file)
+    (error "Can't set wallpaper to a directory: %s" file))
+  (unless (file-exists-p file)
+    (error "No such file: %s" file))
+  (unless (file-readable-p file)
+    (error "File is not readable: %s" file))
+  (wallpaper-debug "Using image %S:" file)
+  (funcall wallpaper-set-function file))
+
+(provide 'wallpaper)
+
+;;; wallpaper.el ends here
diff --git a/lisp/imenu.el b/lisp/imenu.el
index dcd816cb7a..7f68bd15b9 100644
--- a/lisp/imenu.el
+++ b/lisp/imenu.el
@@ -208,6 +208,13 @@ called within a `save-excursion'.
 
 See `imenu--index-alist' for the format of the buffer index alist.")
 
+;;;###autoload
+(defvar-local imenu-submenus-on-top t
+  "Flag specifying whether items with sublists should be kept at top.
+
+For some indexes, such as those describing sections in a document, it
+makes sense to keep their original order even in the menubar.")
+
 ;;;###autoload
 (defvar-local imenu-prev-index-position-function 'beginning-of-defun
   "Function for finding the next index position.
@@ -310,7 +317,7 @@ element recalculates the buffer's index alist.")
 
 (defvar imenu--history-list nil
   ;; Making this buffer local caused it not to work!
-  "History list for `jump-to-function-in-buffer'.")
+  "History list for `imenu-choose-buffer-index'.")
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;;;
@@ -373,10 +380,11 @@ The returned alist DOES NOT share structure with 
MENULIST."
     (if (memq imenu--rescan-item menulist)
        (setq keep-at-top (list imenu--rescan-item)
              menulist (delq imenu--rescan-item menulist)))
-    (dolist (item menulist)
-      (when (imenu--subalist-p item)
-       (push item keep-at-top)
-       (setq menulist (delq item menulist))))
+    (if imenu-submenus-on-top
+        (dolist (item menulist)
+          (when (imenu--subalist-p item)
+           (push item keep-at-top)
+           (setq menulist (delq item menulist)))))
     (if imenu-sort-function
        (setq menulist (sort menulist imenu-sort-function)))
     (if (> (length menulist) imenu-max-items)
diff --git a/lisp/indent.el b/lisp/indent.el
index b0c1a021da..c7ec5c9a3e 100644
--- a/lisp/indent.el
+++ b/lisp/indent.el
@@ -695,12 +695,10 @@ A value of nil means a tab stop every `tab-width' 
columns."
   :safe 'listp
   :type '(repeat integer))
 
-(defvar edit-tab-stops-map
-  (let ((map (make-sparse-keymap)))
-    (define-key map "\C-x\C-s" 'edit-tab-stops-note-changes)
-    (define-key map "\C-c\C-c" 'edit-tab-stops-note-changes)
-    map)
-  "Keymap used in `edit-tab-stops'.")
+(defvar-keymap edit-tab-stops-map
+  :doc "Keymap used in `edit-tab-stops'."
+  "C-x C-s" #'edit-tab-stops-note-changes
+  "C-c C-c" #'edit-tab-stops-note-changes)
 
 (defvar edit-tab-stops-buffer nil
   "Buffer whose tab stops are being edited.
diff --git a/lisp/info.el b/lisp/info.el
index fb4c3fd782..292bf93a6f 100644
--- a/lisp/info.el
+++ b/lisp/info.el
@@ -451,6 +451,7 @@ or `Info-virtual-nodes'."
        (".info.z"    . "gunzip")
        (".info.bz2"  . ("bzip2" "-dc"))
        (".info.xz"   . "unxz")
+       (".info.zst"  . ("zstd" "-dc"))
        (".info"      . nil)
        ("-info.Z"    . "uncompress")
        ("-info.Y"    . "unyabba")
@@ -458,6 +459,7 @@ or `Info-virtual-nodes'."
        ("-info.bz2"  . ("bzip2" "-dc"))
        ("-info.z"    . "gunzip")
        ("-info.xz"   . "unxz")
+       ("-info.zst"  . ("zstd" "-dc"))
        ("-info"      . nil)
        ("/index.Z"   . "uncompress")
        ("/index.Y"   . "unyabba")
@@ -465,6 +467,7 @@ or `Info-virtual-nodes'."
        ("/index.z"   . "gunzip")
        ("/index.bz2" . ("bzip2" "-dc"))
        ("/index.xz"  . "unxz")
+       ("/index.zst" . ("zstd" "-dc"))
        ("/index"     . nil)
        (".Z"         . "uncompress")
        (".Y"         . "unyabba")
@@ -472,6 +475,7 @@ or `Info-virtual-nodes'."
        (".z"         . "gunzip")
        (".bz2"       . ("bzip2" "-dc"))
        (".xz"        . "unxz")
+       (".zst"       . ("zstd" "-dc"))
        (""           . nil)))
   "List of file name suffixes and associated decoding commands.
 Each entry should be (SUFFIX . STRING); the file is given to
@@ -4451,9 +4455,12 @@ Advanced commands:
   (setq buffer-read-only t)
   (setq Info-tag-table-marker (make-marker))
   (unless (or (display-multi-font-p)
-              (coding-system-equal
-               (coding-system-base (terminal-coding-system))
-               'utf-8))
+              (and (coding-system-equal
+                    (coding-system-base (terminal-coding-system))
+                    'utf-8)
+                   ;; The Linux console has limited character
+                   ;; repertoire even when its encoding is UTF-8.
+                   (not (equal (tty-type) "linux"))))
     (dolist (elt info-symbols-and-replacements)
       (let ((ch (car elt))
             (repl (cdr elt)))
diff --git a/lisp/international/characters.el b/lisp/international/characters.el
index d6e83c81e7..9dcae187f2 100644
--- a/lisp/international/characters.el
+++ b/lisp/international/characters.el
@@ -152,6 +152,9 @@ with L, LRE, or LRO Unicode bidi character type.")
 (modify-category-entry '(#x20000 . #x2FFFF) ?|)
 (modify-category-entry '(#x20000 . #x2FFFF) ?C)
 (modify-category-entry '(#x20000 . #x2FFFF) ?c)
+(modify-category-entry '(#x30000 . #x323AF) ?|)
+(modify-category-entry '(#x30000 . #x323AF) ?C)
+(modify-category-entry '(#x30000 . #x323AF) ?c)
 
 
 ;; Chinese character set (GB2312)
@@ -227,7 +230,7 @@ with L, LRE, or LRO Unicode bidi character type.")
 (modify-category-entry #x1B001 ?H)
 (modify-category-entry #x1B11F ?H)
 (modify-category-entry '(#x1B150 . #x1B152) ?H)
-(modify-category-entry '(#x1B002 . #x1B11E) ?H) ; Hentiagana
+(modify-category-entry '(#x1B002 . #x1B11E) ?H) ; Hentaigana
 
 (modify-category-entry '(#x1AFF0 . #x1B1FF) ?j)
 
@@ -306,6 +309,7 @@ with L, LRE, or LRO Unicode bidi character type.")
 (modify-category-entry '(#xfb50 . #xfdcf) ?b)
 (modify-category-entry '(#xfdf0 . #xfdff) ?b)
 (modify-category-entry '(#xfe70 . #xfefe) ?b)
+(modify-category-entry '(#x10ec0 . #x10eff) ?b)
 
 ;; Cyrillic character set (ISO-8859-5)
 
@@ -615,6 +619,9 @@ with L, LRE, or LRO Unicode bidi character type.")
   ;; Cyrillic Extended-C
   (modify-category-entry '(#x1C80 . #x1C8F) ?y)
 
+  ;; Cyrillic Extended-D
+  (modify-category-entry '(#x1E030 . #x1E08F) ?y)
+
   ;; space characters (see section 6.2 in the Unicode Standard)
   (set-case-syntax ?  " " tbl)
   (setq c #x2000)
@@ -1283,16 +1290,24 @@ with L, LRE, or LRO Unicode bidi character type.")
           (#x2B1B . #x2B1C)
           (#x2B50 . #x2B50)
           (#x2B55 . #x2B55)
-          (#x2E80 . #x303E)
-          (#x3040 . #x3247)
+          (#x2E80 . #x2E99)
+           (#x2E9B . #x2EF3)
+           (#x2F00 . #x2FD5)
+           (#x2FF0 . #x2FFB)
+           (#x3000 . #x303E)
+          (#x3041 . #x3096)
+           (#x3099 . #x30FF)
+           (#x3105 . #x312F)
+           (#x3131 . #x31E3)
+           (#x31F0 . #x3247)
           (#x3250 . #x4DBF)
-          (#x4E00 . #x9FFF)
+          (#x4E00 . #xA48C)
           (#xA490 . #xA4C6)
-          (#xA960 . #xA97F)
+          (#xA960 . #xA97C)
           (#xAC00 . #xD7A3)
           (#xF900 . #xFAFF)
           (#xFE10 . #xFE19)
-          (#xFE30 . #xFE6F)
+          (#xFE30 . #xFE6B)
           (#xFF01 . #xFF60)
           (#xFFE0 . #xFFE6)
           (#x16FE0 . #x16FE4)
@@ -1300,8 +1315,14 @@ with L, LRE, or LRO Unicode bidi character type.")
           (#x17000 . #x187F7)
           (#x18800 . #x18AFF)
           (#x18B00 . #x18CD5)
-          (#x1AFF0 . #x1AFFF)
-          (#x1B000 . #x1B152)
+           (#x18D00 . #x18D08)
+          (#x1AFF0 . #x1AFF3)
+           (#x1AFF5 . #x1AFFB)
+           (#x1AFFD . #x1AFFE)
+          (#x1B000 . #x1B122)
+           (#x1B132 . #x1B132)
+           (#x1B150 . #x1B152)
+           (#x1B155 . #x1B155)
           (#x1B164 . #x1B167)
           (#x1B170 . #x1B2FB)
           (#x1F004 . #x1F004)
@@ -1309,7 +1330,12 @@ with L, LRE, or LRO Unicode bidi character type.")
           (#x1F18E . #x1F18E)
           (#x1F191 . #x1F19A)
           (#x1F1AD . #x1F1AD)
-          (#x1F200 . #x1F320)
+          (#x1F200 . #x1F202)
+           (#x1F210 . #x1F23B)
+           (#x1F240 . #x1F248)
+           (#x1F250 . #x1F251)
+           (#x1F260 . #x1F265)
+           (#x1F300 . #x1F320)
           (#x1F32D . #x1F335)
           (#x1F337 . #x1F37C)
           (#x1F37E . #x1F393)
@@ -1334,10 +1360,11 @@ with L, LRE, or LRO Unicode bidi character type.")
           (#x1F6CC . #x1F6CC)
           (#x1F6D0 . #x1F6D2)
           (#x1F6D5 . #x1F6D7)
-          (#x1F6DD . #x1F6DF)
+          (#x1F6DC . #x1F6DF)
           (#x1F6EB . #x1F6EC)
           (#x1F6F4 . #x1F6FC)
-          (#x1F7E0 . #x1F7F0)
+          (#x1F7E0 . #x1F7EB)
+           (#x1F7F0 . #x1F7F0)
           (#x1F90C . #x1F93A)
           (#x1F93C . #x1F945)
           (#x1F947 . #x1F9FF)
@@ -1345,13 +1372,12 @@ with L, LRE, or LRO Unicode bidi character type.")
           (#x1FA60 . #x1FA6D)
           (#x1FA70 . #x1FA74)
           (#x1FA78 . #x1FA7C)
-          (#x1FA80 . #x1FA86)
-          (#x1FA90 . #x1FAAC)
-          (#x1FAB0 . #x1FABA)
-          (#x1FAC0 . #x1FAC5)
-          (#x1FAD0 . #x1FAD9)
-          (#x1FAE0 . #x1FAE7)
-          (#x1FAF0 . #x1FAF6)
+          (#x1FA80 . #x1FA88)
+          (#x1FA90 . #x1FABD)
+          (#x1FABF . #x1FAC5)
+          (#x1FACE . #x1FADB)
+          (#x1FAE0 . #x1FAE8)
+          (#x1FAF0 . #x1FAF8)
           (#x1FB00 . #x1FB92)
           (#x20000 . #x2FFFF)
           (#x30000 . #x3FFFF))))
@@ -1661,12 +1687,14 @@ GROUP must be one of these symbols:
                     like U+2069 (PDI) and U+202B (RLE).
   `variation-selectors':
                     Characters in the range U+FE00..U+FE0F and
-                    U+E0100..U+E01EF, used for selecting alternate glyph
-                    presentations, such as Emoji vs Text presentation, of
-                    the preceding character(s).
-  `no-font':        For GUI frames, characters for which no suitable
-                    font is found; for text-mode frames, characters
-                    that cannot be encoded by `terminal-coding-system'.
+                    U+E0100..U+E01EF, used for choosing between
+                    glyph variations, such as Emoji vs Text
+                    presentation, of the preceding character(s).
+  `no-font':        For GUI frames, characters for which no
+                    suitable font is found; for text-mode frames,
+                    characters that cannot be encoded by
+                    `terminal-coding-system' or those for which
+                    the terminal has no glyphs.
 
 METHOD must be one of these symbols:
   `zero-width': don't display.
@@ -1680,7 +1708,10 @@ METHOD must be one of these symbols:
 Do not set its value directly from Lisp; the value takes effect
 only via a custom `:set'
 function (`update-glyphless-char-display'), which updates
-`glyphless-char-display'."
+`glyphless-char-display'.
+
+See also the `glyphless-char' face, which is used to display the
+visual representation of these characters."
   :version "28.1"
   :type '(alist :key-type (symbol :tag "Character Group")
                :value-type (symbol :tag "Display Method"))
diff --git a/lisp/international/fontset.el b/lisp/international/fontset.el
index 8d34aa99c3..6e44b85e6c 100644
--- a/lisp/international/fontset.el
+++ b/lisp/international/fontset.el
@@ -221,6 +221,7 @@
        (lycian #x10280)
        (carian #x102A0)
        (old-italic #x10300)
+        (gothic #x10330 #x10348)
        (ugaritic #x10380)
        (old-permic #x10350)
        (old-persian #x103A0)
@@ -270,6 +271,7 @@
        (masaram-gondi #x11D00)
        (gunjala-gondi #x11D60)
        (makasar #x11EE0 #x11EF7)
+        (kawi #x11F04 #x11F41 #x11F4F)
        (cuneiform #x12000)
        (cypro-minoan #x12F90)
        (egyptian #x13000)
@@ -286,18 +288,21 @@
        (byzantine-musical-symbol #x1D000)
        (musical-symbol #x1D100)
        (ancient-greek-musical-notation #x1D200)
+        (kaktovik-numeral #x1D2C0)
        (tai-xuan-jing-symbol #x1D300)
        (counting-rod-numeral #x1D360)
        (nyiakeng-puachue-hmong #x1e100)
-       (toto #x1E290)
-       (wancho #x1e2c0)
-       (mende-kikakui #x1E810)
-       (adlam #x1E900)
-       (indic-siyaq-number #x1ec71)
-       (ottoman-siyaq-number #x1ed01)
+       (toto #x1E290 #x1E295 #x1E2AD)
+       (wancho #x1E2C0 #x1E2E8 #x1E2EF)
+        (nag-mundari #x1E4D0 #x1E4EB #x1E4F0)
+       (mende-kikakui #x1E810 #x1E8A6)
+       (adlam #x1E900 #x1E943)
+       (indic-siyaq-number #x1EC71 #x1EC9F)
+       (ottoman-siyaq-number #x1ED01 #x1ED27)
        (mahjong-tile #x1F000)
        (domino-tile #x1F030)
-        (emoji #x1F300 #x1F600)))
+        (emoji #x1F300 #x1F600)
+        (chess-symbol . [#x1FA00 #x1FA67])))
 
 (defvar otf-script-alist)
 
@@ -371,6 +376,7 @@
        (knda . kannada)
        (knd2 . kannada)
        (kana . kana)   ; Hiragana
+        (kawi . kawi)
        (kali . kayah-li)
        (khar . kharoshthi)
        (kits . khitan-small-script)
@@ -410,6 +416,7 @@
        (mymr . burmese)
         (nand . nandinagari)
        (nbat . nabataean)
+        (nagm . nag-mundari)
        (newa . newa)
        (nko\  . nko)
        (nshu . nushu)
@@ -785,6 +792,7 @@
                    lycian
                    carian
                    old-italic
+                    gothic
                    ugaritic
                    old-persian
                    deseret
@@ -810,6 +818,7 @@
                     siddham
                     modi
                    makasar
+                    kawi
                     dives-akuru
                    cuneiform
                    egyptian
@@ -821,14 +830,21 @@
                    byzantine-musical-symbol
                    musical-symbol
                    ancient-greek-musical-notation
+                    kaktovik-numeral
                    tai-xuan-jing-symbol
                    counting-rod-numeral
                     toto
+                    wancho
+                    nag-mundari
+                    mende-kikakui
                    adlam
                     tai-tham
+                    indic-siyaq-number
+                    ottoman-siyaq-number
                    mahjong-tile
                    domino-tile
-                    emoji))
+                    emoji
+                    chess-symbol))
     (set-fontset-font "fontset-default"
                      script (font-spec :registry "iso10646-1" :script script)
                      nil 'append))
@@ -974,7 +990,11 @@
     (set-fontset-font "fontset-default" symbol-subgroup
                       "-*-fixed-medium-*-*-*-*-*-*-*-*-*-iso10646-1"
                       nil 'prepend))
-  ;; This sets up the Emoji codepoints to use prettier fonts.
+  ;; This sets up the Emoji codepoints to use prettier fonts:
+  ;;  this is fallback, if they don't have color Emoji capabilities...
+  (set-fontset-font "fontset-default" 'emoji
+                    '("Noto Emoji" . "iso10646-1") nil 'prepend)
+  ;;  ...and this is if they do
   (set-fontset-font "fontset-default" 'emoji
                     '("Noto Color Emoji" . "iso10646-1") nil 'prepend)
 
diff --git a/lisp/international/latin1-disp.el 
b/lisp/international/latin1-disp.el
index 4de1d6084f..cb3b429957 100644
--- a/lisp/international/latin1-disp.el
+++ b/lisp/international/latin1-disp.el
@@ -87,7 +87,7 @@ This option also treats some characters in the 
`mule-unicode-...'
 charsets if you don't have a Unicode font with which to display them.
 
 Setting this variable directly does not take effect;
-use either \\[customize] or the function `latin1-display'."
+use either \\[customize] or the command `latin1-display'."
   :group 'latin1-display
   :type 'boolean
   :require 'latin1-disp
@@ -746,7 +746,7 @@ This uses the transliterations of the Lynx browser.  The 
display isn't
 changed if the display can render Unicode characters.
 
 Setting this variable directly does not take effect;
-use either \\[customize] or the function `latin1-display'."
+use either \\[customize] or the command `latin1-display-ucs-per-lynx'."
   :group 'latin1-display
   :type 'boolean
   :require 'latin1-disp
diff --git a/lisp/international/mule-cmds.el b/lisp/international/mule-cmds.el
index 12896cc4b0..48e5c9aa1f 100644
--- a/lisp/international/mule-cmds.el
+++ b/lisp/international/mule-cmds.el
@@ -1389,9 +1389,6 @@ Maximum length of the history list is determined by the 
value
 of `history-length', which see.")
 (put 'input-method-history 'permanent-local t)
 
-(define-obsolete-variable-alias
-  'inactivate-current-input-method-function
-  'deactivate-current-input-method-function "24.3")
 (defvar-local deactivate-current-input-method-function nil
   "Function to call for deactivating the current input method.
 Every input method should set this to an appropriate value when activated.
@@ -1524,10 +1521,6 @@ If INPUT-METHOD is nil, deactivate any current input 
method."
        (setq current-input-method nil)
        (force-mode-line-update)))))
 
-(define-obsolete-function-alias
-  'inactivate-input-method
-  'deactivate-input-method "24.3")
-
 (defun set-input-method (input-method &optional interactive)
   "Select and activate input method INPUT-METHOD for the current buffer.
 This also sets the default input method to the one you specify.
@@ -1741,10 +1734,6 @@ just activated."
   :type 'hook
   :group 'mule)
 
-(define-obsolete-variable-alias
-  'input-method-inactivate-hook
-  'input-method-deactivate-hook "24.3")
-
 (defcustom input-method-deactivate-hook nil
   "Normal hook run just after an input method is deactivated.
 
@@ -1920,8 +1909,11 @@ The default status is as follows:
 
 (reset-language-environment)
 
-(defun set-display-table-and-terminal-coding-system (language-name &optional 
coding-system display)
-  "Set up the display table and terminal coding system for LANGUAGE-NAME."
+(defun set-display-table-and-terminal-coding-system (language-name
+                                                     &optional coding-system
+                                                     display inhibit-refresh)
+  "Set up the display table and terminal coding system for LANGUAGE-NAME.
+If INHIBIT-REFRESH, don't redraw the current frame."
   (let ((coding (get-language-info language-name 'unibyte-display)))
     (if (and coding
             (or (not coding-system)
@@ -1934,7 +1926,8 @@ The default status is as follows:
       (when standard-display-table
        (dotimes (i 128)
          (aset standard-display-table (+ i 128) nil))))
-    (set-terminal-coding-system (or coding-system coding) display)))
+    (set-terminal-coding-system (or coding-system coding) display
+                                inhibit-refresh)))
 
 (defun set-language-environment (language-name)
   "Set up multilingual environment for using LANGUAGE-NAME.
@@ -2671,17 +2664,23 @@ For example, translate \"swedish\" into 
\"sv_SE.ISO8859-1\"."
   "The currently set locale environment.")
 
 (defmacro with-locale-environment (locale-name &rest body)
-  "Execute BODY with the locale set to LOCALE-NAME."
+  "Execute BODY with the locale set to LOCALE-NAME.
+
+Note that changing the locale modifies settings that affect
+the display, such as `terminal-coding-system' and `standard-display-table',
+but this macro does not by itself perform redisplay.  If BODY needs to
+display something with LOCALE-NAME's settings, include a call
+to `redraw-frame' in BODY."
   (declare (indent 1) (debug (sexp def-body)))
   (let ((current (gensym)))
     `(let ((,current current-locale-environment))
        (unwind-protect
            (progn
-             (set-locale-environment ,locale-name)
+             (set-locale-environment ,locale-name nil t)
              ,@body)
-         (set-locale-environment ,current)))))
+         (set-locale-environment ,current nil t)))))
 
-(defun set-locale-environment (&optional locale-name frame)
+(defun set-locale-environment (&optional locale-name frame inhibit-refresh)
   "Set up multilingual environment for using LOCALE-NAME.
 This sets the language environment, the coding system priority,
 the default input method and sometimes other things.
@@ -2710,6 +2709,9 @@ This function sets the `current-locale-environment' 
variable.  To
 change the locale temporarily, `with-locale-environment' can be
 used.
 
+By default, this function will redraw the current frame.  If
+INHIBIT-REFRESH is non-nil, this isn't done.
+
 See also `locale-charset-language-names', `locale-language-names',
 `locale-preferred-coding-systems' and `locale-coding-system'."
   (interactive (list (completing-read "Set environment for locale: "
@@ -2819,7 +2821,7 @@ See also `locale-charset-language-names', 
`locale-language-names',
            (set-language-environment language-name))
 
          (set-display-table-and-terminal-coding-system
-          language-name coding-system frame)
+          language-name coding-system frame inhibit-refresh)
 
          ;; Set the `keyboard-coding-system' if appropriate (tty
          ;; only).  At least X and MS Windows can generate
@@ -2876,7 +2878,7 @@ See also `locale-charset-language-names', 
`locale-language-names',
           (or output-coding (setq output-coding code-page-coding))
          (unless frame (setq locale-coding-system locale-coding))
          (set-keyboard-coding-system code-page-coding frame)
-         (set-terminal-coding-system output-coding frame)
+         (set-terminal-coding-system output-coding frame inhibit-refresh)
          (setq default-file-name-coding-system ansi-code-page-coding))))
 
     (when (eq system-type 'darwin)
@@ -2887,7 +2889,7 @@ See also `locale-charset-language-names', 
`locale-language-names',
       ;; the locale.
       (when (and (null window-system)
                 (equal (getenv "TERM_PROGRAM" frame) "Apple_Terminal"))
-       (set-terminal-coding-system 'utf-8)
+       (set-terminal-coding-system 'utf-8 nil inhibit-refresh)
        (set-keyboard-coding-system 'utf-8)))
 
     ;; Default to A4 paper if we're not in a C, POSIX or US locale.
@@ -3195,7 +3197,7 @@ Defines the sorting order either by character names or 
their codepoints."
   :group 'mule
   :version "28.1")
 
-(defun read-char-by-name (prompt)
+(defun read-char-by-name (prompt &optional allow-single)
   "Read a character by its Unicode name or hex number string.
 Display PROMPT and read a string that represents a character by its
 Unicode property `name' or `old-name'.
@@ -3216,7 +3218,10 @@ Accept a name like \"CIRCULATION FUNCTION\", a 
hexadecimal
 number like \"2A10\", or a number in hash notation (e.g.,
 \"#x2a10\" for hex, \"10r10768\" for decimal, or \"#o25020\" for
 octal).  Treat otherwise-ambiguous strings like \"BED\" (U+1F6CF)
-as names, not numbers."
+as names, not numbers.
+
+Optional arg ALLOW-SINGLE non-nil means to additionally allow
+single characters to be treated as standing for themselves."
   (let* ((enable-recursive-minibuffers t)
         (completion-ignore-case t)
         (completion-tab-width 4)
@@ -3239,6 +3244,9 @@ as names, not numbers."
         (char
           (cond
            ((char-from-name input t))
+           ((and allow-single
+                 (string-match-p "\\`.\\'" input)
+                 (ignore-errors (string-to-char input))))
            ((string-match-p "\\`[[:xdigit:]]+\\'" input)
             (ignore-errors (string-to-number input 16)))
            ((string-match-p "\\`#\\([bBoOxX]\\|[0-9]+[rR]\\)[0-9a-zA-Z]+\\'"
@@ -3248,7 +3256,6 @@ as names, not numbers."
       (error "Invalid character"))
     char))
 
-(define-obsolete-function-alias 'ucs-insert 'insert-char "24.3")
 (define-key ctl-x-map "8\r" 'insert-char)
 (define-key ctl-x-map "8e"
             (define-keymap
diff --git a/lisp/international/mule.el b/lisp/international/mule.el
index ab74c2cffd..6a794a8410 100644
--- a/lisp/international/mule.el
+++ b/lisp/international/mule.el
@@ -767,7 +767,7 @@ VALUE must be a translation table to use on encoding.
 
 VALUE must be a function to call after some text is inserted and
 decoded by the coding system itself and before any functions in
-`after-insert-functions' are called.  This function is passed one
+`after-insert-file-functions' are called.  This function is passed one
 argument: the number of characters in the text to convert, with
 point at the start of the text.  The function should leave point
 and the match data unchanged, and should return the new character
@@ -1362,7 +1362,8 @@ to CODING-SYSTEM."
 This is normally set according to the selected language environment.
 See also the command `set-terminal-coding-system'.")
 
-(defun set-terminal-coding-system (coding-system &optional terminal)
+(defun set-terminal-coding-system (coding-system &optional terminal
+                                                 inhibit-refresh)
   "Set coding system of terminal output to CODING-SYSTEM.
 All text output to TERMINAL will be encoded
 with the specified coding system.
@@ -1371,9 +1372,12 @@ For a list of possible values of CODING-SYSTEM, use 
\\[list-coding-systems].
 The default is determined by the selected language environment
 or by the previous use of this command.
 
-TERMINAL may be a terminal object, a frame, or nil for the
-selected frame's terminal.  The setting has no effect on
-graphical terminals."
+Optional argument TERMINAL may be a terminal object or a frame,
+and defaults to the selected frame's terminal.  The setting has no
+effect on graphical terminals.
+
+By default, this function will redraw the current frame;
+optional argument INHIBIT-REFRESH, if non-nil, prevents that."
   (interactive
    (list (let ((default (if (and (not (terminal-coding-system))
                                 default-terminal-coding-system)
@@ -1387,7 +1391,8 @@ graphical terminals."
   (if coding-system
       (setq default-terminal-coding-system coding-system))
   (set-terminal-coding-system-internal coding-system terminal)
-  (redraw-frame))
+  (unless inhibit-refresh
+    (redraw-frame)))
 
 (defvar default-keyboard-coding-system nil
   "Default value of the keyboard coding system.
diff --git a/lisp/international/quail.el b/lisp/international/quail.el
index 4bb6dbcc8e..e2ba485bbe 100644
--- a/lisp/international/quail.el
+++ b/lisp/international/quail.el
@@ -540,8 +540,6 @@ This function runs the normal hook `quail-deactivate-hook'."
   (interactive)
   (quail-activate -1))
 
-(define-obsolete-function-alias 'quail-inactivate 'quail-deactivate "24.3")
-
 (defun quail-activate (&optional arg)
   "Activate Quail input method.
 With ARG, activate Quail input method if and only if arg is positive.
@@ -583,10 +581,6 @@ While this input method is active, the variable
     (run-hooks 'quail-activate-hook)
     (setq-local input-method-function #'quail-input-method)))
 
-(define-obsolete-variable-alias
-  'quail-inactivate-hook
-  'quail-deactivate-hook "24.3")
-
 (defun quail-exit-from-minibuffer ()
   (deactivate-input-method)
   (if (<= (minibuffer-depth) 1)
diff --git a/lisp/international/rfc1843.el b/lisp/international/rfc1843.el
index 5050b7c030..73b610c034 100644
--- a/lisp/international/rfc1843.el
+++ b/lisp/international/rfc1843.el
@@ -1,4 +1,4 @@
-;;; rfc1843.el --- HZ (rfc1843) decoding  -*- lexical-binding:t -*-
+;;; rfc1843.el --- HZ (RFC 1843) decoding  -*- lexical-binding:t -*-
 
 ;; Copyright (C) 1998-2022 Free Software Foundation, Inc.
 
@@ -22,6 +22,9 @@
 
 ;;; Commentary:
 
+;; HZ (RFC 1843) decoding.  HZ is a data format for exchanging files
+;; of arbitrarily mixed Chinese and ASCII characters.
+;;
 ;; Test:
 ;; (rfc1843-decode-string  "~{<:Ky2;S{#,NpJ)l6HK!#~}")
 
diff --git a/lisp/international/robin.el b/lisp/international/robin.el
index 4c498d7f92..9f0ff80e62 100644
--- a/lisp/international/robin.el
+++ b/lisp/international/robin.el
@@ -393,8 +393,6 @@ A nil value means no package is selected.")
   (interactive)
   (robin-activate -1))
 
-(define-obsolete-function-alias 'robin-inactivate 'robin-deactivate "24.3")
-
 (defun robin-activate (&optional arg)
   "Activate robin input method.
 
@@ -423,10 +421,6 @@ While this input method is active, the variable
               'robin-activate-hook)
     (setq-local input-method-function 'robin-input-method)))
 
-(define-obsolete-variable-alias
-  'robin-inactivate-hook
-  'robin-deactivate-hook "24.3")
-
 (defun robin-exit-from-minibuffer ()
   (deactivate-input-method)
   (if (<= (minibuffer-depth) 1)
diff --git a/lisp/international/textsec-check.el 
b/lisp/international/textsec-check.el
index 567ef73feb..99ffd397e2 100644
--- a/lisp/international/textsec-check.el
+++ b/lisp/international/textsec-check.el
@@ -35,7 +35,7 @@ If nil, these checks are disabled."
   :version "29.1")
 
 (defface textsec-suspicious
-  '((t (:weight bold :background "red")))
+  '((t (:weight bold :background "red" :foreground "white")))
   "Face used to highlight suspicious strings.")
 
 ;;;###autoload
diff --git a/lisp/international/titdic-cnv.el b/lisp/international/titdic-cnv.el
index 080045e752..d2a6ee1e9d 100644
--- a/lisp/international/titdic-cnv.el
+++ b/lisp/international/titdic-cnv.el
@@ -281,7 +281,7 @@ SPC, 6, 3, 4, or 7 specifying a tone (SPC:陰平, 6:陽平, 3:上聲, 
4:去聲,
     (while (not (eobp))
       (let ((ch (following-char))
            (pos (point)))
-       (cond ((= ch ?C)                ; COMMENT
+       (cond ((eq ch ?C)               ; COMMENT
               (cond ((looking-at "COMMENT")
                      (let ((pos (match-end 0))
                            (to (progn (end-of-line) (point))))
@@ -295,7 +295,7 @@ SPC, 6, 3, 4, or 7 specifying a tone (SPC:陰平, 6:陽平, 3:上聲, 
4:去聲,
                        (setq tit-comments
                              (cons (buffer-substring-no-properties pos (point))
                                    tit-comments))))))
-             ((= ch ?M)                ; MULTICHOICE, MOVERIGHT, MOVELEFT
+             ((eq ch ?M)               ; MULTICHOICE, MOVERIGHT, MOVELEFT
               (cond ((looking-at "MULTICHOICE:[ \t]*")
                      (goto-char (match-end 0))
                      (setq tit-multichoice (looking-at "YES")))
@@ -305,7 +305,7 @@ SPC, 6, 3, 4, or 7 specifying a tone (SPC:陰平, 6:陽平, 3:上聲, 
4:去聲,
                     ((looking-at "MOVELEFT:[ \t]*")
                      (goto-char (match-end 0))
                      (setq tit-moveleft (tit-read-key-value)))))
-             ((= ch ?P)                ; PROMPT
+             ((eq ch ?P)               ; PROMPT
               (cond ((looking-at "PROMPT:[ \t]*")
                      (goto-char (match-end 0))
                      (setq tit-prompt (tit-read-key-value))
@@ -316,7 +316,7 @@ SPC, 6, 3, 4, or 7 specifying a tone (SPC:陰平, 6:陽平, 3:上聲, 
4:去聲,
                        (if (or (eq (nth 1 split) 32)
                                (eq (nth 2 split) 32))
                            (setq tit-prompt (substring tit-prompt 0 -1)))))))
-             ((= ch ?B)                ; BACKSPACE, BEGINDICTIONARY,
+             ((eq ch ?B)               ; BACKSPACE, BEGINDICTIONARY,
                                        ; BEGINPHRASE
               (cond ((looking-at "BACKSPACE:[ \t]*")
                      (goto-char (match-end 0))
@@ -325,7 +325,7 @@ SPC, 6, 3, 4, or 7 specifying a tone (SPC:陰平, 6:陽平, 3:上聲, 
4:去聲,
                      (setq tit-dictionary t))
                     ((looking-at "BEGINPHRASE")
                      (setq tit-dictionary nil))))
-             ((= ch ?K)                ; KEYPROMPT
+             ((eq ch ?K)               ; KEYPROMPT
               (cond ((looking-at "KEYPROMPT(\\(.*\\)):[ \t]*")
                      (let ((key-char (match-string 1)))
                        (goto-char (match-end 0))
diff --git a/lisp/isearch.el b/lisp/isearch.el
index 9f1fbb14a4..3e840b014f 100644
--- a/lisp/isearch.el
+++ b/lisp/isearch.el
@@ -832,17 +832,15 @@ This is like `describe-bindings', but displays only 
Isearch keys."
             :image '(isearch-tool-bar-image "left-arrow")))
     map))
 
-(defvar minibuffer-local-isearch-map
-  (let ((map (make-sparse-keymap)))
-    (set-keymap-parent map minibuffer-local-map)
-    (define-key map "\r"    'exit-minibuffer)
-    (define-key map "\M-\t" 'isearch-complete-edit)
-    (define-key map "\C-s"  'isearch-forward-exit-minibuffer)
-    (define-key map "\C-r"  'isearch-reverse-exit-minibuffer)
-    (define-key map "\C-f"  'isearch-yank-char-in-minibuffer)
-    (define-key map [right] 'isearch-yank-char-in-minibuffer)
-    map)
-  "Keymap for editing Isearch strings in the minibuffer.")
+(defvar-keymap minibuffer-local-isearch-map
+  :doc "Keymap for editing Isearch strings in the minibuffer."
+  :parent minibuffer-local-map
+  "RET"     #'exit-minibuffer
+  "M-TAB"   #'isearch-complete-edit
+  "C-s"     #'isearch-forward-exit-minibuffer
+  "C-r"     #'isearch-reverse-exit-minibuffer
+  "C-f"     #'isearch-yank-char-in-minibuffer
+  "<right>" #'isearch-yank-char-in-minibuffer)
 
 ;; Internal variables declared globally for byte-compiler.
 ;; These are all set with setq while isearching
@@ -2389,8 +2387,7 @@ type \\[help-command] at that time."
              (if (use-region-p) " in region" ""))
       isearch-regexp)
      t isearch-regexp (or delimited isearch-regexp-function) nil nil
-     (if (use-region-p) (region-beginning))
-     (if (use-region-p) (region-end))
+     (use-region-beginning) (use-region-end)
      backward))
   (and isearch-recursive-edit (exit-recursive-edit)))
 
diff --git a/lisp/jit-lock.el b/lisp/jit-lock.el
index be26ca55f0..6ef46ad60b 100644
--- a/lisp/jit-lock.el
+++ b/lisp/jit-lock.el
@@ -45,9 +45,12 @@ Preserves the `buffer-modified-p' state of the current 
buffer."
   :group 'font-lock)
 
 (defcustom jit-lock-chunk-size 1500
-  "Jit-lock fontifies chunks of at most this many characters at a time.
+  "Jit-lock asks to fontify chunks of at most this many characters at a time.
 
-This variable controls both `display-time' and stealth fontification.
+The actual size of the fontified chunk of text can be different,
+depending on what the `fontification-functions' actually decide to do.
+
+This variable controls both display-time and stealth fontifications.
 
 The optimum value is a little over the typical number of buffer
 characters which fit in a typical window."
@@ -379,7 +382,11 @@ is active."
                   (or (not (eq jit-lock-defer-time 0))
                       (input-pending-p))))
        ;; No deferral.
-       (jit-lock-fontify-now start (+ start jit-lock-chunk-size))
+       (let* ((cend (min (point-max) (+ start jit-lock-chunk-size)))
+              (vend (next-single-property-change start 'invisible nil cend)))
+         ;; Presumably if we're called it means `start' is
+         ;; not at EOB (nor invisible) and hence (> vend start).
+         (jit-lock-fontify-now start vend))
       ;; Record the buffer for later fontification.
       (unless (memq (current-buffer) jit-lock-defer-buffers)
        (push (current-buffer) jit-lock-defer-buffers))
diff --git a/lisp/language/cyrillic.el b/lisp/language/cyrillic.el
index 4576373a6d..a9017062cc 100644
--- a/lisp/language/cyrillic.el
+++ b/lisp/language/cyrillic.el
@@ -131,13 +131,15 @@ Support for Russian using koi8-r and the russian-computer 
input method.")
   :mime-charset 'koi8-u)
 
 (set-language-info-alist
- "Ukrainian" '((charset koi8-u)
+ "Ukrainian" '((tutorial . "TUTORIAL.uk")
+              (charset koi8-u)
               (coding-system koi8-u)
               (coding-priority koi8-u)
               (nonascii-translation . koi8-u)
               (input-method . "ukrainian-computer")
+              (sample-text . "Ukrainian (Українська)   Вітаю / Добрий день! / 
Привіт")
               (documentation
-               . "Support for Ukrainian with KOI8-U character set."))
+               . "Support for Ukrainian with koi8-u character set."))
  '("Cyrillic"))
 
 ;;; ALTERNATIVNYJ stuff
@@ -254,14 +256,6 @@ Support for Russian using koi8-r and the russian-computer 
input method.")
 \(The name Belarusian replaced Byelorussian in the early 1990s.)"))
  '("Cyrillic"))
 
-(set-language-info-alist
- "Ukrainian" '((coding-system koi8-u)
-              (coding-priority koi8-u)
-              (input-method . "ukrainian-computer")
-              (documentation
-               . "Support for Ukrainian with koi8-u character set."))
- '("Cyrillic"))
-
 (provide 'cyrillic)
 
 ;;; cyrillic.el ends here
diff --git a/lisp/language/ethio-util.el b/lisp/language/ethio-util.el
index a0159679da..2f76acfe7c 100644
--- a/lisp/language/ethio-util.el
+++ b/lisp/language/ethio-util.el
@@ -794,15 +794,15 @@ The 2nd and 3rd arguments BEGIN and END specify the 
region."
   "This function is deprecated."
   (interactive "*cInput number: 1.����  2.����  3.����  4.����  5.����")
   (cond
-   ((= arg ?1)
+   ((eq arg ?1)
     (insert "����"))
-   ((= arg ?2)
+   ((eq arg ?2)
     (insert "����"))
-   ((= arg ?3)
+   ((eq arg ?3)
     (insert "����"))
-   ((= arg ?4)
+   ((eq arg ?4)
     (insert "����"))
-   ((= arg ?5)
+   ((eq arg ?5)
     (insert "����"))
    (t
     (error ""))))
@@ -816,7 +816,7 @@ The 2nd and 3rd arguments BEGIN and END specify the region."
   "Convert each fidel characters in the current buffer into a fidel-tex 
command."
   (interactive)
   (let ((buffer-read-only nil)
-       comp ch)
+       comp)
 
     ;; Special treatment for geminated characters.
     ;; Geminated characters la", etc. change into \geminateG{\laG}, etc.
@@ -835,21 +835,22 @@ The 2nd and 3rd arguments BEGIN and END specify the 
region."
     ;; Special Ethiopic punctuation.
     (goto-char (point-min))
     (while (re-search-forward "\\ce[».?]\\|«\\ce" nil t)
-      (cond
-       ((= (setq ch (preceding-char)) ?\»)
-       (delete-char -1)
-       (insert "\\rquoteG"))
-       ((= ch ?.)
-       (delete-char -1)
-       (insert "\\dotG"))
-       ((= ch ??)
-       (delete-char -1)
-       (insert "\\qmarkG"))
-       (t
-       (forward-char -1)
-       (delete-char -1)
-       (insert "\\lquoteG")
-       (forward-char 1))))
+      (let ((ch (preceding-char)))
+        (cond
+         ((eq ch ?\»)
+         (delete-char -1)
+         (insert "\\rquoteG"))
+         ((eq ch ?.)
+         (delete-char -1)
+         (insert "\\dotG"))
+         ((eq ch ??)
+         (delete-char -1)
+         (insert "\\qmarkG"))
+         (t
+         (forward-char -1)
+         (delete-char -1)
+         (insert "\\lquoteG")
+         (forward-char 1)))))
 
     ;; Ethiopic characters to TeX macros
     (robin-invert-region (point-min) (point-max) "ethiopic-tex")
diff --git a/lisp/language/indian.el b/lisp/language/indian.el
index 407173827f..4994cfdc7a 100644
--- a/lisp/language/indian.el
+++ b/lisp/language/indian.el
@@ -266,6 +266,27 @@ Meetei language and its script Meetei Mayek are supported 
in this
 language environment."))
  '("Indian"))
 
+(set-language-info-alist
+ "Wancho" '((charset unicode)
+            (coding-system utf-8)
+            (coding-priority utf-8)
+            (input-method . "wancho")
+            (sample-text . "Wancho (𞋒𞋀𞋉𞋃𞋕)     𞋂𞋈𞋛")
+            (documentation . "\
+Wancho language and its script are supported in this language
+environment."))
+ '("Indian"))
+
+(set-language-info-alist
+ "Toto" '((charset unicode)
+          (coding-system utf-8)
+          (coding-priority utf-8)
+          (input-method . "toto")
+          (documentation . "\
+Toto language using the Toto script is supported in this language
+environment."))
+ '("Indian"))
+
 ;; Replace mnemonic characters in REGEXP according to TABLE.  TABLE is
 ;; an alist of (MNEMONIC-STRING . REPLACEMENT-STRING).
 
@@ -340,23 +361,24 @@ language environment."))
 
 (defconst gurmukhi-composable-pattern
   (let ((table
-        '(("a" . "[\u0A01\u0A02\u0A70]") ; SIGN ADAK BINDI .. BINDI, TIPPI
+        '(("a" . "[\u0A01\u0A02\u0A70\u0A71\u0A75]") ; SIGN ADAK BINDI .. 
BINDI, TIPPI, ADDAK, YAKASH
           ("A" . "\u0A03")             ; SIGN VISARGA
-          ("V" . "[\u0A05-\u0A14]")    ; independent vowel
+          ("V" . "[\u0A05-\u0A14\u0A72\u0A73]")        ; independent vowel
           ("C" . "[\u0A15-\u0A39\u0A59-\u0A5E]")       ; consonant
           ("Y" . "[\u0A2F\u0A30\u0A35\u0A39]") ; YA, RA, VA, HA
           ("n" . "\u0A3C")             ; NUKTA
           ("v" . "[\u0A3E-\u0A4C]")    ; vowel sign
           ("H" . "\u0A4D")             ; VIRAMA
+           ("s" . "\u0A51")            ; stress sign
           ("N" . "\u200C")             ; ZWNJ
           ("J" . "\u200D")             ; ZWJ
           ("X" . "[\u0A00-\u0A7F]")))) ; all coverage
     (indian-compose-regexp
      (concat
       ;; consonant-based syllables, or
-      "Cn?\\(?:J?HJ?Cn?\\)*\\(?:H[NJ]?\\|v*n?a?A?\\)\\|"
+      "Cn?\\(?:J?HJ?Cn?\\)*\\(?:H[NJ]?\\|v*n?a?s?v?A?\\)\\|"
       ;; syllables with an independent vowel, or
-      "Vn?\\(?:J?HY\\)?v*n?a?A?\\|"
+      "Vn?\\(?:J?HY\\)?v*n?a?s?A?\\|"
       ;; special consonant form, or
       "JHY\\|"
       ;; any other singleton characters
diff --git a/lisp/language/lao.el b/lisp/language/lao.el
index 1861eff15e..0ad5b9f84e 100644
--- a/lisp/language/lao.el
+++ b/lisp/language/lao.el
@@ -60,9 +60,9 @@
           (len (length chars))
           ;; Replace `c', `t', `v' to consonant, tone, and vowel.
            (regexp (mapconcat (lambda (c)
-                                (cond ((= c ?c) consonant)
-                                      ((= c ?t) tone)
-                                      ((= c ?v) vowel-upper-lower)
+                                (cond ((eq c ?c) consonant)
+                                      ((eq c ?t) tone)
+                                      ((eq c ?v) vowel-upper-lower)
                                       (t (string c))))
                              (cdr l) ""))
           ;; Element of composition-function-table.
diff --git a/lisp/language/misc-lang.el b/lisp/language/misc-lang.el
index 3d5b68f84b..4a2e7838fc 100644
--- a/lisp/language/misc-lang.el
+++ b/lisp/language/misc-lang.el
@@ -269,6 +269,56 @@ using the Kharoṣṭhī script.")))
                                   modifier "*")
                           1 'font-shape-gstring))))
 
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Adlam
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(set-language-info-alist
+ "Adlam" '((charset unicode)
+           (coding-system utf-8)
+           (coding-priority utf-8)
+           (input-method . "adlam")
+           (sample-text . "Adlam (𞤀𞤣𞤤𞤢𞤥)       𞤅𞤢𞤤𞤢𞥄𞤥")
+           (documentation . "\
+Fulani language and its script Adlam are supported
+in this language environment.")))
+
+;; Adlam composition rules
+(set-char-table-range
+ composition-function-table
+ '(#x1E900 . #x1E95F)
+ (list (vector
+        "[\x1E900-\x1E95F]+"
+        0 'font-shape-gstring)))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Mende Kikakui
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(set-language-info-alist
+ "Mende Kikakui" '((charset unicode)
+                   (coding-system utf-8)
+                   (coding-priority utf-8)
+                   (input-method . "mende-kikakui")
+                   (sample-text . "Mende Kikakui (𞠀𞠁𞠂) 𞠛𞠉")
+                   (documentation . "\
+Mende language and its script Kikakui are supported
+in this language environment.")))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Gothic
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(set-language-info-alist
+ "Gothic" '((charset unicode)
+            (coding-system utf-8)
+            (coding-priority utf-8)
+            (input-method . "gothic")
+            (sample-text . "Gothic (𐌲𐌿𐍄𐌹𐍃𐌺𐌰)   𐌷𐌰𐌹𐌻𐍃 / 𐌷𐌰𐌹𐌻𐌰")
+            (documentation . "\
+Ancient Gothic language using the Gothic script is supported in this
+language environment.")))
+
 (provide 'misc-lang)
 
 ;;; misc-lang.el ends here
diff --git a/lisp/ldefs-boot.el b/lisp/ldefs-boot.el
index 07dfc23a09..c9502fbb21 100644
--- a/lisp/ldefs-boot.el
+++ b/lisp/ldefs-boot.el
@@ -897,6 +897,15 @@ variable, and is meant to be used in 
`compilation-filter-hook'.")
 (register-definition-prefixes "ansi-color" '("ansi-color-"))
 
 
+;;; Generated autoloads from ansi-osc.el
+
+(autoload 'ansi-osc-compilation-filter "ansi-osc" "\
+Maybe collect OSC control sequences.
+This function depends on the variable `ansi-osc-for-compilation-buffer',
+and is meant to be used in `compilation-filter-hook'.")
+(register-definition-prefixes "ansi-osc" '("ansi-osc-"))
+
+
 ;;; Generated autoloads from progmodes/antlr-mode.el
 
 (push (purecopy '(antlr-mode 2 2 3)) package--builtin-versions)
@@ -4004,6 +4013,16 @@ FROM is for internal use.  It specifies an index in the 
STRING
 from which to start.
 
 (fn STRING &optional LAX FROM)")
+(autoload 'describe-char-fold-equivalences "char-fold" "\
+Display characters equivalent to CHAR under character-folding.
+Prompt for CHAR (using `read-char-by-name', which see for how to
+specify the character).  With no input, i.e. when CHAR is nil,
+describe all available character equivalences of `char-fold-to-regexp'.
+Optional argument LAX (interactively, the prefix argument), if
+non-nil, means also include partially matching ligatures and
+non-canonical equivalences.
+
+(fn CHAR &optional LAX)" t)
 (register-definition-prefixes "char-fold" '("char-fold-"))
 
 
@@ -4038,14 +4057,10 @@ Returns non-nil if any false statements are found.
 (put 'checkdoc-force-history-flag 'safe-local-variable #'booleanp)
 (put 'checkdoc-permit-comma-termination-flag 'safe-local-variable #'booleanp)
 (put 'checkdoc-spellcheck-documentation-flag 'safe-local-variable #'booleanp)
-(put 'checkdoc-ispell-list-words 'safe-local-variable 
#'checkdoc-list-of-strings-p)
+(put 'checkdoc-ispell-list-words 'safe-local-variable #'list-of-strings-p)
 (put 'checkdoc-arguments-in-order-flag 'safe-local-variable #'booleanp)
 (put 'checkdoc-verb-check-experimental-flag 'safe-local-variable #'booleanp)
-(put 'checkdoc-symbol-words 'safe-local-variable #'checkdoc-list-of-strings-p)
-(autoload 'checkdoc-list-of-strings-p "checkdoc" "\
-Return t when OBJ is a list of strings.
-
-(fn OBJ)")
+(put 'checkdoc-symbol-words 'safe-local-variable #'list-of-strings-p)
 (put 'checkdoc-proper-noun-regexp 'safe-local-variable 'stringp)
 (put 'checkdoc-common-verbs-regexp 'safe-local-variable 'stringp)
 (autoload 'checkdoc "checkdoc" "\
@@ -4306,79 +4321,6 @@ it is disabled.
 ;;; Generated autoloads from emacs-lisp/cl-generic.el
 
 (push (purecopy '(cl-generic 1 0)) package--builtin-versions)
-(autoload 'cl-defgeneric "cl-generic" "\
-Create a generic function NAME.
-DOC-STRING is the base documentation for this class.  A generic
-function has no body, as its purpose is to decide which method body
-is appropriate to use.  Specific methods are defined with `cl-defmethod'.
-With this implementation the ARGS are currently ignored.
-OPTIONS-AND-METHODS currently understands:
-- (:documentation DOCSTRING)
-- (declare DECLARATIONS)
-- (:argument-precedence-order &rest ARGS)
-- (:method [QUALIFIERS...] ARGS &rest BODY)
-DEFAULT-BODY, if present, is used as the body of a default method.
-
-(fn NAME ARGS [DOC-STRING] [OPTIONS-AND-METHODS...] &rest DEFAULT-BODY)" nil t)
-(function-put 'cl-defgeneric 'lisp-indent-function 2)
-(function-put 'cl-defgeneric 'doc-string-elt 3)
-(autoload 'cl-generic-define "cl-generic" "\
-
-
-(fn NAME ARGS OPTIONS)")
-(autoload 'cl-defmethod "cl-generic" "\
-Define a new method for generic function NAME.
-This defines an implementation of NAME to use for invocations
-of specific types of arguments.
-
-ARGS is a list of dispatch arguments (see `cl-defun'), but where
-each variable element is either just a single variable name VAR,
-or a list on the form (VAR TYPE).
-
-For instance:
-
-  (cl-defmethod foo (bar (format-string string) &optional zot)
-    (format format-string bar))
-
-The dispatch arguments have to be among the mandatory arguments, and
-all methods of NAME have to use the same set of arguments for dispatch.
-Each dispatch argument and TYPE are specified in ARGS where the corresponding
-formal argument appears as (VAR TYPE) rather than just VAR.
-
-The optional EXTRA element, on the form `:extra STRING', allows
-you to add more methods for the same specializers and qualifiers.
-These are distinguished by STRING.
-
-The optional argument QUALIFIER is a specifier that modifies how
-the method is combined with other methods, including:
-   :before  - Method will be called before the primary
-   :after   - Method will be called after the primary
-   :around  - Method will be called around everything else
-The absence of QUALIFIER means this is a \"primary\" method.
-The set of acceptable qualifiers and their meaning is defined
-(and can be extended) by the methods of `cl-generic-combine-methods'.
-
-ARGS can also include so-called context specializers, introduced by
-`&context' (which should appear right after the mandatory arguments,
-before any &optional or &rest).  They have the form (EXPR TYPE) where
-EXPR is an Elisp expression whose value should match TYPE for the
-method to be applicable.
-
-The set of acceptable TYPEs (also called \"specializers\") is defined
-(and can be extended) by the various methods of `cl-generic-generalizers'.
-
-(fn NAME [EXTRA] [QUALIFIER] ARGS &rest [DOCSTRING] BODY)" nil t)
-(function-put 'cl-defmethod 'doc-string-elt 'cl--defmethod-doc-pos)
-(function-put 'cl-defmethod 'lisp-indent-function 'defun)
-(autoload 'cl-generic-define-method "cl-generic" "\
-
-
-(fn NAME QUALIFIERS ARGS CALL-CON FUNCTION)")
-(autoload 'cl-find-method "cl-generic" "\
-
-
-(fn GENERIC QUALIFIERS SPECIALIZERS)")
-(register-definition-prefixes "cl-generic" '("cl-"))
 
 
 ;;; Generated autoloads from emacs-lisp/cl-indent.el
@@ -4466,8 +4408,6 @@ instead.
 ;;; Generated autoloads from emacs-lisp/cl-lib.el
 
 (push (purecopy '(cl-lib 1 0)) package--builtin-versions)
-(define-obsolete-variable-alias 'custom-print-functions 
'cl-custom-print-functions "\
-24.3")
 (defvar cl-custom-print-functions nil "\
 This is a list of functions that format user objects for printing.
 Each function is called in turn with three arguments: the object, the
@@ -5020,6 +4960,8 @@ evaluate `compilation-shell-minor-mode'.
 The mode's hook is called both when the mode is enabled and when
 it is disabled.
 
+\\{compilation-shell-minor-mode-map}
+
 (fn &optional ARG)" t)
 (autoload 'compilation-minor-mode "compile" "\
 Toggle Compilation minor mode.
@@ -5043,6 +4985,8 @@ evaluate `compilation-minor-mode'.
 The mode's hook is called both when the mode is enabled and when
 it is disabled.
 
+\\{compilation-minor-mode-map}
+
 (fn &optional ARG)" t)
 (autoload 'compilation-next-error-function "compile" "\
 Advance to the next error message and visit the file where the error was.
@@ -7242,6 +7186,14 @@ The mode's hook is called both when the mode is enabled 
and when
 it is disabled.
 
 (fn &optional ARG)" t)
+(defvar diff-add-log-use-relative-names nil "\
+Use relative file names when generating ChangeLog skeletons.
+The files will be relative to the root directory of the VC
+repository.  This option affects the behavior of
+`diff-add-log-current-defuns'.")
+(custom-autoload 'diff-add-log-use-relative-names "diff-mode" t)
+(put 'diff-add-log-use-relative-names 'safe-local-variable #'booleanp)
+(autoload 'diff-vc-deduce-fileset "diff-mode")
 (register-definition-prefixes "diff-mode" '("diff-"))
 
 
@@ -7570,6 +7522,34 @@ This provides increased compatibility for users who call 
this function
 in `.emacs'.
 
 (fn ARG)")
+(autoload 'standard-display-by-replacement-char "disp-table" "\
+Produce code to display characters between FROM and TO using REPL.
+This function produces a buffer with code to set up `standard-display-table'
+such that characters that cannot be displayed by the terminal, and
+don't already have their display set up in `standard-display-table', will
+be represented by a replacement character.  You can evaluate the produced
+code to use the setup for the current Emacs session, or copy the code
+into your init file, to make Emacs use it for subsequent sessions.
+
+Interactively, the produced code arranges for any character in
+the range [#x100..#x10FFFF] that the terminal cannot display to
+be represented by the #xFFFD Unicode replacement character.
+
+When called from Lisp, FROM and TO define the range of characters for
+which to produce the setup code for `standard-display-table'.  If they
+are omitted, they default to #x100 and #x10FFFF respectively, covering
+the entire non-ASCII range of Unicode characters.
+REPL is the replacement character to use.  If it's omitted, it defaults
+to #xFFFD, the Unicode replacement character, usually displayed as a
+black diamond with a question mark inside.
+The produced code sets up `standard-display-table' to show REPL with
+the `homoglyph' face, making the replacements stand out on display.
+
+This command is most useful with text-mode terminals, such as the
+Linux console, for which Emacs has a reliable way of determining
+which characters can be displayed and which cannot.
+
+(fn &optional REPL FROM TO)" t)
 (register-definition-prefixes "disp-table" '("display-table-print-array"))
 
 
@@ -7829,7 +7809,7 @@ Document types are symbols like `dvi', `ps', `pdf', 
`epub',
 Major mode in DocView buffers.
 
 DocView Mode is an Emacs document viewer.  It displays PDF, PS
-and DVI files (as PNG images) in Emacs buffers.
+and DVI files (as PNG or SVG images) in Emacs buffers.
 
 You can use \\<doc-view-mode-map>\\[doc-view-toggle-display] to
 toggle between displaying the document or editing it as text.
@@ -8064,6 +8044,7 @@ Valid keywords and arguments are:
              `nodigits' to suppress digits as prefix arguments.
 
 (fn BS &optional NAME M ARGS)")
+(make-obsolete 'easy-mmode-define-keymap 'define-keymap "29.1")
 (autoload 'easy-mmode-defmap "easy-mmode" "\
 Define a constant M whose value is the result of `easy-mmode-define-keymap'.
 The M, BS, and ARGS arguments are as per that function.  DOC is
@@ -8074,6 +8055,7 @@ This macro is deprecated; use `defvar-keymap' instead.
 (fn M BS DOC &rest ARGS)" nil t)
 (function-put 'easy-mmode-defmap 'doc-string-elt 3)
 (function-put 'easy-mmode-defmap 'lisp-indent-function 1)
+(make-obsolete 'easy-mmode-defmap 'defvar-keymap "29.1")
 (autoload 'easy-mmode-defsyntax "easy-mmode" "\
 Define variable ST as a syntax-table.
 CSS contains a list of syntax specifications of the form (CHAR . SYNTAX).
@@ -8389,7 +8371,7 @@ A second call of this function without changing point 
inserts the next match.
 A call with prefix PREFIX reads the symbol to insert from the minibuffer with
 completion.
 
-(fn PREFIX)" '("P"))
+(fn PREFIX)" t)
 (autoload 'ebrowse-tags-loop-continue "ebrowse" "\
 Repeat last operation on files in tree.
 FIRST-TIME non-nil means this is not a repetition, but the first time.
@@ -9172,8 +9154,7 @@ the mode if ARG is nil, omitted, or is a positive number.
 Disable the mode if ARG is a negative number.
 
 To check whether the minor mode is enabled in the current buffer,
-evaluate `(buffer-local-value \\='electric-pair-mode
-(current-buffer))'.
+evaluate `electric-pair-mode'.
 
 The mode's hook is called both when the mode is enabled and when
 it is disabled.
@@ -9444,6 +9425,8 @@ Already submitted bugs can be found in the Emacs bug 
tracker:
 
 (fn TOPIC &optional UNUSED)" t)
 (set-advertised-calling-convention 'report-emacs-bug '(topic) '"24.5")
+(autoload 'emacs-build-description "emacsbug" "\
+Insert a description of the current Emacs build in the current buffer." t)
 (autoload 'submit-emacs-patch "emacsbug" "\
 Send an Emacs patch to the Emacs maintainers.
 Interactively, you will be prompted for SUBJECT and a patch FILE
@@ -9451,7 +9434,7 @@ name (which will be attached to the mail).  You will end 
up in a
 Message buffer where you can explain more about the patch.
 
 (fn SUBJECT FILE)" t)
-(register-definition-prefixes "emacsbug" '("emacs-bug--system-description" 
"report-emacs-bug-"))
+(register-definition-prefixes "emacsbug" '("report-emacs-bug-"))
 
 
 ;;; Generated autoloads from vc/emerge.el
@@ -9937,7 +9920,7 @@ When present, ID should be an opaque object used to 
identify the
 connection unequivocally.  This is rarely needed and not available
 interactively.
 
-(fn &key (SERVER (erc-compute-server)) (PORT (erc-compute-port)) (NICK 
(erc-compute-nick)) (USER (erc-compute-user)) PASSWORD (FULL-NAME 
(erc-compute-full-name)) ID)" '((erc-select-read-args)))
+(fn &key (SERVER (erc-compute-server)) (PORT (erc-compute-port)) (NICK 
(erc-compute-nick)) (USER (erc-compute-user)) PASSWORD (FULL-NAME 
(erc-compute-full-name)) ID)" t)
 (defalias 'erc-select #'erc)
 (autoload 'erc-tls "erc" "\
 ERC is a powerful, modular, and extensible IRC client.
@@ -9984,7 +9967,7 @@ symbol composed of letters from the Latin alphabet.)  
This option is
 generally unneeded, however.  See info node `(erc) Connecting' for use
 cases.  Not available interactively.
 
-(fn &key (SERVER (erc-compute-server)) (PORT (erc-compute-port)) (NICK 
(erc-compute-nick)) (USER (erc-compute-user)) PASSWORD (FULL-NAME 
(erc-compute-full-name)) CLIENT-CERTIFICATE ID)" '((let ((erc-default-port 
erc-default-port-tls)) (erc-select-read-args))))
+(fn &key (SERVER (erc-compute-server)) (PORT (erc-compute-port)) (NICK 
(erc-compute-nick)) (USER (erc-compute-user)) PASSWORD (FULL-NAME 
(erc-compute-full-name)) CLIENT-CERTIFICATE ID)" t)
 (autoload 'erc-handle-irc-url "erc" "\
 Use ERC to IRC on HOST:PORT in CHANNEL as USER with PASSWORD.
 If ERC is already connected to HOST:PORT, simply /join CHANNEL.
@@ -10200,7 +10183,9 @@ it has to be wrapped in `(eval (quote ...))'.
 If NAME is already defined as a test and Emacs is running
 in batch mode, an error is signalled.
 
-(fn NAME () [DOCSTRING] [:expected-result RESULT-TYPE] [:tags \\='(TAG...)] 
BODY...)" nil 'macro)
+(fn NAME () [DOCSTRING] [:expected-result RESULT-TYPE] [:tags \\='(TAG...)] 
BODY...)" nil t)
+(function-put 'ert-deftest 'doc-string-elt 3)
+(function-put 'ert-deftest 'lisp-indent-function 2)
 (autoload 'ert-run-tests-batch "ert" "\
 Run the tests specified by SELECTOR, printing results to the terminal.
 
@@ -12106,12 +12091,17 @@ Define some key bindings for the `find-function' 
family of functions.")
 ;;; Generated autoloads from find-lisp.el
 
 (autoload 'find-lisp-find-dired "find-lisp" "\
-Find files in DIR, matching REGEXP.
+Find the files within DIR whose names match REGEXP.
+A Dired buffer with the results will be opened.
 
 (fn DIR REGEXP)" t)
 (autoload 'find-lisp-find-dired-subdirectories "find-lisp" "\
 Find all subdirectories of DIR.
 
+(fn DIR)" t)
+(autoload 'find-lisp-find-dired-subdirs-other-window "find-lisp" "\
+Same as `find-lisp-find-dired-subdirectories', but use another window.
+
 (fn DIR)" t)
 (autoload 'find-lisp-find-dired-filter "find-lisp" "\
 Change the filter on a `find-lisp-find-dired' buffer to REGEXP.
@@ -12271,6 +12261,8 @@ evaluate `flymake-mode'.
 The mode's hook is called both when the mode is enabled and when
 it is disabled.
 
+\\{flymake-mode-map}
+
 (fn &optional ARG)" t)
 (autoload 'flymake-mode-on "flymake" "\
 Turn Flymake mode on.")
@@ -12568,6 +12560,18 @@ value associated with ?b in SPECIFICATION, either 
padding it with
 leading zeros or truncating leading characters until it's ten
 characters wide\".
 
+the substitution for a specification character can also be a
+function, taking no arguments and returning a string to be used
+for the replacement.  It will only be called if FORMAT uses that
+character.  For example:
+
+  (format-spec \"%n\"
+               \\=`((?n . ,(lambda ()
+                          (read-number \"Number: \")))))
+
+Note that it is best to make sure the function is not quoted,
+like above, so that it is compiled by the byte-compiler.
+
 Any text properties of FORMAT are copied to the result, with any
 text properties of a %-spec itself copied to its substitution.
 
@@ -14186,7 +14190,9 @@ include it when specifying `grep-command'.
 
 In interactive usage, the actual value of this variable is set up
 by `grep-compute-defaults'; to change the default value, use
-\\[customize] or call the function `grep-apply-setting'.")
+\\[customize] or call the function `grep-apply-setting'.
+
+Also see `grep-command-position'.")
 (custom-autoload 'grep-command "grep" nil)
 (defvar grep-find-command nil "\
 The default find command for \\[grep-find].
@@ -15394,7 +15400,9 @@ it is disabled.
 (defvar hs-special-modes-alist (mapcar 'purecopy '((c-mode "{" "}" "/[*/]" nil 
nil) (c++-mode "{" "}" "/[*/]" nil nil) (bibtex-mode ("@\\S(*\\(\\s(\\)" 1)) 
(java-mode "{" "}" "/[*/]" nil nil) (js-mode "{" "}" "/[*/]" nil) (mhtml-mode 
"{\\|<[^/>]*?" "}\\|</[^/>]*[^/]>" "<!--" mhtml-forward nil))) "\
 Alist for initializing the hideshow variables for different modes.
 Each element has the form
-  (MODE START END COMMENT-START FORWARD-SEXP-FUNC ADJUST-BEG-FUNC).
+  (MODE START END COMMENT-START FORWARD-SEXP-FUNC ADJUST-BEG-FUNC
+   FIND-BLOCK-BEGINNING-FUNC FIND-NEXT-BLOCK-FUNC
+   LOOKING-AT-BLOCK-START-P-FUNC).
 
 If non-nil, hideshow will use these values as regexps to define blocks
 and comments, respectively for major mode MODE.
@@ -15415,6 +15423,15 @@ cases, FORWARD-SEXP-FUNC specifies another function to 
use instead.
 See the documentation for `hs-adjust-block-beginning' to see what is the
 use of ADJUST-BEG-FUNC.
 
+See the documentation for `hs-find-block-beginning-func' to see
+what is the use of FIND-BLOCK-BEGINNING-FUNC.
+
+See the documentation for `hs-find-next-block-func' to see what
+is the use of FIND-NEXT-BLOCK-FUNC.
+
+See the documentation for `hs-looking-at-block-start-p-func' to
+see what is the use of LOOKING-AT-BLOCK-START-P-FUNC.
+
 If any of the elements is left nil or omitted, hideshow tries to guess
 appropriate values.  The regexps should not contain leading or trailing
 whitespace.  Case does not matter.")
@@ -15871,7 +15888,8 @@ inlined into the compiled format versions.  This means 
that if you
 change its definition, you should explicitly call
 `ibuffer-recompile-formats'.
 
-(fn SYMBOL (&key NAME INLINE PROPS SUMMARIZER) &rest BODY)" nil 'macro)
+(fn SYMBOL (&key NAME INLINE PROPS SUMMARIZER) &rest BODY)" nil t)
+(function-put 'define-ibuffer-column 'lisp-indent-function 'defun)
 (autoload 'define-ibuffer-sorter "ibuf-macs" "\
 Define a method of sorting named NAME.
 DOCUMENTATION is the documentation of the function, which will be called
@@ -15882,7 +15900,9 @@ For sorting, the forms in BODY will be evaluated with 
`a' bound to one
 buffer object, and `b' bound to another.  BODY should return a non-nil
 value if and only if `a' is \"less than\" `b'.
 
-(fn NAME DOCUMENTATION (&key DESCRIPTION) &rest BODY)" nil 'macro)
+(fn NAME DOCUMENTATION (&key DESCRIPTION) &rest BODY)" nil t)
+(function-put 'define-ibuffer-sorter 'lisp-indent-function 1)
+(function-put 'define-ibuffer-sorter 'doc-string-elt 2)
 (autoload 'define-ibuffer-op "ibuf-macs" "\
 Generate a function which operates on a buffer.
 OP becomes the name of the function; if it doesn't begin with
@@ -15921,7 +15941,9 @@ BODY define the operation; they are forms to evaluate 
per each
 marked buffer.  BODY is evaluated with `buf' bound to the
 buffer object.
 
-(fn OP ARGS DOCUMENTATION (&key INTERACTIVE MARK MODIFIER-P DANGEROUS OPSTRING 
ACTIVE-OPSTRING BEFORE AFTER COMPLEX) &rest BODY)" nil 'macro)
+(fn OP ARGS DOCUMENTATION (&key INTERACTIVE MARK MODIFIER-P DANGEROUS OPSTRING 
ACTIVE-OPSTRING BEFORE AFTER COMPLEX) &rest BODY)" nil t)
+(function-put 'define-ibuffer-op 'lisp-indent-function 2)
+(function-put 'define-ibuffer-op 'doc-string-elt 3)
 (autoload 'define-ibuffer-filter "ibuf-macs" "\
 Define a filter named NAME.
 DOCUMENTATION is the documentation of the function.
@@ -15936,7 +15958,9 @@ not a particular buffer should be displayed or not.  
The forms in BODY
 will be evaluated with BUF bound to the buffer object, and QUALIFIER
 bound to the current value of the filter.
 
-(fn NAME DOCUMENTATION (&key READER DESCRIPTION) &rest BODY)" nil 'macro)
+(fn NAME DOCUMENTATION (&key READER DESCRIPTION) &rest BODY)" nil t)
+(function-put 'define-ibuffer-filter 'lisp-indent-function 2)
+(function-put 'define-ibuffer-filter 'doc-string-elt 2)
 (register-definition-prefixes "ibuf-macs" '("ibuffer-"))
 
 
@@ -16720,8 +16744,9 @@ and that image type is present in 
`image-type-auto-detectable' with a
 non-nil value.  If that value is non-nil, but not t, then the image type
 must be available.")
 (autoload 'create-image "image" "\
-Create an image.
-FILE-OR-DATA is an image file name or image data.
+Create an image from FILE-OR-DATA.
+FILE-OR-DATA is an image file name or image data.  If it is a relative
+file name, the function will look for it along `image-load-path'.
 
 Optional TYPE is a symbol describing the image type.  If TYPE is omitted
 or nil, try to determine the image type from its first few bytes
@@ -16740,10 +16765,6 @@ Value is the image created, or nil if images of type 
TYPE are not supported.
 
 Images should not be larger than specified by `max-image-size'.
 
-Image file names that are not absolute are searched for in the
-\"images\" sub-directory of `data-directory' and
-`x-bitmap-file-path' (in that order).
-
 (fn FILE-OR-DATA &optional TYPE DATA-P &rest PROPS)")
 (autoload 'put-image "image" "\
 Put image IMAGE in front of POS in the current buffer.
@@ -16878,17 +16899,39 @@ should output the image in the current buffer, 
converted to
 (register-definition-prefixes "image-converter" '("image-convert"))
 
 
-;;; Generated autoloads from image-dired.el
+;;; Generated autoloads from image/image-crop.el
 
-(push (purecopy '(image-dired 0 4 11)) package--builtin-versions)
-(autoload 'image-dired-dired-toggle-marked-thumbs "image-dired" "\
-Toggle thumbnails in front of file names in the Dired buffer.
-If no marked file could be found, insert or hide thumbnails on the
-current line.  ARG, if non-nil, specifies the files to use instead
-of the marked files.  If ARG is an integer, use the next ARG (or
-previous -ARG, if ARG<0) files.
+(autoload 'image-cut "image-crop" "\
+Cut a rectangle from the image under point, filling it with COLOR.
+COLOR defaults to the value of `image-cut-color'.
+Interactively, with prefix argument, prompt for COLOR to use.
 
-(fn &optional ARG)" t)
+(fn &optional COLOR)" t)
+(autoload 'image-crop "image-crop" "\
+Crop the image under point.
+If CUT is non-nil, remove a rectangle from the image instead of
+cropping the image.  In that case CUT should be the name of a
+color to fill the rectangle.
+
+While cropping the image, the following key bindings are available:
+
+`q':   Exit without changing anything.
+`RET': Crop/cut the image.
+`m':   Make mouse movements move the rectangle instead of altering the
+       rectangle shape.
+`s':   Same as `m', but make the rectangle into a square first.
+
+After cropping an image, you can save it by `M-x image-save' or
+\\<image-map>\\[image-save] when point is over the image.
+
+(fn &optional CUT)" t)
+(register-definition-prefixes "image-crop" '("image-c"))
+
+
+;;; Generated autoloads from image/image-dired.el
+
+(push (purecopy '(image-dired 0 5)) package--builtin-versions)
+(put 'image-dired-thumbnail-storage 'safe-local-variable (lambda (x) (eq x 
'per-directory)))
 (autoload 'image-dired-dired-with-window-configuration "image-dired" "\
 Open directory DIR and create a default window configuration.
 
@@ -16929,11 +16972,11 @@ used or not.  If non-nil, use `display-buffer' 
instead of
 `image-dired-previous-line-and-display' where we do not want the
 thumbnail buffer to be selected.
 
-(fn &optional ARG APPEND DO-NOT-POP)" t)
+(fn &optional ARG APPEND DO-NOT-POP)" '(nil dired-mode))
 (autoload 'image-dired-show-all-from-dir "image-dired" "\
 Make a thumbnail buffer for all images in DIR and display it.
-Any file matching `image-file-name-regexp' is considered an image
-file.
+Any file matching `image-dired--file-name-regexp' is considered an
+image file.
 
 If the number of image files in DIR exceeds
 `image-dired-show-all-from-dir-max-files', ask for confirmation
@@ -16942,22 +16985,52 @@ never ask for confirmation.
 
 (fn DIR)" t)
 (defalias 'image-dired 'image-dired-show-all-from-dir)
-(autoload 'image-dired-tag-files "image-dired" "\
-Tag marked file(s) in Dired.  With prefix ARG, tag file at point.
+(autoload 'image-dired-bookmark-jump "image-dired" "\
+Default bookmark handler for Image-Dired buffers.
 
-(fn ARG)" t)
-(autoload 'image-dired-delete-tag "image-dired" "\
-Remove tag for selected file(s).
-With prefix argument ARG, remove tag from file at point.
+(fn BOOKMARK)")
+(define-obsolete-function-alias 'tumme #'image-dired "24.4")
+(define-obsolete-function-alias 'image-dired-setup-dired-keybindings 
#'image-dired-minor-mode "26.1")
+(register-definition-prefixes "image-dired" '("image-dired-"))
 
-(fn ARG)" t)
-(autoload 'image-dired-jump-thumbnail-buffer "image-dired" "\
-Jump to thumbnail buffer." t)
-(autoload 'image-dired-minor-mode "image-dired" "\
-Setup easy-to-use keybindings for the commands to be used in Dired mode.
+
+;;; Generated autoloads from image/image-dired-dired.el
 
-Note that n, p and <down> and <up> will be hijacked and bound to
-`image-dired-dired-next-line' and `image-dired-dired-previous-line'.
+(autoload 'image-dired-dired-toggle-marked-thumbs "image-dired-dired" "\
+Toggle thumbnails in front of file names in the Dired buffer.
+If no marked file could be found, insert or hide thumbnails on the
+current line.  ARG, if non-nil, specifies the files to use instead
+of the marked files.  If ARG is an integer, use the next ARG (or
+previous -ARG, if ARG<0) files.
+
+(fn &optional ARG)" '(dired-mode))
+(autoload 'image-dired-jump-thumbnail-buffer "image-dired-dired" "\
+Jump to thumbnail buffer." '(dired-mode))
+(autoload 'image-dired-minor-mode "image-dired-dired" "\
+Setup easy-to-use keybindings for Image-Dired in Dired mode.
+
+This minor mode adds these additional bindings:
+\\<image-dired-minor-mode-map>
+  \\[image-dired-next-line-and-display]                Move to next line and 
display thumbnail image.
+  \\[image-dired-previous-line-and-display]            Move to previous line 
and display thumbnail image.
+  \\[image-dired-mark-and-display-next]                Mark current file and 
display next thumbnail image.
+  \\[image-dired-jump-thumbnail-buffer]                Jump to thumbnail 
buffer.
+
+For reference, these are the default Image-Dired bindings that
+are always available in Dired:
+\\<dired-mode-map>
+  \\[image-dired-display-thumbs]               Display thumbnails of all 
marked files.
+  \\[image-dired-tag-files]            Tag marked file(s).
+  \\[image-dired-delete-tag]           Remove tag for selected file(s).
+  \\[image-dired-jump-thumbnail-buffer]                Jump to thumbnail 
buffer.
+  \\[image-dired-dired-display-image]          Display current image file.
+  \\[image-dired-dired-display-external]               Display file at point 
using an external viewer.
+  \\[image-dired-display-thumbs-append]                Append thumbnails to 
thumbnail buffer.
+  \\[image-dired-display-thumb]                Display thumbnails of all 
marked files.
+  \\[image-dired-dired-comment-files]          Add comment to current or 
marked files in Dired.
+  \\[image-dired-mark-tagged-files]            Use REGEXP to mark files with 
matching tag.
+  \\[image-dired-dired-toggle-marked-thumbs]   Toggle thumbnails in front of 
file names.
+  \\[image-dired-dired-edit-comment-and-tags]          Edit comment and tags 
of marked images.
 
 This is a minor mode.  If called interactively, toggle the
 `Image-Dired minor mode' mode.  If the prefix argument is
@@ -16975,21 +17048,19 @@ The mode's hook is called both when the mode is 
enabled and when
 it is disabled.
 
 (fn &optional ARG)" t)
-(autoload 'image-dired-display-thumbs-append "image-dired" "\
-Append thumbnails to `image-dired-thumbnail-buffer'." t)
-(autoload 'image-dired-display-thumb "image-dired" "\
-Shorthand for `image-dired-display-thumbs' with prefix argument." t)
-(autoload 'image-dired-dired-display-external "image-dired" "\
-Display file at point using an external viewer." t)
-(autoload 'image-dired-dired-display-image "image-dired" "\
+(autoload 'image-dired-display-thumbs-append "image-dired-dired" "\
+Append thumbnails to `image-dired-thumbnail-buffer'." '(dired-mode))
+(autoload 'image-dired-display-thumb "image-dired-dired" "\
+Shorthand for `image-dired-display-thumbs' with prefix argument." 
'(dired-mode))
+(autoload 'image-dired-dired-display-external "image-dired-dired" "\
+Display file at point using an external viewer." '(dired-mode))
+(autoload 'image-dired-dired-display-image "image-dired-dired" "\
 Display current image file.
 See documentation for `image-dired-display-image' for more information.
-With prefix argument ARG, display image in its original size.
 
-(fn &optional ARG)" t)
-(autoload 'image-dired-dired-comment-files "image-dired" "\
-Add comment to current or marked files in Dired." t)
-(autoload 'image-dired-mark-tagged-files "image-dired" "\
+(fn &optional _)" '(dired-mode))
+(set-advertised-calling-convention 'image-dired-dired-display-image 'nil 
'"29.1")
+(autoload 'image-dired-mark-tagged-files "image-dired-dired" "\
 Use REGEXP to mark files with matching tag.
 A `tag' is a keyword, a piece of meta data, associated with an
 image file and stored in image-dired's database file.  This command
@@ -16997,18 +17068,38 @@ lets you input a regexp and this will be matched 
against all tags
 on all image files in the database file.  The files that have a
 matching tag will be marked in the Dired buffer.
 
-(fn REGEXP)" t)
-(autoload 'image-dired-dired-edit-comment-and-tags "image-dired" "\
+(fn REGEXP)" '(dired-mode))
+(register-definition-prefixes "image-dired-dired" '("image-dired-"))
+
+
+;;; Generated autoloads from image/image-dired-external.el
+
+(register-definition-prefixes "image-dired-external" '("image-dired-"))
+
+
+;;; Generated autoloads from image/image-dired-tags.el
+
+(autoload 'image-dired-tag-files "image-dired-tags" "\
+Tag marked file(s) in Dired.  With prefix ARG, tag file at point.
+
+(fn ARG)" '(dired-mode))
+(autoload 'image-dired-delete-tag "image-dired-tags" "\
+Remove tag for selected file(s).
+With prefix argument ARG, remove tag from file at point.
+
+(fn ARG)" '(dired-mode))
+(autoload 'image-dired-dired-comment-files "image-dired-tags" "\
+Add comment to current or marked files in Dired." '(dired-mode))
+(autoload 'image-dired-dired-edit-comment-and-tags "image-dired-tags" "\
 Edit comment and tags of current or marked image files.
 Edit comment and tags for all marked image files in an
-easy-to-use form." t)
-(autoload 'image-dired-bookmark-jump "image-dired" "\
-Default bookmark handler for Image-Dired buffers.
+easy-to-use form." '(dired-mode))
+(register-definition-prefixes "image-dired-tags" '("image-dired-"))
 
-(fn BOOKMARK)")
-(define-obsolete-function-alias 'tumme #'image-dired "24.4")
-(define-obsolete-function-alias 'image-dired-setup-dired-keybindings 
#'image-dired-minor-mode "26.1")
-(register-definition-prefixes "image-dired" '("image-dired-"))
+
+;;; Generated autoloads from image/image-dired-util.el
+
+(register-definition-prefixes "image-dired-util" '("image-dired-"))
 
 
 ;;; Generated autoloads from image-file.el
@@ -17186,6 +17277,11 @@ an index alist of the current buffer.  The function is
 called within a `save-excursion'.
 
 See `imenu--index-alist' for the format of the buffer index alist.")
+(defvar-local imenu-submenus-on-top t "\
+Flag specifying whether items with sublists should be kept at top.
+
+For some indexes, such as those describing sections in a document, it
+makes sense to keep their original order even in the menubar.")
 (defvar-local imenu-prev-index-position-function 'beginning-of-defun "\
 Function for finding the next index position.
 
@@ -18450,7 +18546,7 @@ This option also treats some characters in the 
`mule-unicode-...'
 charsets if you don't have a Unicode font with which to display them.
 
 Setting this variable directly does not take effect;
-use either \\[customize] or the function `latin1-display'.")
+use either \\[customize] or the command `latin1-display'.")
 (custom-autoload 'latin1-display "latin1-disp" nil)
 (autoload 'latin1-display "latin1-disp" "\
 Set up Latin-1/ASCII display for the arguments character SETS.
@@ -18466,8 +18562,17 @@ This uses the transliterations of the Lynx browser.  
The display isn't
 changed if the display can render Unicode characters.
 
 Setting this variable directly does not take effect;
-use either \\[customize] or the function `latin1-display'.")
+use either \\[customize] or the command `latin1-display-ucs-per-lynx'.")
 (custom-autoload 'latin1-display-ucs-per-lynx "latin1-disp" nil)
+(autoload 'latin1-display-ucs-per-lynx "latin1-disp" "\
+Set up Latin-1/ASCII display for Unicode characters.
+This uses the transliterations of the Lynx browser.
+
+With argument ARG, turn such display on if ARG is positive, otherwise
+turn it off and display Unicode characters literally.  The display
+isn't changed if the display can render Unicode characters.
+
+(fn ARG)" t)
 (register-definition-prefixes "latin1-disp" '("latin1-display-"))
 
 
@@ -18569,58 +18674,6 @@ sleep in seconds.
 (register-definition-prefixes "life" '("life-"))
 
 
-;;; Generated autoloads from linum.el
-
-(autoload 'linum-mode "linum" "\
-Toggle display of line numbers in the left margin (Linum mode).
-
-This mode has been largely replaced by `display-line-numbers-mode'
-(which is much faster and has fewer interaction problems with other
-modes).
-
-Linum mode is a buffer-local minor mode.
-
-This is a minor mode.  If called interactively, toggle the `Linum
-mode' mode.  If the prefix argument is positive, enable the mode,
-and if it is zero or negative, disable the mode.
-
-If called from Lisp, toggle the mode if ARG is `toggle'.  Enable
-the mode if ARG is nil, omitted, or is a positive number.
-Disable the mode if ARG is a negative number.
-
-To check whether the minor mode is enabled in the current buffer,
-evaluate `linum-mode'.
-
-The mode's hook is called both when the mode is enabled and when
-it is disabled.
-
-(fn &optional ARG)" t)
-(put 'global-linum-mode 'globalized-minor-mode t)
-(defvar global-linum-mode nil "\
-Non-nil if Global Linum mode is enabled.
-See the `global-linum-mode' command
-for a description of this minor mode.
-Setting this variable directly does not take effect;
-either customize it (see the info node `Easy Customization')
-or call the function `global-linum-mode'.")
-(custom-autoload 'global-linum-mode "linum" nil)
-(autoload 'global-linum-mode "linum" "\
-Toggle Linum mode in all buffers.
-With prefix ARG, enable Global Linum mode if ARG is positive;
-otherwise, disable it.
-
-If called from Lisp, toggle the mode if ARG is `toggle'.
-Enable the mode if ARG is nil, omitted, or is a positive number.
-Disable the mode if ARG is a negative number.
-
-Linum mode is enabled in all buffers where `linum-on' would do it.
-
-See `linum-mode' for more information on Linum mode.
-
-(fn &optional ARG)" t)
-(register-definition-prefixes "linum" '("linum-"))
-
-
 ;;; Generated autoloads from cedet/ede/linux.el
 
 (register-definition-prefixes "ede/linux" '("ede-linux-" "project-linux-"))
@@ -18641,7 +18694,7 @@ See `linum-mode' for more information on Linum mode.
 (put 'generated-autoload-file 'safe-local-variable 'stringp)
 (put 'generated-autoload-load-name 'safe-local-variable 'stringp)
 (autoload 'loaddefs-generate "loaddefs-gen" "\
-Generate loaddefs files for Lisp files in the directories DIRS.
+Generate loaddefs files for Lisp files in one or more directories given by DIR.
 DIR can be either a single directory or a list of directories.
 
 The autoloads will be written to OUTPUT-FILE.  If any Lisp file
@@ -18649,7 +18702,7 @@ binds `generated-autoload-file' as a file-local 
variable, write
 its autoloads into the specified file instead.
 
 The function does NOT recursively descend into subdirectories of the
-directory or directories specified by DIRS.
+directories specified by DIR.
 
 Optional argument EXCLUDED-FILES, if non-nil, should be a list of
 files, such as preloaded files, whose autoloads should not be written
@@ -20869,6 +20922,11 @@ it is disabled.
 (register-definition-prefixes "mwheel" '("mouse-wheel-" "mwheel-"))
 
 
+;;; Generated autoloads from emacs-lisp/nadvice.el
+
+(push (purecopy '(nadvice 1 0)) package--builtin-versions)
+
+
 ;;; Generated autoloads from net/net-utils.el
 
 (autoload 'ifconfig "net-utils" "\
@@ -21861,7 +21919,7 @@ Coloring:
 
 ;;; Generated autoloads from org/org.el
 
-(push (purecopy '(org 9 5 4)) package--builtin-versions)
+(push (purecopy '(org 9 5 5)) package--builtin-versions)
 (autoload 'org-babel-do-load-languages "org" "\
 Load the languages defined in `org-babel-load-languages'.
 
@@ -23167,6 +23225,13 @@ Completion rules for the `cvs' command.")
 (register-definition-prefixes "pcmpl-cvs" '("pcmpl-cvs-"))
 
 
+;;; Generated autoloads from pcmpl-git.el
+
+(autoload 'pcomplete/git "pcmpl-git" "\
+Completion for the `git' command.")
+(register-definition-prefixes "pcmpl-git" '("pcmpl-git--"))
+
+
 ;;; Generated autoloads from pcmpl-gnu.el
 
 (autoload 'pcomplete/gzip "pcmpl-gnu" "\
@@ -23179,7 +23244,16 @@ Completion for GNU `make'.")
 Completion for the GNU tar utility.")
 (autoload 'pcomplete/find "pcmpl-gnu" "\
 Completion for the GNU find utility.")
-(defalias 'pcomplete/gdb 'pcomplete/xargs)
+(autoload 'pcomplete/awk "pcmpl-gnu" "\
+Completion for the `awk' command.")
+(autoload 'pcomplete/gpg "pcmpl-gnu" "\
+Completion for the `gpg` command.")
+(autoload 'pcomplete/gdb "pcmpl-gnu" "\
+Completion for the `gdb' command.")
+(autoload 'pcomplete/emacs "pcmpl-gnu" "\
+Completion for the `emacs' command.")
+(autoload 'pcomplete/emacsclient "pcmpl-gnu" "\
+Completion for the `emacsclient' command.")
 (register-definition-prefixes "pcmpl-gnu" '("pcmpl-gnu-" "pcomplete/find"))
 
 
@@ -23191,6 +23265,10 @@ Completion for GNU/Linux `kill', using /proc 
filesystem.")
 Completion for GNU/Linux `umount'.")
 (autoload 'pcomplete/mount "pcmpl-linux" "\
 Completion for GNU/Linux `mount'.")
+(autoload 'pcomplete/systemctl "pcmpl-linux" "\
+Completion for the `systemctl' command.")
+(autoload 'pcomplete/journalctl "pcmpl-linux" "\
+Completion for the `journalctl' command.")
 (register-definition-prefixes "pcmpl-linux" '("pcmpl-linux-" 
"pcomplete-pare-list"))
 
 
@@ -23198,6 +23276,8 @@ Completion for GNU/Linux `mount'.")
 
 (autoload 'pcomplete/rpm "pcmpl-rpm" "\
 Completion for the `rpm' command.")
+(autoload 'pcomplete/dnf "pcmpl-rpm" "\
+Completion for the `dnf' command.")
 (register-definition-prefixes "pcmpl-rpm" '("pcmpl-rpm-"))
 
 
@@ -23209,16 +23289,174 @@ Completion for `cd'.")
 (autoload 'pcomplete/rmdir "pcmpl-unix" "\
 Completion for `rmdir'.")
 (autoload 'pcomplete/rm "pcmpl-unix" "\
-Completion for `rm'.")
+Completion for the `rm' command.")
 (autoload 'pcomplete/xargs "pcmpl-unix" "\
 Completion for `xargs'.")
-(defalias 'pcomplete/time 'pcomplete/xargs)
+(autoload 'pcomplete/time "pcmpl-unix" "\
+Completion for the `time' command.")
 (autoload 'pcomplete/which "pcmpl-unix" "\
 Completion for `which'.")
+(autoload 'pcomplete/cat "pcmpl-unix" "\
+Completion for the `cat' command.")
+(autoload 'pcomplete/tac "pcmpl-unix" "\
+Completion for the `tac' command.")
+(autoload 'pcomplete/nl "pcmpl-unix" "\
+Completion for the `nl' command.")
+(autoload 'pcomplete/od "pcmpl-unix" "\
+Completion for the `od' command.")
+(autoload 'pcomplete/base32 "pcmpl-unix" "\
+Completion for the `base32' and `base64' commands.")
+(defalias 'pcomplete/base64 'pcomplete/base32)
+(autoload 'pcomplete/basenc "pcmpl-unix" "\
+Completion for the `basenc' command.")
+(autoload 'pcomplete/fmt "pcmpl-unix" "\
+Completion for the `fmt' command.")
+(autoload 'pcomplete/pr "pcmpl-unix" "\
+Completion for the `pr' command.")
+(autoload 'pcomplete/fold "pcmpl-unix" "\
+Completion for the `fold' command.")
+(autoload 'pcomplete/head "pcmpl-unix" "\
+Completion for the `head' command.")
+(autoload 'pcomplete/tail "pcmpl-unix" "\
+Completion for the `tail' command.")
+(autoload 'pcomplete/split "pcmpl-unix" "\
+Completion for the `split' command.")
+(autoload 'pcomplete/csplit "pcmpl-unix" "\
+Completion for the `csplit' command.")
+(autoload 'pcomplete/wc "pcmpl-unix" "\
+Completion for the `wc' command.")
+(autoload 'pcomplete/sum "pcmpl-unix" "\
+Completion for the `sum' command.")
+(autoload 'pcomplete/cksum "pcmpl-unix" "\
+Completion for the `cksum' command.")
+(autoload 'pcomplete/b2sum "pcmpl-unix" "\
+Completion for the `b2sum' command.")
+(autoload 'pcomplete/md5sum "pcmpl-unix" "\
+Completion for checksum commands.")
+(defalias 'pcomplete/sha1sum 'pcomplete/md5sum)
+(defalias 'pcomplete/sha224sum 'pcomplete/md5sum)
+(defalias 'pcomplete/sha256sum 'pcomplete/md5sum)
+(defalias 'pcomplete/sha384sum 'pcomplete/md5sum)
+(defalias 'pcomplete/sha521sum 'pcomplete/md5sum)
+(autoload 'pcomplete/sort "pcmpl-unix" "\
+Completion for the `sort' command.")
+(autoload 'pcomplete/shuf "pcmpl-unix" "\
+Completion for the `shuf' command.")
+(autoload 'pcomplete/uniq "pcmpl-unix" "\
+Completion for the `uniq' command.")
+(autoload 'pcomplete/comm "pcmpl-unix" "\
+Completion for the `comm' command.")
+(autoload 'pcomplete/ptx "pcmpl-unix" "\
+Completion for the `ptx' command.")
+(autoload 'pcomplete/tsort "pcmpl-unix" "\
+Completion for the `tsort' command.")
+(autoload 'pcomplete/cut "pcmpl-unix" "\
+Completion for the `cut' command.")
+(autoload 'pcomplete/paste "pcmpl-unix" "\
+Completion for the `paste' command.")
+(autoload 'pcomplete/join "pcmpl-unix" "\
+Completion for the `join' command.")
+(autoload 'pcomplete/tr "pcmpl-unix" "\
+Completion for the `tr' command.")
+(autoload 'pcomplete/expand "pcmpl-unix" "\
+Completion for the `expand' command.")
+(autoload 'pcomplete/unexpand "pcmpl-unix" "\
+Completion for the `unexpand' command.")
+(autoload 'pcomplete/ls "pcmpl-unix" "\
+Completion for the `ls' command.")
+(defalias 'pcomplete/dir 'pcomplete/ls)
+(defalias 'pcomplete/vdir 'pcomplete/ls)
+(autoload 'pcomplete/cp "pcmpl-unix" "\
+Completion for the `cp' command.")
+(autoload 'pcomplete/dd "pcmpl-unix" "\
+Completion for the `dd' command.")
+(autoload 'pcomplete/install "pcmpl-unix" "\
+Completion for the `install' command.")
+(autoload 'pcomplete/mv "pcmpl-unix" "\
+Completion for the `mv' command.")
+(autoload 'pcomplete/shred "pcmpl-unix" "\
+Completion for the `shred' command.")
+(autoload 'pcomplete/ln "pcmpl-unix" "\
+Completion for the `ln' command.")
+(autoload 'pcomplete/mkdir "pcmpl-unix" "\
+Completion for the `mkdir' command.")
+(autoload 'pcomplete/mkfifo "pcmpl-unix" "\
+Completion for the `mkfifo' command.")
+(autoload 'pcomplete/mknod "pcmpl-unix" "\
+Completion for the `mknod' command.")
+(autoload 'pcomplete/readlink "pcmpl-unix" "\
+Completion for the `readlink' command.")
 (autoload 'pcomplete/chown "pcmpl-unix" "\
 Completion for the `chown' command.")
 (autoload 'pcomplete/chgrp "pcmpl-unix" "\
 Completion for the `chgrp' command.")
+(autoload 'pcomplete/chmod "pcmpl-unix" "\
+Completion for the `chmod' command.")
+(autoload 'pcomplete/touch "pcmpl-unix" "\
+Completion for the `touch' command.")
+(autoload 'pcomplete/df "pcmpl-unix" "\
+Completion for the `df' command.")
+(autoload 'pcomplete/du "pcmpl-unix" "\
+Completion for the `du' command.")
+(autoload 'pcomplete/stat "pcmpl-unix" "\
+Completion for the `stat' command.")
+(autoload 'pcomplete/sync "pcmpl-unix" "\
+Completion for the `sync' command.")
+(autoload 'pcomplete/truncate "pcmpl-unix" "\
+Completion for the `truncate' command.")
+(autoload 'pcomplete/echo "pcmpl-unix" "\
+Completion for the `echo' command.")
+(autoload 'pcomplete/test "pcmpl-unix" "\
+Completion for the `test' command.")
+(defalias (intern "pcomplete/[") 'pcomplete/test)
+(autoload 'pcomplete/tee "pcmpl-unix" "\
+Completion for the `tee' command.")
+(autoload 'pcomplete/basename "pcmpl-unix" "\
+Completion for the `basename' command.")
+(autoload 'pcomplete/dirname "pcmpl-unix" "\
+Completion for the `dirname' command.")
+(autoload 'pcomplete/pathchk "pcmpl-unix" "\
+Completion for the `pathchk' command.")
+(autoload 'pcomplete/mktemp "pcmpl-unix" "\
+Completion for the `mktemp' command.")
+(autoload 'pcomplete/realpath "pcmpl-unix" "\
+Completion for the `realpath' command.")
+(autoload 'pcomplete/id "pcmpl-unix" "\
+Completion for the `id' command.")
+(autoload 'pcomplete/groups "pcmpl-unix" "\
+Completion for the `groups' command.")
+(autoload 'pcomplete/who "pcmpl-unix" "\
+Completion for the `who' command.")
+(autoload 'pcomplete/date "pcmpl-unix" "\
+Completion for the `date' command.")
+(autoload 'pcomplete/nproc "pcmpl-unix" "\
+Completion for the `nproc' command.")
+(autoload 'pcomplete/uname "pcmpl-unix" "\
+Completion for the `uname' command.")
+(autoload 'pcomplete/hostname "pcmpl-unix" "\
+Completion for the `hostname' command.")
+(autoload 'pcomplete/uptime "pcmpl-unix" "\
+Completion for the `uptime' command.")
+(autoload 'pcomplete/chcon "pcmpl-unix" "\
+Completion for the `chcon' command.")
+(autoload 'pcomplete/runcon "pcmpl-unix" "\
+Completion for the `runcon' command.")
+(autoload 'pcomplete/chroot "pcmpl-unix" "\
+Completion for the `chroot' command.")
+(autoload 'pcomplete/env "pcmpl-unix" "\
+Completion for the `env' command.")
+(autoload 'pcomplete/nice "pcmpl-unix" "\
+Completion for the `nice' command.")
+(autoload 'pcomplete/nohup "pcmpl-unix" "\
+Completion for the `nohup' command.")
+(autoload 'pcomplete/stdbuf "pcmpl-unix" "\
+Completion for the `stdbuf' command.")
+(autoload 'pcomplete/timeout "pcmpl-unix" "\
+Completion for the `timeout' command.")
+(autoload 'pcomplete/numfmt "pcmpl-unix" "\
+Completion for the `numfmt' command.")
+(autoload 'pcomplete/seq "pcmpl-unix" "\
+Completion for the `seq' command.")
 (autoload 'pcomplete/ssh "pcmpl-unix" "\
 Completion rules for the `ssh' command.")
 (defalias 'pcomplete/rsh #'pcomplete/ssh)
@@ -23226,13 +23464,25 @@ Completion rules for the `ssh' command.")
 Completion rules for the `scp' command.
 Includes files as well as host names followed by a colon.")
 (autoload 'pcomplete/telnet "pcmpl-unix")
+(autoload 'pcomplete/sudo "pcmpl-unix" "\
+Completion for the `sudo' command.")
 (register-definition-prefixes "pcmpl-unix" '("pcmpl-" "pcomplete/"))
 
 
 ;;; Generated autoloads from pcmpl-x.el
 
+(autoload 'pcomplete/tex "pcmpl-x" "\
+Completion for the `tex' command.")
+(defalias 'pcomplete/pdftex 'pcomplete/tex)
+(defalias 'pcomplete/latex 'pcomplete/tex)
+(defalias 'pcomplete/pdflatex 'pcomplete/tex)
+(autoload 'pcomplete/luatex "pcmpl-x" "\
+Completion for the `luatex' command.")
+(defalias 'pcomplete/lualatex 'pcomplete/luatex)
 (autoload 'pcomplete/tlmgr "pcmpl-x" "\
 Completion for the `tlmgr' command.")
+(autoload 'pcomplete/rg "pcmpl-x" "\
+Completion for the `rg' command.")
 (autoload 'pcomplete/ack "pcmpl-x" "\
 Completion for the `ack' command.
 Start an argument with `-' to complete short options and `--' for
@@ -23243,6 +23493,8 @@ Completion for the `ag' command.")
 (autoload 'pcomplete/bcc32 "pcmpl-x" "\
 Completion function for Borland's C++ compiler.")
 (defalias 'pcomplete/bcc 'pcomplete/bcc32)
+(autoload 'pcomplete/rclone "pcmpl-x" "\
+Completion for the `rclone' command.")
 (register-definition-prefixes "pcmpl-x" '("pcmpl-x-"))
 
 
@@ -24780,8 +25032,8 @@ Run an inferior Python process.
 Argument CMD defaults to `python-shell-calculate-command' return
 value.  When called interactively with `prefix-arg', it allows
 the user to edit such value and choose whether the interpreter
-should be DEDICATED for the current buffer.  When numeric prefix
-arg is other than 0 or 4 do not SHOW.
+should be DEDICATED to the current buffer or project.  When
+numeric prefix arg is other than 0 or 4 do not SHOW.
 
 For a given buffer and same values of DEDICATED, if a process is
 already running for it, it will do nothing.  This means that if
@@ -24793,6 +25045,37 @@ Runs the hook `inferior-python-mode-hook' after
 process buffer for a list of commands.)
 
 (fn &optional CMD DEDICATED SHOW)" t)
+(autoload 'python-add-import "python" "\
+Add an import statement to the current buffer.
+
+Interactively, ask for an import statement using all imports
+found in the current project as suggestions.  With a prefix
+argument, restrict the suggestions to imports defining the symbol
+at point.  If there is only one such suggestion, act without
+asking.
+
+When calling from Lisp, use a non-nil NAME to restrict the
+suggestions to imports defining NAME.
+
+(fn NAME)" t)
+(autoload 'python-import-symbol-at-point "python" "\
+Add an import statement for the symbol at point to the current buffer.
+This works like `python-add-import', but with the opposite
+behavior regarding the prefix argument." t)
+(autoload 'python-remove-import "python" "\
+Remove an import statement from the current buffer.
+
+Interactively, ask for an import statement to remove, displaying
+the imports of the current buffer as suggestions.  With a prefix
+argument, restrict the suggestions to imports defining the symbol
+at point.  If there is only one such suggestion, act without
+asking.
+
+(fn NAME)" t)
+(autoload 'python-sort-imports "python" "\
+Sort Python imports in the current buffer." t)
+(autoload 'python-fix-imports "python" "\
+Add missing imports and remove unused ones from the current buffer." t)
 (autoload 'python-mode "python" "\
 Major mode for editing Python files.
 
@@ -25313,6 +25596,8 @@ evaluate `rectangle-mark-mode'.
 The mode's hook is called both when the mode is enabled and when
 it is disabled.
 
+\\{rectangle-mark-mode-map}
+
 (fn &optional ARG)" t)
 (register-definition-prefixes "rect" '("apply-on-rectangle" 
"clear-rectangle-line" "delete-" "extract-rectangle-" "killed-rectangle" "ope" 
"rectangle-" "spaces-string" "string-rectangle-"))
 
@@ -25848,7 +26133,7 @@ Regexp to match Header fields that Rmail should display.
 If nil, display all header fields except those matched by
 `rmail-ignored-headers'.")
 (custom-autoload 'rmail-displayed-headers "rmail" t)
-(defvar rmail-retry-ignored-headers (purecopy 
"^x-authentication-warning:\\|^x-detected-operating-system:\\|^x-spam[-a-z]*:\\|content-type:\\|content-transfer-encoding:\\|mime-version:\\|message-id:")
 "\
+(defvar rmail-retry-ignored-headers (concat 
"^x-authentication-warning:\\|^x-detected-operating-system:\\|" 
"^x-spam[-a-z]*:\\|^arc-.*:\\|" 
"^content-type:\\|^content-transfer-encoding:\\|" 
"^mime-version:\\|^message-id:\\|^x-google-smtp-source:\\|" 
"^x-received:\\|^received-spf:\\|" 
"^authentication-results:\\|^dkim-signature:") "\
 Headers that should be stripped when retrying a failed message.")
 (custom-autoload 'rmail-retry-ignored-headers "rmail" t)
 (defvar rmail-highlighted-headers (purecopy "^From:\\|^Subject:") "\
@@ -29285,6 +29570,10 @@ Studlify-case the current buffer." t)
 
 ;;; Generated autoloads from emacs-lisp/subr-x.el
 
+(defsubst string-join (strings &optional separator) "\
+Join all STRINGS using SEPARATOR.
+Optional argument SEPARATOR must be a string, a vector, or a list of
+characters; nil stands for the empty string." (mapconcat #'identity strings 
separator))
 (autoload 'string-truncate-left "subr-x" "\
 If STRING is longer than LENGTH, return a truncated version.
 When truncating, \"...\" is always prepended to the string, so
@@ -30489,9 +30778,9 @@ such as if there are no commands in the file, the value 
of `tex-default-mode'
 says which mode to use.
 
 (fn)" t)
-(defalias 'TeX-mode #'tex-mode)
-(defalias 'plain-TeX-mode #'plain-tex-mode)
-(defalias 'LaTeX-mode #'latex-mode)
+ (defalias 'TeX-mode #'tex-mode)
+ (defalias 'plain-TeX-mode #'plain-tex-mode)
+ (defalias 'LaTeX-mode #'latex-mode)
 (autoload 'plain-tex-mode "tex-mode" "\
 Major mode for editing files of input for plain TeX.
 Makes $ and } display the characters they match.
@@ -30912,28 +31201,6 @@ Display a list of threads." t)
 (register-definition-prefixes "thread" '("thread-list-"))
 
 
-;;; Generated autoloads from thumbs.el
-
-(autoload 'thumbs-find-thumb "thumbs" "\
-Display the thumbnail for IMG.
-
-(fn IMG)" t)
-(autoload 'thumbs-show-from-dir "thumbs" "\
-Make a preview buffer for all images in DIR.
-Optional argument REG to select file matching a regexp,
-and SAME-WINDOW to show thumbs in the same window.
-
-(fn DIR &optional REG SAME-WINDOW)" t)
-(autoload 'thumbs-dired-show-marked "thumbs" "\
-In dired, make a thumbs buffer with marked files." t)
-(autoload 'thumbs-dired-show "thumbs" "\
-In dired, make a thumbs buffer with all files in current directory." t)
-(defalias 'thumbs 'thumbs-show-from-dir)
-(autoload 'thumbs-dired-setroot "thumbs" "\
-In dired, call the setroot program on the image at point." t)
-(register-definition-prefixes "thumbs" '("thumbs-"))
-
-
 ;;; Generated autoloads from emacs-lisp/thunk.el
 
 (push (purecopy '(thunk 1 0)) package--builtin-versions)
@@ -31653,7 +31920,7 @@ the output buffer or changing the window configuration.
 Whether Tramp is enabled.
 If it is set to nil, all remote file names are used literally.")
 (custom-autoload 'tramp-mode "tramp" t)
-(defconst tramp-initial-file-name-regexp "\\`/[^/:]+:[^/:]*:" "\
+(defconst tramp-initial-file-name-regexp (rx bos "/" (+ (not (any "/:"))) ":" 
(* (not (any "/:"))) ":") "\
 Value for `tramp-file-name-regexp' for autoload.
 It must match the initial `tramp-syntax' settings.")
 (defvar tramp-file-name-regexp tramp-initial-file-name-regexp "\
@@ -31664,7 +31931,7 @@ initial value is overwritten by the car of 
`tramp-file-name-structure'.")
 (defvar tramp-ignored-file-name-regexp nil "\
 Regular expression matching file names that are not under Tramp's control.")
 (custom-autoload 'tramp-ignored-file-name-regexp "tramp" t)
-(defconst tramp-autoload-file-name-regexp (concat "\\`/" (if (memq system-type 
'(cygwin windows-nt)) "\\(-\\|[^/|:]\\{2,\\}\\)" "[^/|:]+") ":") "\
+(defconst tramp-autoload-file-name-regexp (rx bos "/" (| "-" (>= 2 (not (any 
"/:|")))) ":") "\
 Regular expression matching file names handled by Tramp autoload.
 It must match the initial `tramp-syntax' settings.  It should not
 match file names at root of the underlying local file system,
@@ -31706,7 +31973,7 @@ It must be supported by libarchive(3).")
 List of suffixes which indicate a compressed file.
 It must be supported by libarchive(3).")
 (defmacro tramp-archive-autoload-file-name-regexp nil "\
-Regular expression matching archive file names." '(concat "\\`" "\\(" ".+" 
"\\." (regexp-opt tramp-archive-suffixes) "\\(?:" "\\." (regexp-opt 
tramp-archive-compression-suffixes) "\\)*" "\\)" "\\(" "/" ".*" "\\)" "\\'"))
+Regular expression matching archive file names." `(rx bos (group (+ nonl) "." 
,(cons '| tramp-archive-suffixes) (32 "." ,(cons '| 
tramp-archive-compression-suffixes))) (group "/" (* nonl)) eos))
 (autoload 'tramp-archive-file-name-handler "tramp-archive")
 (defun tramp-archive-autoload-file-name-handler (operation &rest args) "\
 Load Tramp archive file name handler, and perform OPERATION." (defvar 
tramp-archive-autoload) (let ((default-directory temporary-file-directory) 
(tramp-archive-autoload tramp-archive-enabled)) (apply 
#'tramp-autoload-file-name-handler operation args)))
@@ -31729,6 +31996,7 @@ Add archive file name handler to 
`file-name-handler-alist'." (when (and tramp-ar
 
 ;;; Generated autoloads from net/tramp-compat.el
 
+ (defalias 'tramp-compat-rx #'rx)
 (register-definition-prefixes "tramp-compat" '("tramp-"))
 
 
@@ -31737,6 +32005,11 @@ Add archive file name handler to 
`file-name-handler-alist'." (when (and tramp-ar
 (register-definition-prefixes "tramp-crypt" '("tramp-crypt-"))
 
 
+;;; Generated autoloads from net/tramp-docker.el
+
+(register-definition-prefixes "tramp-docker" '("tramp-docker-"))
+
+
 ;;; Generated autoloads from net/tramp-ftp.el
 
 (register-definition-prefixes "tramp-ftp" '("tramp-"))
@@ -32544,14 +32817,14 @@ Fetch a GNU Info URL.
 
 
 (fn URL)")
-(defalias 'url-rlogin 'url-generic-emulator-loader)
+(define-obsolete-function-alias 'url-rlogin #'url-generic-emulator-loader 
"29.1")
 (defalias 'url-telnet 'url-generic-emulator-loader)
 (defalias 'url-tn3270 'url-generic-emulator-loader)
 (autoload 'url-data "url-misc" "\
 Fetch a data URL (RFC 2397).
 
 (fn URL)")
-(register-definition-prefixes "url-misc" '("url-do-terminal-emulator"))
+(register-definition-prefixes "url-misc" '("url-"))
 
 
 ;;; Generated autoloads from url/url-news.el
@@ -33030,9 +33303,10 @@ working revisions.  With a prefix argument HISTORIC, 
it reads two revision
 designators specifying which revisions to compare.
 
 The optional argument NOT-URGENT non-nil means it is ok to say no to
-saving the buffer.
+saving the buffer.  The optional argument FILESET can override the
+deduced fileset.
 
-(fn &optional HISTORIC NOT-URGENT)" t)
+(fn &optional HISTORIC NOT-URGENT FILESET)" t)
 (autoload 'vc-diff-mergebase "vc" "\
 Report diffs between the merge base of REV1 and REV2 revisions.
 The merge base is a common ancestor between REV1 and REV2 revisions.
@@ -33111,6 +33385,12 @@ given, the tag is made as a new branch and the files 
are
 checked out in that new branch.
 
 (fn DIR NAME BRANCHP)" t)
+(autoload 'vc-create-branch "vc" "\
+Descending recursively from DIR, make a branch called NAME.
+After a new branch is made, the files are checked out in that new branch.
+Uses `vc-create-tag' with the non-nil arg `branchp'.
+
+(fn DIR NAME)" t)
 (autoload 'vc-retrieve-tag "vc" "\
 For each file in or below DIR, retrieve their tagged version NAME.
 NAME can name a branch, in which case this command will switch to the
@@ -33121,8 +33401,16 @@ If NAME is empty, it refers to the latest revisions of 
the current branch.
 If locking is used for the files in DIR, then there must not be any
 locked files at or below DIR (but if NAME is empty, locked files are
 allowed and simply skipped).
+If the prefix argument BRANCHP is given, switch the branch
+and check out the files in that branch.
 This function runs the hook `vc-retrieve-tag-hook' when finished.
 
+(fn DIR NAME &optional BRANCHP)" t)
+(autoload 'vc-switch-branch "vc" "\
+Switch to the branch NAME in the directory DIR.
+If NAME is empty, it refers to the latest revisions of the current branch.
+Uses `vc-retrieve-tag' with the non-nil arg `branchp'.
+
 (fn DIR NAME)" t)
 (autoload 'vc-print-log "vc" "\
 List the change log of the current fileset in a window.
@@ -33214,6 +33502,22 @@ VCS command to run.
 On a non-distributed version control system, this signals an error.
 It also signals an error in a Bazaar bound branch.
 
+(fn &optional ARG)" t)
+(autoload 'vc-pull-and-push "vc" "\
+First pull, and then push the current branch.
+The push will only be performed if the pull operation was successful.
+
+You must be visiting a version controlled file, or in a `vc-dir' buffer.
+
+On a distributed version control system, this runs a \"pull\"
+operation on the current branch, prompting for the precise
+command if required.  Optional prefix ARG non-nil forces a prompt
+for the VCS command to run.  If this is successful, a \"push\"
+operation will then be done.
+
+On a non-distributed version control system, this signals an error.
+It also signals an error in a Bazaar bound branch.
+
 (fn &optional ARG)" t)
 (autoload 'vc-switch-backend "vc" "\
 Make BACKEND the current version control system for FILE.
@@ -33263,6 +33567,10 @@ From a program, any ARGS are assumed to be filenames 
for which
 log entries should be gathered.
 
 (fn &rest ARGS)" t)
+(autoload 'vc-edit-next-command "vc" "\
+Request editing the next VC shell command before execution.
+This is a prefix command.  It affects only a VC command executed
+immediately after this one." t)
 (register-definition-prefixes "vc" '("vc-" "with-vc-properties"))
 
 
@@ -33382,6 +33690,7 @@ FILE-OR-LIST is the name of a working file; it may be a 
list of
 files or be nil (to execute commands that don't expect a file
 name or set of files).  If an optional list of FLAGS is present,
 that is inserted into the command line before the filename.
+
 Return the return value of the slave command in the synchronous
 case, and the process object in the asynchronous case.
 
@@ -33396,6 +33705,7 @@ case, and the process object in the asynchronous case.
 
 ;;; Generated autoloads from vc/vc-git.el
 
+(put 'vc-git-annotate-switches 'safe-local-variable (lambda (switches) (equal 
switches "-w")))
  (defun vc-git-registered (file)
   "Return non-nil if FILE is registered with git."
   (if (vc-find-root file ".git")       ; Short cut.
@@ -34555,6 +34865,24 @@ Turn on Viper emulation of Vi in Emacs.  See Info node 
`(viper)Top'." t)
 (register-definition-prefixes "w32-vars" '("w32-"))
 
 
+;;; Generated autoloads from image/wallpaper.el
+
+(put 'wallpaper-setter-create 'lisp-indent-function 1)
+(autoload 'wallpaper-set "wallpaper" "\
+Set the desktop background to FILE in a graphical environment.
+
+On GNU/Linux and other Unix-like systems, this relies on an
+external command.  Which command to use is automatically detected
+in most cases, but can be manually customized with the user
+options `wallpaper-command' and `wallpaper-command-args'.
+
+On MS-Windows and Haiku systems, no external command is needed,
+so the value of `wallpaper-commands' is ignored.
+
+(fn FILE)" t)
+(register-definition-prefixes "wallpaper" '("wallpaper-"))
+
+
 ;;; Generated autoloads from emacs-lisp/warnings.el
 
 (defvar warning-prefix-function nil "\
@@ -35586,7 +35914,7 @@ If LIMIT is non-nil, then do not consider characters 
beyond LIMIT.
 
 ;;; Generated autoloads from progmodes/xref.el
 
-(push (purecopy '(xref 1 5 0)) package--builtin-versions)
+(push (purecopy '(xref 1 5 1)) package--builtin-versions)
 (autoload 'xref-find-backend "xref")
 (define-obsolete-function-alias 'xref-pop-marker-stack #'xref-go-back "29.1")
 (autoload 'xref-go-back "xref" "\
@@ -35805,6 +36133,7 @@ Zone out, completely." t)
 ;; no-byte-compile: t
 ;; version-control: never
 ;; no-update-autoloads: t
+;; no-native-compile: t
 ;; coding: utf-8-emacs-unix
 ;; End:
 
diff --git a/lisp/leim/quail/hangul.el b/lisp/leim/quail/hangul.el
index 83fee1e04c..89b9abe137 100644
--- a/lisp/leim/quail/hangul.el
+++ b/lisp/leim/quail/hangul.el
@@ -537,10 +537,6 @@ HELP-TEXT is a text set in 
`hangul-input-method-help-text'."
         (setq describe-current-input-method-function nil))
     (kill-local-variable 'input-method-function)))
 
-(define-obsolete-function-alias
-  'hangul-input-method-inactivate
-  #'hangul-input-method-deactivate "24.3")
-
 (defun hangul-input-method-help ()
   "Describe the current Hangul input method."
   (interactive)
diff --git a/lisp/leim/quail/indian.el b/lisp/leim/quail/indian.el
index 431d8369c1..048e16e8d8 100644
--- a/lisp/leim/quail/indian.el
+++ b/lisp/leim/quail/indian.el
@@ -2134,5 +2134,119 @@ is."
  ("`m" ?ꫲ)
  ("`?" ?꫱))
 
+(quail-define-package
+ "wancho" "Wancho" "𞋒" t "Wancho phonetic input method.
+
+ `\\=`' is used to switch levels instead of Alt-Gr."
+ nil t t t t nil nil nil nil nil t)
+
+(quail-define-rules
+ ("``" ?𞋿)
+ ("1"  ?𞋱)
+ ("`1" ?1)
+ ("2"  ?𞋲)
+ ("`2" ?2)
+ ("3"  ?𞋳)
+ ("`3" ?3)
+ ("4"  ?𞋴)
+ ("`4" ?4)
+ ("5"  ?𞋵)
+ ("`5" ?5)
+ ("6"  ?𞋶)
+ ("`6" ?6)
+ ("7"  ?𞋷)
+ ("`7" ?7)
+ ("8"  ?𞋸)
+ ("`8" ?8)
+ ("9"  ?𞋹)
+ ("`9" ?9)
+ ("0"  ?𞋰)
+ ("`0" ?0)
+ ("q"  ?𞋠)
+ ("Q"  ?𞋡)
+ ("w"  ?𞋒)
+ ("e"  ?𞋛)
+ ("E"  ?𞋧)
+ ("r"  ?𞋗)
+ ("t"  ?𞋋)
+ ("T"  ?𞋌)
+ ("y"  ?𞋆)
+ ("Y"  ?𞋫)
+ ("u"  ?𞋞)
+ ("U"  ?𞋪)
+ ("i"  ?𞋜)
+ ("I"  ?𞋥)
+ ("o"  ?𞋕)
+ ("O"  ?𞋖)
+ ("`o" ?𞋢)
+ ("`O" ?𞋦)
+ ("p"  ?𞋊)
+ ("P"  ?𞋇)
+ ("a"  ?𞋁)
+ ("A"  ?𞋀)
+ ("`a" ?𞋤)
+ ("`A" ?𞋣)
+ ("s"  ?𞋎)
+ ("S"  ?𞋏)
+ ("d"  ?𞋄)
+ ("f"  ?𞋍)
+ ("g"  ?𞋅)
+ ("h"  ?𞋚)
+ ("j"  ?𞋐)
+ ("k"  ?𞋔)
+ ("K"  ?𞋙)
+ ("l"  ?𞋈)
+ ("L"  ?𞋟)
+ ("z"  ?𞋑)
+ ("x"  ?𞋩)
+ ("X"  ?𞋝)
+ ("c"  ?𞋃)
+ ("C"  ?𞋬)
+ ("v"  ?𞋓)
+ ("V"  ?𞋭)
+ ("b"  ?𞋂)
+ ("B"  ?𞋮)
+ ("n"  ?𞋉)
+ ("N"  ?𞋯)
+ ("m"  ?𞋘)
+ ("M"  ?𞋨))
+
+(quail-define-package
+ "toto" "Toto" "𞊒𞊪" nil "Toto script phonetic input method."
+ nil t t t t nil nil nil nil nil t)
+
+(quail-define-rules
+ ("q"  ?𞊫)
+ ("Q"  ?𞊬)
+ ("w"  ?𞊜)
+ ("e"  ?𞊦)
+ ("E"  ?𞊧)
+ ("r"  ?𞊟)
+ ("t"  ?𞊒)
+ ("y"  ?𞊛)
+ ("u"  ?𞊥)
+ ("i"  ?𞊡)
+ ("I"  ?𞊢)
+ ("o"  ?𞊪)
+ ("p"  ?𞊐)
+ ("a"  ?𞊭)
+ ("s"  ?𞊙)
+ ("d"  ?𞊓)
+ ("f"  ?𞊮)
+ ("g"  ?𞊕)
+ ("h"  ?𞊞)
+ ("j"  ?𞊝)
+ ("k"  ?𞊔)
+ ("l"  ?𞊠)
+ ("z"  ?𞊣)
+ ("Z"  ?𞊤)
+ ("x"  ?𞊨)
+ ("X"  ?𞊩)
+ ("c"  ?𞊚)
+ ("b"  ?𞊑)
+ ("n"  ?𞊗)
+ ("N"  ?𞊘)
+ ("m"  ?𞊖))
+
 (provide 'indian)
 ;;; indian.el ends here
diff --git a/lisp/leim/quail/misc-lang.el b/lisp/leim/quail/misc-lang.el
index 0c4a0d4ce4..dad5cfc3e3 100644
--- a/lisp/leim/quail/misc-lang.el
+++ b/lisp/leim/quail/misc-lang.el
@@ -1180,5 +1180,383 @@
  (".||" ?𐩗)
  (".=" ?𐩘))
 
+(quail-define-package
+ "adlam" "Adlam" "𞤀" t "Adlam input method.
+
+ `\\=`' is used to switch levels instead of Alt-Gr.
+" nil t t t t nil nil nil nil nil t)
+
+(quail-define-rules
+ ("1"  ?𞥑)
+ ("`!" ?𞥞)
+ ("2"  ?𞥒)
+ ("3"  ?𞥓)
+ ("4"  ?𞥔)
+ ("5"  ?𞥕)
+ ("6"  ?𞥖)
+ ("7"  ?𞥗)
+ ("8"  ?𞥘)
+ ("9"  ?𞥙)
+ ("0"  ?𞥐)
+ ("q"  ?𞤹)
+ ("Q"  ?𞤗)
+ ("`q" ?𞥆)
+ ("w"  ?𞤱)
+ ("W"  ?𞤏)
+ ("`w" ?𞥈)
+ ("`W" ?𞥉)
+ ("e"  ?𞤫)
+ ("E"  ?𞤉)
+ ("`e" ?𞥅)
+ ("r"  ?𞤪)
+ ("R"  ?𞤈)
+ ("t"  ?𞤼)
+ ("T"  ?𞤚)
+ ("y"  ?𞤴)
+ ("Y"  ?𞤒)
+ ("`y" ?𞤰)
+ ("`Y" ?𞤎)
+ ("u"  ?𞤵)
+ ("U"  ?𞤓)
+ ("i"  ?𞤭)
+ ("I"  ?𞤋)
+ ("o"  ?𞤮)
+ ("O"  ?𞤌)
+ ("p"  ?𞤨)
+ ("P"  ?𞤆)
+ ("a"  ?𞤢)
+ ("A"  ?𞤀)
+ ("`a" ?𞥄)
+ ("s"  ?𞤧)
+ ("S"  ?𞤅)
+ ("`s" ?𞥃)
+ ("`S" ?𞤡)
+ ("d"  ?𞤣)
+ ("D"  ?𞤁)
+ ("`d" ?𞤯)
+ ("`D" ?𞤍)
+ ("f"  ?𞤬)
+ ("F"  ?𞤊)
+ ("g"  ?𞤺)
+ ("G"  ?𞤘)
+ ("`g" ?𞥀)
+ ("`G" ?𞤞)
+ ("h"  ?𞤸)
+ ("H"  ?𞤖)
+ ("`h" ?𞥇)
+ ("j"  ?𞤶)
+ ("J"  ?𞤔)
+ ("k"  ?𞤳)
+ ("K"  ?𞤑)
+ ("`k" ?𞤿)
+ ("`K" ?𞤝)
+ ("l"  ?𞤤)
+ ("L"  ?𞤂)
+ ("z"  ?𞥁)
+ ("Z"  ?𞤟)
+ ("`z" ?𞥂)
+ ("`Z" ?𞤠)
+ ("x"  ?𞤽)
+ ("X"  ?𞤛)
+ ("c"  ?𞤷)
+ ("C"  ?𞤕)
+ ("`c" #x200C) ; ZWNJ
+ ("v"  ?𞤾)
+ ("V"  ?𞤜)
+ ("`v" ?𞥊)
+ ("b"  ?𞤦)
+ ("B"  ?𞤄)
+ ("`b" ?𞤩)
+ ("`B" ?𞤇)
+ ("n"  ?𞤲)
+ ("N"  ?𞤐)
+ ("`n" ?𞤻)
+ ("`N" ?𞤙)
+ ("m"  ?𞤥)
+ ("M"  ?𞤃)
+ ("`m" ?𞥋)
+ ("`/" ?𞥟))
+
+(quail-define-package
+ "mende-kikakui" "Mende Kikakui" "𞠗" nil
+ "Mende Kikakui input method." nil t t t t nil nil nil nil nil t)
+
+(quail-define-rules
+ ("1"    ?𞣇)
+ ("2"    ?𞣈)
+ ("3"    ?𞣉)
+ ("4"    ?𞣊)
+ ("5"    ?𞣋)
+ ("6"    ?𞣌)
+ ("7"    ?𞣍)
+ ("8"    ?𞣎)
+ ("9"    ?𞣏)
+
+ (".1"   ?𞣐)
+ (".2"   ?𞣑)
+ (".3"   ?𞣒)
+ (".4"   ?𞣓)
+ (".5"   ?𞣔)
+ (".6"   ?𞣕)
+ (".7"   ?𞣖)
+
+ ("ki"   ?𞠀)
+ ("ka"   ?𞠁)
+ ("ku"   ?𞠂)
+ ("kee"  ?𞠃)
+ ("ke"   ?𞠄)
+ ("koo"  ?𞠅)
+ ("ko"   ?𞠆)
+ ("kua"  ?𞠇)
+
+ ("wi"   ?𞠈)
+ ("wa"   ?𞠉)
+ ("wu"   ?𞠊)
+ ("wee"  ?𞠋)
+ ("we"   ?𞠌)
+ ("woo"  ?𞠍)
+ ("wo"   ?𞠎)
+ ("wui"  ?𞠏)
+ ("wei"  ?𞠐)
+
+ ("wvi"  ?𞠑)
+ ("wua"  ?𞠒)
+ ("wve"  ?𞠓)
+
+ ("min"  ?𞠔)
+ ("man"  ?𞠕)
+ ("mun"  ?𞠖)
+ ("men"  ?𞠗)
+ ("mon"  ?𞠘)
+ ("muan" ?𞠙)
+ ("muen" ?𞠚)
+
+ ("bi"   ?𞠛)
+ ("ba"   ?𞠜)
+ ("bu"   ?𞠝)
+ ("bee"  ?𞠞)
+ ("be"   ?𞠟)
+ ("boo"  ?𞠠)
+ ("bo"   ?𞠡)
+
+ ("i"    ?𞠢)
+ ("a"    ?𞠣)
+ ("u"    ?𞠤)
+ ("ee"   ?𞠥)
+ ("e"    ?𞠦)
+ ("oo"   ?𞠧)
+ ("o"    ?𞠨)
+ ("ei"   ?𞠩)
+ ("in"   ?𞠪)
+ ("inn"  ?𞠫)
+ ("an"   ?𞠬)
+ ("en"   ?𞠭)
+
+ ("si"   ?𞠮)
+ ("sa"   ?𞠯)
+ ("su"   ?𞠰)
+ ("see"  ?𞠱)
+ ("se"   ?𞠲)
+ ("soo"  ?𞠳)
+ ("so"   ?𞠴)
+ ("sia"  ?𞠵)
+
+ ("li"   ?𞠶)
+ ("la"   ?𞠷)
+ ("lu"   ?𞠸)
+ ("lee"  ?𞠹)
+ ("le"   ?𞠺)
+ ("loo"  ?𞠻)
+ ("lo"   ?𞠼)
+ ("lle"  ?𞠽)
+
+ ("di"   ?𞠾)
+ ("da"   ?𞠿)
+ ("du"   ?𞡀)
+ ("dee"  ?𞡁)
+ ("doo"  ?𞡂)
+ ("do"   ?𞡃)
+
+ ("ti"   ?𞡄)
+ ("ta"   ?𞡅)
+ ("tu"   ?𞡆)
+ ("tee"  ?𞡇)
+ ("te"   ?𞡈)
+ ("too"  ?𞡉)
+ ("to"   ?𞡊)
+
+ ("ji"   ?𞡋)
+ ("ja"   ?𞡌)
+ ("ju"   ?𞡍)
+ ("jee"  ?𞡎)
+ ("je"   ?𞡏)
+ ("joo"  ?𞡐)
+ ("jo"   ?𞡑)
+ ("jjo"  ?𞡒)
+
+ ("yi"   ?𞡓)
+ ("ya"   ?𞡔)
+ ("yu"   ?𞡕)
+ ("yee"  ?𞡖)
+ ("ye"   ?𞡗)
+ ("yoo"  ?𞡘)
+ ("yo"   ?𞡙)
+
+ ("fi"   ?𞡚)
+ ("fa"   ?𞡛)
+ ("fu"   ?𞡜)
+ ("fee"  ?𞡝)
+ ("fe"   ?𞡞)
+ ("foo"  ?𞡟)
+ ("fo"   ?𞡠)
+ ("fua"  ?𞡡)
+ ("fan"  ?𞡢)
+
+ ("nin"  ?𞡣)
+ ("nan"  ?𞡤)
+ ("nun"  ?𞡥)
+ ("nen"  ?𞡦)
+ ("non"  ?𞡧)
+
+ ("hi"   ?𞡨)
+ ("ha"   ?𞡩)
+ ("hu"   ?𞡪)
+ ("hee"  ?𞡫)
+ ("he"   ?𞡬)
+ ("hoo"  ?𞡭)
+ ("ho"   ?𞡮)
+ ("heei" ?𞡯)
+ ("hoou" ?𞡰)
+ ("hin"  ?𞡱)
+ ("han"  ?𞡲)
+ ("hun"  ?𞡳)
+ ("hen"  ?𞡴)
+ ("hon"  ?𞡵)
+ ("huan" ?𞡶)
+
+ ("nggi"   ?𞡷)
+ ("ngga"   ?𞡸)
+ ("nggu"   ?𞡹)
+ ("nggee"  ?𞡺)
+ ("ngge"   ?𞡻)
+ ("nggoo"  ?𞡼)
+ ("nggo"   ?𞡽)
+ ("nggaa"  ?𞡾)
+ ("nggua"  ?𞡿)
+ ("nngge"  ?𞢀)
+ ("nnggoo" ?𞢁)
+ ("nnggo"  ?𞢂)
+
+ ("gi"    ?𞢃)
+ ("ga"    ?𞢄)
+ ("gu"    ?𞢅)
+ ("gee"   ?𞢆)
+ ("guei"  ?𞢇)
+ ("guan"  ?𞢈)
+
+ ("ngen"  ?𞢉)
+ ("ngon"  ?𞢊)
+ ("nguan" ?𞢋)
+
+ ("pi"    ?𞢌)
+ ("pa"    ?𞢍)
+ ("pu"    ?𞢎)
+ ("pee"   ?𞢏)
+ ("pe"    ?𞢐)
+ ("poo"   ?𞢑)
+ ("po"    ?𞢒)
+
+ ("mbi"   ?𞢓)
+ ("mba"   ?𞢔)
+ ("mbu"   ?𞢕)
+ ("mbee"  ?𞢖)
+ ("mmbee" ?𞢗)
+ ("mbe"   ?𞢘)
+ ("mboo"  ?𞢙)
+ ("mbo"   ?𞢚)
+ ("mbuu"  ?𞢛)
+ ("mmbe"  ?𞢜)
+ ("mmboo" ?𞢝)
+ ("mmbo"  ?𞢞)
+
+ ("kpi"   ?𞢟)
+ ("kpa"   ?𞢠)
+ ("kpu"   ?𞢡)
+ ("kpee"  ?𞢢)
+ ("kpe"   ?𞢣)
+ ("kpoo"  ?𞢤)
+ ("kpo"   ?𞢥)
+
+ ("gbi"   ?𞢦)
+ ("gba"   ?𞢧)
+ ("gbu"   ?𞢨)
+ ("gbee"  ?𞢩)
+ ("gbe"   ?𞢪)
+ ("gboo"  ?𞢫)
+ ("gbo"   ?𞢬)
+
+ ("ra"    ?𞢭)
+
+ ("ndi"   ?𞢮)
+ ("nda"   ?𞢯)
+ ("ndu"   ?𞢰)
+ ("ndee"  ?𞢱)
+ ("nde"   ?𞢲)
+ ("ndoo"  ?𞢳)
+ ("ndo"   ?𞢴)
+
+ ("nja"   ?𞢵)
+ ("nju"   ?𞢶)
+ ("njee"  ?𞢷)
+ ("njoo"  ?𞢸)
+
+ ("vi"    ?𞢹)
+ ("va"    ?𞢺)
+ ("vu"    ?𞢻)
+ ("vee"   ?𞢼)
+ ("ve"    ?𞢽)
+ ("voo"   ?𞢾)
+ ("vo"    ?𞢿)
+
+ ("nyin"  ?𞣀)
+ ("nyan"  ?𞣁)
+ ("nyun"  ?𞣂)
+ ("nyen"  ?𞣃)
+ ("nyon"  ?𞣄))
+
+(quail-define-package
+ "gothic" "Gothic" "𐌰" nil
+ "Input methid for the ancient Gothic script."
+ nil t t t t nil nil nil nil nil t)
+
+(quail-define-rules
+ ("q"  ?𐌵)
+ ("w"  ?𐍅)
+ ("e"  ?𐌴)
+ ("r"  ?𐍂)
+ ("t"  ?𐍄)
+ ("y"  ?𐌸)
+ ("u"  ?𐌿)
+ ("i"  ?𐌹)
+ ("o"  ?𐍉)
+ ("p"  ?𐍀)
+ ("a"  ?𐌰)
+ ("s"  ?𐍃)
+ ("d"  ?𐌳)
+ ("f"  ?𐍆)
+ ("g"  ?𐌲)
+ ("h"  ?𐌷)
+ ("j"  ?𐌾)
+ ("k"  ?𐌺)
+ ("l"  ?𐌻)
+ ("z"  ?𐌶)
+ ("x"  ?𐍇)
+ ("c"  ?𐍈)
+ ("v"  ?𐍁)
+ ("V"  ?𐍊)
+ ("b"  ?𐌱)
+ ("n"  ?𐌽)
+ ("m"  ?𐌼))
+
 (provide 'misc-lang)
 ;;; misc-lang.el ends here
diff --git a/lisp/leim/quail/uni-input.el b/lisp/leim/quail/uni-input.el
index 36d8e6a840..3f10b873a3 100644
--- a/lisp/leim/quail/uni-input.el
+++ b/lisp/leim/quail/uni-input.el
@@ -113,10 +113,6 @@ While this input method is active, the variable
   (interactive)
   (ucs-input-activate -1))
 
-(define-obsolete-function-alias
-  'ucs-input-inactivate
-  #'ucs-input-deactivate "24.3")
-
 (defun ucs-input-help ()
   (interactive)
   (with-output-to-temp-buffer "*Help*"
diff --git a/lisp/loadhist.el b/lisp/loadhist.el
index b4ed043246..0cb02f072e 100644
--- a/lisp/loadhist.el
+++ b/lisp/loadhist.el
@@ -171,6 +171,13 @@ unloading."
     (cond
      ((null hist)
       (defalias fun nil)
+      ;; FIXME: Arguably these properties should be applied via
+      ;; `define-symbol-prop', but most code still uses just `put'.
+      ;; FIXME: Maybe these properties should be attached to the
+      ;; function itself (as for `advertised-calling-convention')
+      ;; rather than to its symbol.
+      (if (get fun 'compiler-macro) (put fun 'compiler-macro nil))
+      (if (get fun 'gv-expander)    (put fun 'gv-expander nil))
       ;; Override the change that `defalias' just recorded.
       (put fun 'function-history nil))
      ((equal (car hist) loadhist-unload-filename)
diff --git a/lisp/loadup.el b/lisp/loadup.el
index 634a331436..c01c827a75 100644
--- a/lisp/loadup.el
+++ b/lisp/loadup.el
@@ -244,9 +244,7 @@
 (load "language/indonesian")
 
 (load "indent")
-(let ((max-specpdl-size (max max-specpdl-size 1800)))
-  ;; A particularly demanding file to load; 1600 does not seem to be enough.
-  (load "emacs-lisp/cl-generic"))
+(load "emacs-lisp/cl-generic")
 (load "simple")
 (load "emacs-lisp/seq")
 (load "emacs-lisp/nadvice")
diff --git a/lisp/mail/emacsbug.el b/lisp/mail/emacsbug.el
index d72809b186..60f733435a 100644
--- a/lisp/mail/emacsbug.el
+++ b/lisp/mail/emacsbug.el
@@ -300,7 +300,7 @@ usually do not have translators for other languages.\n\n")))
     (let ((txt (delete-and-extract-region (1+ user-point) (point))))
       (insert (propertize "\n" 'display txt)))
 
-    (emacs-bug--system-description)
+    (emacs-build-description)
     (insert "Configured features:\n" system-configuration-features "\n\n")
     (fill-region (line-beginning-position -1) (point))
     (when (and (featurep 'native-compile)
@@ -386,7 +386,10 @@ copy text to your preferred mail program.\n"
                 (buffer-substring-no-properties (point-min) (point)))
     (goto-char user-point)))
 
-(defun emacs-bug--system-description ()
+;;;###autoload
+(defun emacs-build-description ()
+  "Insert a description of the current Emacs build in the current buffer."
+  (interactive)
   (let ((start (point)))
     (insert "\nIn " (emacs-version))
     (if emacs-build-system
@@ -414,8 +417,6 @@ copy text to your preferred mail program.\n"
            system-configuration-options "'\n\n")
     (fill-region (line-beginning-position -1) (point))))
 
-(define-obsolete-function-alias 'report-emacs-bug-info #'info-emacs-bug "24.3")
-
 (defun report-emacs-bug-hook ()
   "Do some checking before sending a bug report."
   (goto-char (point-max))
@@ -523,7 +524,7 @@ Message buffer where you can explain more about the patch."
   (compose-mail-other-window report-emacs-bug-address subject)
   (message-goto-body)
   (insert "\n\n\n")
-  (emacs-bug--system-description)
+  (emacs-build-description)
   (mml-attach-file file "text/patch" nil "attachment")
   (message-goto-body)
   (message "Write a description of the patch and use %s to send it"
diff --git a/lisp/mail/feedmail.el b/lisp/mail/feedmail.el
index 989a8b3cd6..2ae916e3ac 100644
--- a/lisp/mail/feedmail.el
+++ b/lisp/mail/feedmail.el
@@ -614,29 +614,12 @@ to arrange for the message to get a From: line."
 
 
 (defcustom feedmail-sendmail-f-doesnt-sell-me-out nil
-  "Whether sendmail should issue a warning header if called with \"-f\".
-The sendmail program has a useful feature to let you set the envelope FROM
-address via a command line option, \"-f\".  Unfortunately, it also has a widely
-disliked default behavior of selling you out if you do that by inserting
-an unattractive warning in the headers.  It looks something like this:
-
-  X-Authentication-Warning: u1.example.com: niceguy set
-      sender to niceguy@example.com using -f
-
-It is possible to configure sendmail to not do this, but such a
-reconfiguration is not an option for many users.  As this is the
-default behavior of most sendmail installations, one can mostly
-only wish it were otherwise.  If feedmail believes the sendmail
-program will sell you out this way, it won't use the \"-f\"
-option when calling sendmail.  If it doesn't think sendmail will
-sell you out, it will use the \"-f\" \(since it is a handy
-feature).  You control what feedmail thinks with this variable.
-The default is nil, meaning that feedmail will believe that
-sendmail will sell you out."
+  "If non-nil, call \"sendmail\" with \"-f\".
+See `message-sendmail-f-is-evil' for an explanation of what the
+\"-f\" parameter does."
   :version "24.1"
   :group 'feedmail-headers
-  :type 'boolean
-)
+  :type 'boolean)
 
 
 (defcustom feedmail-deduce-envelope-from t
diff --git a/lisp/mail/hashcash.el b/lisp/mail/hashcash.el
index eebb140088..5136e11c89 100644
--- a/lisp/mail/hashcash.el
+++ b/lisp/mail/hashcash.el
@@ -57,8 +57,7 @@
   "The default number of bits to pay to unknown users.
 If this is zero, no payment header will be generated.
 See `hashcash-payment-alist'."
-  :type 'natnum
-  :group 'hashcash)
+  :type 'natnum)
 
 (defcustom hashcash-payment-alist '()
   "An association list mapping email addresses to payment amounts.
@@ -72,46 +71,39 @@ present, is the string to be hashed; if not present ADDR 
will be used."
                         (list :tag "Replace hash input"
                               (string :name "Address")
                               (string :name "Hash input")
-                              (integer :name "Amount"))))
-  :group 'hashcash)
+                               (integer :name "Amount")))))
 
 (defcustom hashcash-default-accept-payment 20
   "The default minimum number of bits to accept on incoming payments."
-  :type 'natnum
-  :group 'hashcash)
+  :type 'natnum)
 
 (defcustom hashcash-accept-resources `((,user-mail-address nil))
   "An association list mapping hashcash resources to payment amounts.
 Resources named here are to be accepted in incoming payments.  If the
 corresponding AMOUNT is NIL, the value of `hashcash-default-accept-payment'
 is used instead."
-  :type 'alist
-  :group 'hashcash)
+  :type 'alist)
 
 (define-obsolete-variable-alias 'hashcash-path 'hashcash-program "24.4")
 (defcustom hashcash-program "hashcash"
   "The name of the hashcash executable.
 If this is not in your PATH, specify an absolute file name."
-  :type '(choice (const nil) file)
-  :group 'hashcash)
+  :type '(choice (const nil) file))
 
 (defcustom hashcash-extra-generate-parameters '("-Z2")
   "A list of parameter strings passed to `hashcash-program' when minting.
 For example, on very old hardware, you may want to set this
 to (\"-Z0\") to disable compression."
   :type '(repeat string)
-  :version "29.1"
-  :group 'hashcash)
+  :version "29.1")
 
 (defcustom hashcash-double-spend-database "hashcash.db"
   "The name of the double-spending database file."
-  :type 'file
-  :group 'hashcash)
+  :type 'file)
 
 (defcustom hashcash-in-news nil
   "Specifies whether or not hashcash payments should be made to newsgroups."
-  :type 'boolean
-  :group 'hashcash)
+  :type 'boolean)
 
 (defvar hashcash-process-alist nil
   "Alist of asynchronous hashcash processes and buffers.")
@@ -316,6 +308,7 @@ Set ASYNC to t to start asynchronous calculation.  (See
     (save-excursion
       (save-restriction
        (message-narrow-to-headers)
+        (goto-char (point-max))
        (let ((to (hashcash-strip-quoted-names (mail-fetch-field "To" nil t)))
              (cc (hashcash-strip-quoted-names (mail-fetch-field "Cc" nil t)))
              (ng (hashcash-strip-quoted-names (mail-fetch-field "Newsgroups"
diff --git a/lisp/mail/rmail.el b/lisp/mail/rmail.el
index 4bfec22b3a..812e9a201b 100644
--- a/lisp/mail/rmail.el
+++ b/lisp/mail/rmail.el
@@ -372,11 +372,17 @@ If nil, display all header fields except those matched by
   :group 'rmail-headers)
 
 ;;;###autoload
-(defcustom rmail-retry-ignored-headers (purecopy 
"^x-authentication-warning:\\|^x-detected-operating-system:\\|^x-spam[-a-z]*:\\|content-type:\\|content-transfer-encoding:\\|mime-version:\\|message-id:")
+(defcustom rmail-retry-ignored-headers
+  (concat "^x-authentication-warning:\\|^x-detected-operating-system:\\|"
+          "^x-spam[-a-z]*:\\|^arc-.*:\\|"
+          "^content-type:\\|^content-transfer-encoding:\\|"
+          "^mime-version:\\|^message-id:\\|^x-google-smtp-source:\\|"
+          "^x-received:\\|^received-spf:\\|"
+          "^authentication-results:\\|^dkim-signature:")
   "Headers that should be stripped when retrying a failed message."
   :type '(choice regexp (const :value nil :tag "None"))
   :group 'rmail-headers
-  :version "23.2")        ; added x-detected-operating-system, x-spam
+  :version "29.1")
 
 ;;;###autoload
 (defcustom rmail-highlighted-headers (purecopy "^From:\\|^Subject:")
@@ -4614,6 +4620,9 @@ Argument MIME is non-nil if this is a mime message."
                     "> ")
              (push (rmail-epa-decrypt-1 mime) decrypts))))
 
+      ;; Decode any base64-encoded mime sections.
+      (rmail-epa-decode)
+
       (when (and decrypts (rmail-buffers-swapped-p))
        (when (y-or-n-p "Replace the original message? ")
           (when (eq major-mode 'rmail-mode)
@@ -4678,6 +4687,23 @@ Argument MIME is non-nil if this is a mime message."
       (unless decrypts
        (error "Nothing to decrypt")))))
 
+;; Decode all base64-encoded mime sections, so that this change
+;; is made in the Rmail file, not just in the viewing buffer.
+(defun rmail-epa-decode ()
+  (save-excursion
+    (goto-char (point-min))
+    (while (re-search-forward "--------------[0-9a-zA-Z]+\n" nil t)
+      (let ((delim (concat (substring (match-string 0) 0 -1) "--\n")))
+        (when (looking-at "\
+Content-Type: text/[a-z]+; charset=UTF-8; format=flowed
+Content-Transfer-Encoding: base64\n")
+          (goto-char (match-end 0))
+          (let ((start (point))
+                (inhibit-read-only t))
+            (search-forward delim)
+            (forward-line -1)
+            (base64-decode-region start (point))
+            (forward-line 1)))))))
 
 ;;;;  Desktop support
 
diff --git a/lisp/mh-e/mh-e.el b/lisp/mh-e/mh-e.el
index f6031df9c2..9a04d89097 100644
--- a/lisp/mh-e/mh-e.el
+++ b/lisp/mh-e/mh-e.el
@@ -2831,9 +2831,7 @@ removed and entries from `mh-invisible-header-fields' are 
added."
         (setq mh-invisible-header-fields-compiled
               (concat
                "^"
-               ;; workaround for insufficient default
-               (let ((max-specpdl-size 1000))
-                 (regexp-opt fields t))))
+               (regexp-opt fields t)))
       (setq mh-invisible-header-fields-compiled nil))))
 
 ;; Compile invisible header fields.
@@ -3183,8 +3181,6 @@ function used to insert the signature with
   :group 'mh-letter
   :package-version '(MH-E . "8.0"))
 
-(define-obsolete-variable-alias 'mh-kill-folder-suppress-prompt-hooks
-  'mh-kill-folder-suppress-prompt-functions "24.3")
 (defcustom mh-kill-folder-suppress-prompt-functions '(mh-search-p)
   "Abnormal hook run at the beginning of 
\\<mh-folder-mode-map>\\[mh-kill-folder].
 
diff --git a/lisp/mh-e/mh-funcs.el b/lisp/mh-e/mh-funcs.el
index ab89ef2a3d..4956d9b59f 100644
--- a/lisp/mh-e/mh-funcs.el
+++ b/lisp/mh-e/mh-funcs.el
@@ -101,7 +101,7 @@ a non-nil value to suppress the normal prompt when you 
remove a
 folder. This is useful for folders that are easily regenerated."
   (interactive)
   (if (or (run-hook-with-args-until-success
-           'mh-kill-folder-suppress-prompt-hooks)
+           'mh-kill-folder-suppress-prompt-functions)
           (yes-or-no-p (format "Remove folder %s (and all included messages)? "
                                mh-current-folder)))
       (let ((folder mh-current-folder)
diff --git a/lisp/minibuf-eldef.el b/lisp/minibuf-eldef.el
index 3f04a3e921..ba7e68eb81 100644
--- a/lisp/minibuf-eldef.el
+++ b/lisp/minibuf-eldef.el
@@ -64,6 +64,8 @@
   :type 'boolean
   :group 'minibuffer
   :version "24.3")
+(make-obsolete-variable 'minibuffer-eldef-shorten-default
+                        'minibuffer-default-prompt-format "29.1")
 
 (defvar minibuffer-default-in-prompt-regexps
   (minibuffer-default--in-prompt-regexps)
diff --git a/lisp/minibuffer.el b/lisp/minibuffer.el
index 3daab8a1e8..9f26e4f7f9 100644
--- a/lisp/minibuffer.el
+++ b/lisp/minibuffer.el
@@ -4461,6 +4461,11 @@ FORMAT-ARGS is non-nil, PROMPT is used as a format 
control
 string, and FORMAT-ARGS are the arguments to be substituted into
 it.  See `format' for details.
 
+Both PROMPT and `minibuffer-default-prompt-format' are run
+through `substitute-command-keys' (which see).  In particular,
+this means that single quotes may be displayed by equivalent
+characters, according to the capabilities of the terminal.
+
 If DEFAULT is a list, the first element is used as the default.
 If not, the element is used as is.
 
@@ -4468,12 +4473,12 @@ If DEFAULT is nil or an empty string, no \"default 
value\" string
 is included in the return value."
   (concat
    (if (null format-args)
-       prompt
-     (apply #'format prompt format-args))
+       (substitute-command-keys prompt)
+     (apply #'format (substitute-command-keys prompt) format-args))
    (and default
         (or (not (stringp default))
             (length> default 0))
-        (format minibuffer-default-prompt-format
+        (format (substitute-command-keys minibuffer-default-prompt-format)
                 (if (consp default)
                     (car default)
                   default)))
diff --git a/lisp/mpc.el b/lisp/mpc.el
index ba95308bf6..1775e7d5e7 100644
--- a/lisp/mpc.el
+++ b/lisp/mpc.el
@@ -182,8 +182,6 @@ numerically rather than lexicographically."
                     (abs res))
                 res))))))))
 
-(define-obsolete-function-alias 'mpc-string-prefix-p #'string-prefix-p "24.3")
-
 ;; This can speed up mpc--song-search significantly.  The table may grow
 ;; very large, tho.  It's only bounded by the fact that it gets flushed
 ;; whenever the connection is established; which seems to work OK thanks
diff --git a/lisp/net/dictionary.el b/lisp/net/dictionary.el
index 43dd28ff6d..4c52382c67 100644
--- a/lisp/net/dictionary.el
+++ b/lisp/net/dictionary.el
@@ -59,7 +59,7 @@ the existing connection."
 
 (defgroup dictionary nil
   "Client for accessing the dictd server based dictionaries."
-  :group 'hypermedia)
+  :group 'applications)
 
 (defgroup dictionary-proxy nil
   "Proxy configuration options for the dictionary client."
diff --git a/lisp/net/eww.el b/lisp/net/eww.el
index 6ed0719eca..414de931c4 100644
--- a/lisp/net/eww.el
+++ b/lisp/net/eww.el
@@ -315,7 +315,8 @@ parameter, and should return the (possibly) transformed 
URL."
 
 (defvar-keymap eww-link-keymap
   :parent shr-map
-  "RET" #'eww-follow-link)
+  "RET" #'eww-follow-link
+  "<mouse-2>" #'eww-follow-link)
 
 (defvar-keymap eww-image-link-keymap
   :parent shr-map
@@ -415,13 +416,11 @@ For more information, see Info node `(eww) Top'."
 (defun eww-retrieve (url callback cbargs)
   (cond
    ((null eww-retrieve-command)
-    (url-retrieve url #'eww-render
-                  (list url nil (current-buffer))))
+    (url-retrieve url #'eww-render cbargs))
    ((eq eww-retrieve-command 'sync)
-    (let ((orig-buffer (current-buffer))
-          (data-buffer (url-retrieve-synchronously url)))
+    (let ((data-buffer (url-retrieve-synchronously url)))
       (with-current-buffer data-buffer
-        (eww-render nil url nil orig-buffer))))
+        (apply #'eww-render nil url cbargs))))
    (t
     (let ((buffer (generate-new-buffer " *eww retrieve*"))
           (error-buffer (generate-new-buffer " *eww error*")))
@@ -626,7 +625,10 @@ The renaming scheme is performed in accordance with
            (setq eww-history-position 0)
            (and last-coding-system-used
                 (set-buffer-file-coding-system last-coding-system-used))
-           (run-hooks 'eww-after-render-hook)))
+           (run-hooks 'eww-after-render-hook)
+            ;; Enable undo again so that undo works in text input
+            ;; boxes.
+            (setq buffer-undo-list nil)))
       (kill-buffer data-buffer))))
 
 (defun eww-parse-headers ()
@@ -928,7 +930,8 @@ The renaming scheme is performed in accordance with
   ;; May be set later if there's a next/prev link.
   (setq-local multi-isearch-next-buffer-function nil)
   (unless (eq major-mode 'eww-mode)
-    (eww-mode)))
+    (eww-mode))
+  (buffer-disable-undo))
 
 (defun eww-current-url nil
   "Return URI of the Web page the current EWW buffer is visiting."
@@ -1176,8 +1179,28 @@ the like."
                       '((url . eww--url-at-point))))
   (setq-local bookmark-make-record-function #'eww-bookmark-make-record)
   (buffer-disable-undo)
+  (setq-local shr-url-transformer #'eww--transform-url)
+  ;; Also rescale images when rescaling the text.
+  (add-hook 'text-scale-mode-hook #'eww--rescale-images nil t)
   (setq buffer-read-only t))
 
+(defvar text-scale-mode)
+(defvar text-scale-mode-amount)
+(defun eww--rescale-images ()
+  (let ((scaling (if text-scale-mode
+                     (+ 1 (* text-scale-mode-amount 0.1))
+                   1)))
+    (save-excursion
+      (goto-char (point-min))
+      (while-let ((match (text-property-search-forward
+                          'display nil (lambda (_ value) (imagep value)))))
+        (let ((image (prop-match-value match)))
+          (unless (image-property image :original-scale)
+            (setf (image-property image :original-scale)
+                  (or (image-property image :scale) 1)))
+          (setf (image-property image :scale)
+                (* (image-property image :original-scale) scaling)))))))
+
 (defun eww--url-at-point ()
   "`thing-at-point' provider function."
   (get-text-property (point) 'shr-url))
@@ -1606,7 +1629,7 @@ See URL 
`https://developer.mozilla.org/en-US/docs/Web/HTML/Element/Input'.")
     (unless (= start (point))
       (put-text-property start (1+ start) 'help-echo "Input field")
       ;; Mark this as an element we can TAB to.
-      (put-text-property start (1+ start) 'shr-url dom))))
+      (put-text-property start (1+ start) 'shr-tab-stop t))))
 
 (defun eww-tag-select (dom)
   (shr-ensure-paragraph)
@@ -1878,7 +1901,8 @@ If EXTERNAL is double prefix, browse in new buffer."
    eww-mode)
   (mouse-set-point mouse-event)
   (let* ((orig-url (get-text-property (point) 'shr-url))
-         (url (eww--transform-url orig-url)))
+         (url (eww--transform-url orig-url))
+         target)
     (cond
      ((not url)
       (message "No link under point"))
@@ -1890,12 +1914,17 @@ If EXTERNAL is double prefix, browse in new buffer."
       (funcall browse-url-secondary-browser-function url)
       (shr--blink-link))
      ;; This is a #target url in the same page as the current one.
-     ((and (url-target (url-generic-parse-url url))
+     ((and (setq target (url-target (url-generic-parse-url url)))
           (eww-same-page-p url (plist-get eww-data :url)))
-      (let ((dom (plist-get eww-data :dom)))
+      (let ((point (point)))
        (eww-save-history)
        (plist-put eww-data :url url)
-       (eww-display-html 'utf-8 url dom nil (current-buffer))))
+        (goto-char (point-min))
+        (if-let ((match (text-property-search-forward 'shr-target-id target 
#'member)))
+            (goto-char (prop-match-beginning match))
+          (goto-char (if (equal target "top")
+                         (point-min)
+                       point)))))
      (t
       (eww-browse-url orig-url external)))))
 
diff --git a/lisp/net/ldap.el b/lisp/net/ldap.el
index 5e14589d19..062ff05d69 100644
--- a/lisp/net/ldap.el
+++ b/lisp/net/ldap.el
@@ -72,6 +72,9 @@ HOST is the hostname of an LDAP server (with an optional TCP 
port number
 appended to it using a colon as a separator).
 PROPn and VALn are property/value pairs describing parameters for the server.
 Valid properties include:
+  `auth-source' specifies whether or not to look up, via the
+  `auth-source' library, options which are not otherwise provided
+  in this list.  See `ldap-search-internal'.
   `binddn' is the distinguished name of the user to bind as
     (in RFC 1779 syntax).
   `passwd' is the password to use for simple authentication.
@@ -90,6 +93,11 @@ Valid properties include:
                       (string :tag "Host name")
                       (checklist :inline t
                                  :greedy t
+                                 (list
+                                  :tag "Use auth-source"
+                                  :inline t
+                                  (const :tag "Use auth-source" auth-source)
+                                  boolean)
                                  (list
                                   :tag "Search Base"
                                   :inline t
diff --git a/lisp/net/mailcap.el b/lisp/net/mailcap.el
index 469643dbca..aa0c172655 100644
--- a/lisp/net/mailcap.el
+++ b/lisp/net/mailcap.el
@@ -125,7 +125,7 @@ is consulted."
      ("vnd\\.ms-excel"
       (viewer . "gnumeric %s")
       (test   . (getenv "DISPLAY"))
-      (type . "application/vnd.ms-excel"))
+      (type . "application/vnd\\.ms-excel"))
      ("octet-stream"
       (viewer . mailcap-save-binary-file)
       (non-viewer . t)
@@ -716,27 +716,43 @@ to supply to the test."
           result))))
 
 (defun mailcap-add-mailcap-entry (major minor info &optional storage)
+  "Add handler INFO for mime type MAJOR/MINOR to STORAGE.
+
+MAJOR and MINOR should be strings.  MINOR is treated as a regexp
+in later lookups, and, therefore, you may need to escape it
+appropriately.
+
+The format of INFO is described in `mailcap-mime-data'.
+
+STORAGE should be a symbol referring to a variable.  The value of
+this variable should have the same format as `mailcap-mime-data'.
+STORAGE defaults to `mailcap--computed-mime-data'.
+
+None of this is enforced."
   (let* ((storage (or storage 'mailcap--computed-mime-data))
-         (old-major (assoc major (symbol-value storage))))
-    (if (null old-major)               ; New major area
-        (set storage
-             (cons (cons major (list (cons minor info)))
-                   (symbol-value storage)))
-      (let ((cur-minor (assoc minor old-major)))
-       (cond
-        ((or (null cur-minor)          ; New minor area, or
-             (assq 'test info))        ; Has a test, insert at beginning
-         (setcdr old-major
-                  (cons (cons minor info) (cdr old-major))))
-        ((and (not (assq 'test info))  ; No test info, replace completely
-              (not (assq 'test cur-minor))
-              (equal (assq 'viewer info)  ; Keep alternative viewer
-                     (assq 'viewer cur-minor)))
-         (setcdr cur-minor info))
-        (t
-         (setcdr old-major
-                  (setcdr old-major
-                          (cons (cons minor info) (cdr old-major))))))))))
+        (major-entry (assoc major (symbol-value storage)))
+        (new-minor-entry (cons minor info))
+        minor-entry)
+    (cond
+     ((null major-entry)
+      ;; Add a new major entry containing the new minor entry.
+      (setf major-entry (list major new-minor-entry))
+      (push major-entry (symbol-value storage)))
+     ((and (setf minor-entry (assoc minor major-entry))
+          (not (assq 'test info))
+          (not (assq 'test minor-entry))
+          (equal (assq 'viewer info)
+                 (assq 'viewer minor-entry)))
+      ;; Replace a previous MINOR entry if it and the entry to be
+      ;; added both do *not* have a ‘test’ associated in their info
+      ;; alist and both use the same ‘viewer’ command.  This ignores
+      ;; other fields in the previous entryʼs info alist: they will be
+      ;; lost when the info alist in the cdr of the previous entry is
+      ;; replaced with the new INFO alist.
+      (setf (cdr minor-entry) info))
+     (t
+      ;; Add the new minor entry to the existing major entry.
+      (push new-minor-entry (cdr major-entry))))))
 
 (defun mailcap-add (type viewer &optional test)
   "Add VIEWER as a handler for TYPE.
@@ -963,7 +979,7 @@ If NO-DECODE is non-nil, don't decode STRING."
     (".vox"   . "audio/basic")
     (".vrml"  . "x-world/x-vrml")
     (".wav"   . "audio/x-wav")
-    (".xls"   . "application/vnd.ms-excel")
+    (".xls"   . "application/vnd\\.ms-excel")
     (".wrl"   . "x-world/x-vrml")
     (".xbm"   . "image/xbm")
     (".xpm"   . "image/xpm")
@@ -1035,7 +1051,8 @@ If FORCE, re-parse even if already parsed."
        (setq save-pos (point))
        (skip-chars-forward "^ \t\n")
        (downcase-region save-pos (point))
-       (setq type (buffer-substring save-pos (point)))
+       (setq type (mailcap--regexp-quote-type
+                    (buffer-substring save-pos (point))))
        (while (not (eolp))
          (skip-chars-forward " \t")
          (setq save-pos (point))
@@ -1048,6 +1065,12 @@ If FORCE, re-parse even if already parsed."
         (setq mailcap-mime-extensions (append extns mailcap-mime-extensions)
               extns nil)))))
 
+(defun mailcap--regexp-quote-type (type)
+  (if (not (string-search "/" type))
+      type
+    (pcase-let ((`(,major ,minor) (split-string type "/")))
+      (concat major "/" (regexp-quote minor)))))
+
 (defun mailcap-extension-to-mime (extn)
   "Return the MIME content type of the file extensions EXTN."
   (mailcap-parse-mimetypes)
diff --git a/lisp/net/pop3.el b/lisp/net/pop3.el
index 9d59ddf978..1bd52c7a56 100644
--- a/lisp/net/pop3.el
+++ b/lisp/net/pop3.el
@@ -23,6 +23,8 @@
 
 ;;; Commentary:
 
+;; Post Office Protocol version 3 (RFC 1460) interface.
+;;
 ;; Most of the standard Post Office Protocol version 3 (RFC 1460) commands
 ;; are implemented.  The LIST command has not been implemented due to lack
 ;; of actual usefulness.
diff --git a/lisp/net/rcirc.el b/lisp/net/rcirc.el
index 71505dcaa3..fa481ce528 100644
--- a/lisp/net/rcirc.el
+++ b/lisp/net/rcirc.el
@@ -116,7 +116,7 @@ VALUE must be a string that will be used instead of the 
server
 name for display purposes.  If absent, the real server name will
 be displayed instead."
   :type '(alist :key-type string
-               :value-type (plist :options
+                :value-type (plist :options
                                    ((:nick string)
                                     (:port integer)
                                     (:user-name string)
@@ -167,7 +167,7 @@ If a function (e.g., `frame-text-width' or 
`window-text-width'),
 call it to compute the number of columns."
   :risky t                              ; can get funcalled
   :type '(choice (const :tag "Value of `fill-column'" nil)
-                (integer :tag "Number of columns")
+                 (integer :tag "Number of columns")
                  (function :tag "Function returning the number of columns")))
 
 (defcustom rcirc-fill-prefix nil
@@ -175,7 +175,7 @@ call it to compute the number of columns."
 If nil, calculate the prefix dynamically to line up text
 underneath each nick."
   :type '(choice (const :tag "Dynamic" nil)
-                (string :tag "Prefix text")))
+                 (string :tag "Prefix text")))
 
 (defcustom rcirc-url-max-length nil
   "Maximum number of characters in displayed URLs.
@@ -273,19 +273,19 @@ Examples:
   (\"quakenet.org\" quakenet \"bobby\" \"sekrit\")
   (\"oftc\" sasl \"bob\" \"hunter2\"))"
   :type '(alist :key-type (regexp :tag "Server")
-               :value-type (choice (list :tag "NickServ"
-                                         (const nickserv)
-                                         (string :tag "Nick")
-                                         (string :tag "Password"))
-                                   (list :tag "ChanServ"
-                                         (const chanserv)
-                                         (string :tag "Nick")
-                                         (string :tag "Channel")
-                                         (string :tag "Password"))
-                                   (list :tag "BitlBee"
-                                         (const bitlbee)
-                                         (string :tag "Nick")
-                                         (string :tag "Password"))
+                :value-type (choice (list :tag "NickServ"
+                                          (const nickserv)
+                                          (string :tag "Nick")
+                                          (string :tag "Password"))
+                                    (list :tag "ChanServ"
+                                          (const chanserv)
+                                          (string :tag "Nick")
+                                          (string :tag "Channel")
+                                          (string :tag "Password"))
+                                    (list :tag "BitlBee"
+                                          (const bitlbee)
+                                          (string :tag "Nick")
+                                          (string :tag "Password"))
                                     (list :tag "QuakeNet"
                                           (const quakenet)
                                           (string :tag "Account")
@@ -350,8 +350,6 @@ See `rcirc-bright-nick' face."
 See `rcirc-dim-nick' face."
   :type '(repeat string))
 
-(define-obsolete-variable-alias 'rcirc-print-hooks
-  'rcirc-print-functions "24.3")
 (defcustom rcirc-print-functions nil
   "Hook run after text is printed.
 Called with 5 arguments, PROCESS, SENDER, RESPONSE, TARGET and TEXT."
@@ -388,10 +386,10 @@ messages.
 If VAL is a cons of coding systems, the car part is used for decoding,
 and the cdr part is used for encoding."
   :type '(alist :key-type (choice (regexp :tag "Channel Regexp")
-                                         (cons (regexp :tag "Channel Regexp")
-                                               (regexp :tag "Server Regexp")))
-               :value-type (choice coding-system
-                                   (cons (coding-system :tag "Decode")
+                                  (cons (regexp :tag "Channel Regexp")
+                                        (regexp :tag "Server Regexp")))
+                :value-type (choice coding-system
+                                    (cons (coding-system :tag "Decode")
                                           (coding-system :tag "Encode")))))
 
 (defcustom rcirc-multiline-major-mode 'fundamental-mode
@@ -520,50 +518,50 @@ If ARG is non-nil, instead prompt for connection 
parameters."
   (interactive "P")
   (if arg
       (let* ((server (completing-read "IRC Server: "
-                                     rcirc-server-alist
-                                     nil nil
-                                     (caar rcirc-server-alist)
-                                     'rcirc-server-name-history))
-            (server-plist (cdr (assoc-string server rcirc-server-alist)))
-            (port (read-string "IRC Port: "
-                               (number-to-string
-                                (or (plist-get server-plist :port)
-                                    rcirc-default-port))
-                               'rcirc-server-port-history))
-            (nick (read-string "IRC Nick: "
-                               (or (plist-get server-plist :nick)
-                                   rcirc-default-nick)
-                               'rcirc-nick-name-history))
-            (user-name (read-string "IRC Username: "
+                                      rcirc-server-alist
+                                      nil nil
+                                      (caar rcirc-server-alist)
+                                      'rcirc-server-name-history))
+             (server-plist (cdr (assoc-string server rcirc-server-alist)))
+             (port (read-string "IRC Port: "
+                                (number-to-string
+                                 (or (plist-get server-plist :port)
+                                     rcirc-default-port))
+                                'rcirc-server-port-history))
+             (nick (read-string "IRC Nick: "
+                                (or (plist-get server-plist :nick)
+                                    rcirc-default-nick)
+                                'rcirc-nick-name-history))
+             (user-name (read-string "IRC Username: "
                                      (or (plist-get server-plist :user-name)
                                          rcirc-default-user-name)
                                      'rcirc-user-name-history))
-            (password (read-passwd "IRC Password: " nil
+             (password (read-passwd "IRC Password: " nil
                                     (plist-get server-plist :password)))
-            (channels (split-string
-                       (read-string "IRC Channels: "
-                                    (mapconcat 'identity
-                                               (plist-get server-plist
-                                                          :channels)
-                                               " "))
-                       "[, ]+" t))
+             (channels (split-string
+                        (read-string "IRC Channels: "
+                                     (mapconcat 'identity
+                                                (plist-get server-plist
+                                                           :channels)
+                                                " "))
+                        "[, ]+" t))
              (encryption (rcirc-prompt-for-encryption server-plist))
              (process (rcirc-connect server port nick user-name
-                                    rcirc-default-full-name
-                                    channels password encryption)))
-       (when rcirc-display-server-buffer
+                                     rcirc-default-full-name
+                                     channels password encryption)))
+        (when rcirc-display-server-buffer
           (pop-to-buffer-same-window (process-buffer process))))
     ;; connect to servers in `rcirc-server-alist'
     (let (connected-servers)
       (dolist (c rcirc-server-alist)
-       (let ((server (car c))
-             (nick (or (plist-get (cdr c) :nick) rcirc-default-nick))
-             (port (or (plist-get (cdr c) :port) rcirc-default-port))
-             (user-name (or (plist-get (cdr c) :user-name)
-                            rcirc-default-user-name))
-             (full-name (or (plist-get (cdr c) :full-name)
-                            rcirc-default-full-name))
-             (channels (plist-get (cdr c) :channels))
+        (let ((server (car c))
+              (nick (or (plist-get (cdr c) :nick) rcirc-default-nick))
+              (port (or (plist-get (cdr c) :port) rcirc-default-port))
+              (user-name (or (plist-get (cdr c) :user-name)
+                             rcirc-default-user-name))
+              (full-name (or (plist-get (cdr c) :full-name)
+                             rcirc-default-full-name))
+              (channels (plist-get (cdr c) :channels))
               (password (plist-get (cdr c) :password))
               (encryption (plist-get (cdr c) :encryption))
               (server-alias (plist-get (cdr c) :server-alias))
@@ -577,21 +575,21 @@ If ARG is non-nil, instead prompt for connection 
parameters."
                                                :port port))
                      (pwd (auth-info-password (car auth))))
             (setq password pwd))
-         (when server
-           (let (connected)
-             (dolist (p (rcirc-process-list))
-               (when (string= (or server-alias server) (process-name p))
-                 (setq connected p)))
-             (if (not connected)
-                 (condition-case nil
-                     (let ((process (rcirc-connect server port nick user-name
+          (when server
+            (let (connected)
+              (dolist (p (rcirc-process-list))
+                (when (string= (or server-alias server) (process-name p))
+                  (setq connected p)))
+              (if (not connected)
+                  (condition-case nil
+                      (let ((process (rcirc-connect server port nick user-name
                                                     full-name channels 
password encryption
                                                     client-cert server-alias)))
                         (when rcirc-display-server-buffer
                           (pop-to-buffer-same-window (process-buffer 
process))))
-                   (quit (message "Quit connecting to %s"
+                    (quit (message "Quit connecting to %s"
                                    (or server-alias server))))
-               (with-current-buffer (process-buffer connected)
+                (with-current-buffer (process-buffer connected)
                   (setq contact (process-contact
                                  (get-buffer-process (current-buffer)) :name))
                   (setq connected-servers
@@ -599,12 +597,12 @@ If ARG is non-nil, instead prompt for connection 
parameters."
                                   contact (or server-alias server))
                               connected-servers))))))))
       (when connected-servers
-       (message "Already connected to %s"
-                (if (cdr connected-servers)
-                    (concat (mapconcat 'identity (butlast connected-servers) 
", ")
-                            ", and "
-                            (car (last connected-servers)))
-                  (car connected-servers)))))))
+        (message "Already connected to %s"
+                 (if (cdr connected-servers)
+                     (concat (mapconcat 'identity (butlast connected-servers) 
", ")
+                             ", and "
+                             (car (last connected-servers)))
+                   (car connected-servers)))))))
 
 ;;;###autoload
 (defalias 'irc 'rcirc)
@@ -732,7 +730,7 @@ that are joined after authentication."
         (setq rcirc-nick nick)
         (setq rcirc-startup-channels startup-channels)
         (setq rcirc-last-connect-time (current-time))
-       (setq rcirc-last-server-message-time rcirc-last-connect-time)
+        (setq rcirc-last-server-message-time rcirc-last-connect-time)
 
         ;; Check if the immediate process state
         (sit-for .1)
@@ -804,8 +802,8 @@ MESSAGE should contain a timestamp, indicating when the 
KEEPALIVE
 message was generated."
   (with-rcirc-process-buffer process
     (setq header-line-format
-         (format "%f" (float-time
-                       (time-since (string-to-number message)))))))
+          (format "%f" (float-time
+                        (time-since (string-to-number message)))))))
 
 (defvar rcirc-debug-buffer "*rcirc debug*"
   "Buffer name for debugging messages.")
@@ -832,8 +830,6 @@ is moved to after the text inserted.  Otherwise the point 
is not moved."
                   text))
         (goto-char old)))))
 
-(define-obsolete-variable-alias 'rcirc-sentinel-hooks
-  'rcirc-sentinel-functions "24.3")
 (defvar rcirc-sentinel-functions nil
   "Hook functions called when the process sentinel is called.
 Functions are called with PROCESS and SENTINEL arguments.")
@@ -864,19 +860,19 @@ If QUIET is non-nil, no not emit a message."
           (throw 'exit (or quiet (message "Server process is alive")))
         (delete-process process))
       (let ((conn-info rcirc-connection-info))
-       (setf (nth 5 conn-info)
-             (cl-remove-if-not #'rcirc-channel-p
-                               (mapcar #'car rcirc-buffer-alist)))
+        (setf (nth 5 conn-info)
+              (cl-remove-if-not #'rcirc-channel-p
+                                (mapcar #'car rcirc-buffer-alist)))
         (dolist (buffer (mapcar #'cdr rcirc-buffer-alist))
-         (when (buffer-live-p buffer)
+          (when (buffer-live-p buffer)
             (with-current-buffer buffer
-             (setq mode-line-process ":connecting"))))
-       (let ((nprocess (apply #'rcirc-connect conn-info)))
+              (setq mode-line-process ":connecting"))))
+        (let ((nprocess (apply #'rcirc-connect conn-info)))
           (when (and (< rcirc-failed-attempts rcirc-reconnect-attempts)
                      (eq (process-status nprocess) 'failed))
             (setq rcirc-failed-attempts (1+ rcirc-failed-attempts))
             (rcirc-print nprocess "*rcirc*" "ERROR" nil
-                        (format "Failed to reconnect (%d/%d)..."
+                         (format "Failed to reconnect (%d/%d)..."
                                  rcirc-failed-attempts
                                  rcirc-reconnect-attempts))
             (setq rcirc-reconnection-timer
@@ -932,26 +928,26 @@ SENTINEL describes the change in form of a string."
 
           (message "Connecting to %s...done" (or server-alias server))
           (dolist (buffer (cons nil (mapcar 'cdr rcirc-buffer-alist)))
-           (with-current-buffer (or buffer (current-buffer))
-             (setq mode-line-process nil)))))
+            (with-current-buffer (or buffer (current-buffer))
+              (setq mode-line-process nil)))))
        ((eq status 'closed)
         (let ((now (current-time)))
           (with-rcirc-process-buffer process
             (when (and (< 0 rcirc-reconnect-delay)
                        (time-less-p rcirc-reconnect-delay
-                                   (time-subtract now 
rcirc-last-connect-time)))
+                                    (time-subtract now 
rcirc-last-connect-time)))
               (setq rcirc-last-connect-time now)
               (rcirc-reconnect process)))))
        ((eq status 'failed)
         (dolist (buffer (cons nil (mapcar 'cdr rcirc-buffer-alist)))
-         (with-current-buffer (or buffer (current-buffer))
-           (rcirc-print process "*rcirc*" "ERROR" rcirc-target
-                        (format "%s: %s (%S)"
-                                (process-name process)
-                                sentinel
-                                (process-status process))
+          (with-current-buffer (or buffer (current-buffer))
+            (rcirc-print process "*rcirc*" "ERROR" rcirc-target
+                         (format "%s: %s (%S)"
+                                 (process-name process)
+                                 sentinel
+                                 (process-status process))
                          (not rcirc-target))
-           (rcirc-disconnect-buffer)))))
+            (rcirc-disconnect-buffer)))))
       (run-hook-with-args 'rcirc-sentinel-functions process sentinel))))
 
 (defun rcirc-disconnect-buffer (&optional buffer)
@@ -974,8 +970,6 @@ If BUFFER is nil, default to the current buffer."
           (process-list))
     ps))
 
-(define-obsolete-variable-alias 'rcirc-receive-message-hooks
-  'rcirc-receive-message-functions "24.3")
 (defvar rcirc-receive-message-functions nil
   "Hook functions run when a message is received from server.
 Function is called with PROCESS, COMMAND, SENDER, ARGS and LINE.")
@@ -998,10 +992,10 @@ Function is called with PROCESS, COMMAND, SENDER, ARGS 
and LINE.")
   (with-rcirc-process-buffer process
     (when (not rcirc-connecting)
       (with-rcirc-process-buffer process
-       (when rcirc-timeout-timer (cancel-timer rcirc-timeout-timer))
-       (setq rcirc-timeout-timer (run-at-time rcirc-timeout-seconds nil
-                                              'delete-process
-                                              process))))))
+        (when rcirc-timeout-timer (cancel-timer rcirc-timeout-timer))
+        (setq rcirc-timeout-timer (run-at-time rcirc-timeout-seconds nil
+                                               'delete-process
+                                               process))))))
 
 (defvar rcirc-trap-errors-flag t
   "Non-nil means Lisp errors are degraded to error messages.")
@@ -1017,7 +1011,7 @@ Function is called with PROCESS, COMMAND, SENDER, ARGS 
and LINE.")
 
 (defconst rcirc-process-regexp
   (rx-let ((message-tag ; message tags as specified in
-                        ; https://ircv3.net/specs/extensions/message-tags
+                                        ; 
https://ircv3.net/specs/extensions/message-tags
             (: (? "+")
                (? (+ (or alnum "-")) (+ "." (+ (or alnum "-"))) "/")
                (+ (any alnum "-"))
@@ -1098,7 +1092,7 @@ Note that the messages are stored in reverse order.")
                     (split-string tag-data ";"))))
                rcirc-message-tags))
              (user (match-string 3 text))
-            (sender (rcirc-user-nick user))
+             (sender (rcirc-user-nick user))
              (cmd (match-string 4 text))
              (cmd-end (match-end 4))
              (args nil)
@@ -1140,7 +1134,7 @@ found.  PROCESS, SENDER and RESPONSE are passed on to
 used as the message body."
   (rcirc-print process sender response nil
                (mapconcat 'identity (cdr args) " ")
-              (not (member response rcirc-responses-no-activity))))
+               (not (member response rcirc-responses-no-activity))))
 
 (defun rcirc--connection-open-p (process)
   "Check if PROCESS is open or running."
@@ -1186,7 +1180,7 @@ element in PARTS is a list, append it to PARTS."
   "Return the process associated with channel BUFFER.
 With no argument or nil as argument, use the current buffer."
   (let ((buffer (or buffer (and (buffer-live-p rcirc-server-buffer)
-                               rcirc-server-buffer))))
+                                rcirc-server-buffer))))
     (if buffer
         (buffer-local-value 'rcirc-process buffer)
       rcirc-process)))
@@ -1195,7 +1189,7 @@ With no argument or nil as argument, use the current 
buffer."
   "Return PROCESS server name, given by the 001 response."
   (with-rcirc-process-buffer process
     (or rcirc-server-name
-       (warn "server name for process %S unknown" process))))
+        (warn "server name for process %S unknown" process))))
 
 (defun rcirc-nick (process)
   "Return PROCESS nick."
@@ -1220,17 +1214,17 @@ With no argument or nil as argument, use the current 
buffer."
       (insert message)
       (goto-char (point-min))
       (let (result)
-       (while (not (eobp))
-         (goto-char (or (byte-to-position rcirc-max-message-length)
-                        (point-max)))
-         ;; max message length is 512 including CRLF
-         (while (and (not (bobp))
-                     (> (length (encode-coding-region
-                                 (point-min) (point) encoding t))
-                        rcirc-max-message-length))
-           (forward-char -1))
-         (push (delete-and-extract-region (point-min) (point)) result))
-       (nreverse result)))))
+        (while (not (eobp))
+          (goto-char (or (byte-to-position rcirc-max-message-length)
+                         (point-max)))
+          ;; max message length is 512 including CRLF
+          (while (and (not (bobp))
+                      (> (length (encode-coding-region
+                                  (point-min) (point) encoding t))
+                         rcirc-max-message-length))
+            (forward-char -1))
+          (push (delete-and-extract-region (point-min) (point)) result))
+        (nreverse result)))))
 
 (defun rcirc-send-message (process target message &optional noticep silent)
   "Send TARGET associated with PROCESS a privmsg with text MESSAGE.
@@ -1241,7 +1235,7 @@ If SILENT is non-nil, do not print the message in any irc 
buffer."
     (dolist (msg (rcirc-split-message message))
       (rcirc-send-string process response target : msg)
       (unless silent
-       (rcirc-print process (rcirc-nick process) response target msg)))))
+        (rcirc-print process (rcirc-nick process) response target msg)))))
 
 (defvar-local rcirc-input-ring nil
   "Ring object for input.")
@@ -1293,10 +1287,10 @@ The list is updated automatically by 
`defun-rcirc-command'.")
                      ;; On some networks it is common to message or
                      ;; mention someone using @nick instead of just
                      ;; nick.
-                    (if (re-search-backward "[[:space:]@]" 
rcirc-prompt-end-marker t)
-                        (1+ (point))
-                      rcirc-prompt-end-marker)))
-             (table (cond
+                     (if (re-search-backward "[[:space:]@]" 
rcirc-prompt-end-marker t)
+                         (1+ (point))
+                       rcirc-prompt-end-marker)))
+              (table (cond
                       ;; No completion before the prompt
                       ((< beg rcirc-prompt-end-marker) nil)
                       ;; Only complete nicks mid-message
@@ -1304,23 +1298,23 @@ The list is updated automatically by 
`defun-rcirc-command'.")
                        (mapcar rcirc-nick-filter
                                (rcirc-channel-nicks
                                 (rcirc-buffer-process)
-                               rcirc-target)))
+                                rcirc-target)))
                       ;; Complete commands at the beginning of the
                       ;; message, when the first character is a dash
                       ((eq (char-after beg) ?/)
                        (mapcar
                         (lambda (cmd) (concat cmd " "))
                         (nconc (sort (copy-sequence rcirc-client-commands)
-                                    'string-lessp)
-                              (sort (copy-sequence rcirc-server-commands)
-                                    'string-lessp))))
+                                     'string-lessp)
+                               (sort (copy-sequence rcirc-server-commands)
+                                     'string-lessp))))
                       ;; Complete usernames right after the prompt by
                       ;; appending a colon after the name
                       ((mapcar
                         (lambda (str) (concat (funcall rcirc-nick-filter str) 
": "))
                         (rcirc-channel-nicks (rcirc-buffer-process)
-                                            rcirc-target))))))
-        (list beg (point)
+                                             rcirc-target))))))
+         (list beg (point)
                (lambda (str pred action)
                  (if (eq action 'metadata)
                      '(metadata (cycle-sort-function . identity))
@@ -1346,12 +1340,91 @@ The list is updated automatically by 
`defun-rcirc-command'.")
   'set-rcirc-encode-coding-system
   "28.1")
 
+(defun rcirc-format (pre &optional replace)
+  "Insert markup formatting PRE.
+PRE and \"^O\" (ASCII #x0f) will either be inserted around the
+current point respectively or the active region, if present.
+This function an auxiliary function is not meant to be used
+directly, but is invoked by other commands.  If the optional
+argument REPLACE is non-nil, first remove any formatting before
+inserting the new one."
+  (when replace (rcirc-unformat))
+  (save-excursion
+    (if (use-region-p)
+        (let ((beg (region-beginning)))
+          (goto-char (region-end))
+          (insert "")
+          (goto-char beg)
+          (insert pre))
+      (insert pre "")))
+  (when (or (not (region-active-p)) (< (point) (mark)))
+    (forward-char (length pre))))
+
+(defun rcirc-unformat ()
+  "Remove the closes formatting found closes to the current point."
+  (interactive)
+  (save-excursion
+    (when (and (search-backward-regexp (rx (or "" "" "" "" ""))
+                                       rcirc-prompt-end-marker t)
+               (looking-at (rx (group (or "" "" "" "" ""))
+                               (*? nonl)
+                               (group ""))))
+      (replace-match "" nil nil nil 2)
+      (replace-match "" nil nil nil 1))))
+
+(defun rcirc-format-bold (replace)
+  "Insert bold formatting.
+If REPLACE is non-nil or a prefix argument is given, any prior
+formatting will be replaced before the bold formatting is
+inserted."
+  (interactive "P")
+  (rcirc-format "" replace))
+
+(defun rcirc-format-italic (replace)
+  "Insert italic formatting.
+If REPLACE is non-nil or a prefix argument is given, any prior
+formatting will be replaced before the italic formatting is
+inserted."
+  (interactive "P")
+  (rcirc-format "" replace))
+
+(defun rcirc-format-underline (replace)
+  "Insert underlining formatting.
+If REPLACE is non-nil or a prefix argument is given, any prior
+formatting will be replaced before the underline formatting is
+inserted."
+  (interactive "P")
+  (rcirc-format "" replace))
+
+(defun rcirc-format-strike-trough (replace)
+  "Insert strike-trough formatting.
+If REPLACE is non-nil or a prefix argument is given, any prior
+formatting will be replaced before the strike-trough formatting
+is inserted."
+  (interactive "P")
+  (rcirc-format "" replace))
+
+(defun rcirc-format-fixed-width (replace)
+  "Insert fixed-width formatting.
+If REPLACE is non-nil or a prefix argument is given, any prior
+formatting will be replaced before the fixed width formatting is
+inserted."
+  (interactive "P")
+  (rcirc-format "" replace))
+
 (defvar-keymap rcirc-mode-map
   :doc "Keymap for rcirc mode."
   "RET"     #'rcirc-send-input
   "M-p"     #'rcirc-insert-prev-input
   "M-n"     #'rcirc-insert-next-input
   "TAB"     #'completion-at-point
+  "C-c C-f C-b" #'rcirc-format-bold
+  "C-c C-f C-i" #'rcirc-format-italic
+  "C-c C-f C-u" #'rcirc-format-underline
+  "C-c C-f C-s" #'rcirc-format-strike-trough
+  "C-c C-f C-f" #'rcirc-format-fixed-width
+  "C-c C-f C-t" #'rcirc-format-fixed-width ;as in AucTeX
+  "C-c C-f C-d" #'rcirc-unformat
   "C-c C-b" #'rcirc-browse-url
   "C-c C-c" #'rcirc-edit-multiline
   "C-c C-j" #'rcirc-cmd-join
@@ -1416,13 +1489,13 @@ PROCESS is the process object used for communication.
   (setq mode-line-process nil)
 
   (setq rcirc-input-ring
-             ;; If rcirc-input-ring is already a ring with desired
-             ;; size do not re-initialize.
-             (if (and (ring-p rcirc-input-ring)
-                      (= (ring-size rcirc-input-ring)
-                         rcirc-input-ring-size))
-                 rcirc-input-ring
-               (make-ring rcirc-input-ring-size)))
+        ;; If rcirc-input-ring is already a ring with desired
+        ;; size do not re-initialize.
+        (if (and (ring-p rcirc-input-ring)
+                 (= (ring-size rcirc-input-ring)
+                    rcirc-input-ring-size))
+            rcirc-input-ring
+          (make-ring rcirc-input-ring-size)))
   (setq rcirc-server-buffer (process-buffer process))
   (setq rcirc-target target)
   (setq rcirc-last-post-time (current-time))
@@ -1435,19 +1508,19 @@ PROCESS is the process object used for communication.
   (setq buffer-invisibility-spec '())
   (setq buffer-display-table (make-display-table))
   (set-display-table-slot buffer-display-table 4
-                         (let ((glyph (make-glyph-code
-                                       ?. 'font-lock-keyword-face)))
-                           (make-vector 3 glyph)))
+                          (let ((glyph (make-glyph-code
+                                        ?. 'font-lock-keyword-face)))
+                            (make-vector 3 glyph)))
 
   (dolist (i rcirc-coding-system-alist)
     (let ((chan (if (consp (car i)) (caar i) (car i)))
-         (serv (if (consp (car i)) (cdar i) "")))
+          (serv (if (consp (car i)) (cdar i) "")))
       (when (and (string-match chan (or target ""))
-                (string-match serv (rcirc-server-name process)))
-       (setq-local rcirc-decode-coding-system
-                   (if (consp (cdr i)) (cadr i) (cdr i)))
+                 (string-match serv (rcirc-server-name process)))
+        (setq-local rcirc-decode-coding-system
+                    (if (consp (cdr i)) (cadr i) (cdr i)))
         (setq-local rcirc-encode-coding-system
-                   (if (consp (cdr i)) (cddr i) (cdr i))))))
+                    (if (consp (cdr i)) (cddr i) (cdr i))))))
 
   ;; setup the prompt and markers
   (setq rcirc-prompt-start-marker (point-max-marker))
@@ -1463,7 +1536,7 @@ PROCESS is the process object used for communication.
   (add-hook 'kill-buffer-hook 'rcirc-kill-buffer-hook nil t)
 
   ;; add to buffer list, and update buffer abbrevs
-  (when target                         ; skip server buffer
+  (when target                          ; skip server buffer
     (let ((buffer (current-buffer)))
       (with-rcirc-process-buffer process
         (push (cons target buffer) rcirc-buffer-alist)))
@@ -1485,41 +1558,41 @@ PROCESS is the process object used for communication.
 If ALL is non-nil, update prompts in all IRC buffers."
   (if all
       (mapc (lambda (process)
-             (mapc (lambda (buffer)
-                     (with-current-buffer buffer
-                       (rcirc-update-prompt)))
-                   (with-rcirc-process-buffer process
-                     (mapcar 'cdr rcirc-buffer-alist))))
-           (rcirc-process-list))
+              (mapc (lambda (buffer)
+                      (with-current-buffer buffer
+                        (rcirc-update-prompt)))
+                    (with-rcirc-process-buffer process
+                      (mapcar 'cdr rcirc-buffer-alist))))
+            (rcirc-process-list))
     (let ((inhibit-read-only t)
-         (prompt (or rcirc-prompt "")))
+          (prompt (or rcirc-prompt "")))
       (mapc (lambda (rep)
-             (setq prompt
-                   (replace-regexp-in-string (car rep) (cdr rep) prompt)))
-           (list (cons "%n" (rcirc-buffer-nick))
-                 (cons "%s" (with-rcirc-server-buffer rcirc-server-name))
-                 (cons "%t" (or rcirc-target ""))))
+              (setq prompt
+                    (replace-regexp-in-string (car rep) (cdr rep) prompt)))
+            (list (cons "%n" (rcirc-buffer-nick))
+                  (cons "%s" (with-rcirc-server-buffer rcirc-server-name))
+                  (cons "%t" (or rcirc-target ""))))
       (save-excursion
-       (delete-region rcirc-prompt-start-marker rcirc-prompt-end-marker)
-       (goto-char rcirc-prompt-start-marker)
-       (let ((start (point)))
-         (insert-before-markers prompt)
-         (set-marker rcirc-prompt-start-marker start)
-         (when (not (zerop (- rcirc-prompt-end-marker
-                              rcirc-prompt-start-marker)))
-           (add-text-properties rcirc-prompt-start-marker
-                                rcirc-prompt-end-marker
-                                (list 'face 'rcirc-prompt
-                                      'read-only t 'field t
-                                      'front-sticky t 'rear-nonsticky t))))))))
+        (delete-region rcirc-prompt-start-marker rcirc-prompt-end-marker)
+        (goto-char rcirc-prompt-start-marker)
+        (let ((start (point)))
+          (insert-before-markers prompt)
+          (set-marker rcirc-prompt-start-marker start)
+          (when (not (zerop (- rcirc-prompt-end-marker
+                               rcirc-prompt-start-marker)))
+            (add-text-properties rcirc-prompt-start-marker
+                                 rcirc-prompt-end-marker
+                                 (list 'face 'rcirc-prompt
+                                       'read-only t 'field t
+                                       'front-sticky t 'rear-nonsticky 
t))))))))
 
 (defun rcirc-set-changed (option value)
   "Set OPTION to VALUE and update after a customization change."
   (set-default option value)
   (cond ((eq option 'rcirc-prompt)
-        (rcirc-update-prompt 'all))
-       (t
-        (error "Bad option %s" option))))
+         (rcirc-update-prompt 'all))
+        (t
+         (error "Bad option %s" option))))
 
 (defun rcirc-channel-p (target)
   "Return t if TARGET is a channel name."
@@ -1554,7 +1627,7 @@ with it."
     (when (and rcirc-buffer-alist ;; it's a server buffer
                rcirc-kill-channel-buffers)
       (dolist (channel rcirc-buffer-alist)
-       (kill-buffer (cdr channel))))))
+        (kill-buffer (cdr channel))))))
 
 (defun rcirc-change-major-mode-hook ()
   "Part the channel when changing the major mode."
@@ -1565,18 +1638,18 @@ with it."
   (let ((buffer (current-buffer)))
     (rcirc-clear-activity buffer)
     (when (and (rcirc-buffer-process)
-              (rcirc--connection-open-p (rcirc-buffer-process)))
+               (rcirc--connection-open-p (rcirc-buffer-process)))
       (with-rcirc-server-buffer
-       (setq rcirc-buffer-alist
-            (rassq-delete-all buffer rcirc-buffer-alist)))
+        (setq rcirc-buffer-alist
+              (rassq-delete-all buffer rcirc-buffer-alist)))
       (rcirc-update-short-buffer-names)
       (if (rcirc-channel-p rcirc-target)
-         (rcirc-send-string (rcirc-buffer-process)
+          (rcirc-send-string (rcirc-buffer-process)
                              "PART" rcirc-target : reason)
-       (when rcirc-target
-         (rcirc-remove-nick-channel (rcirc-buffer-process)
-                                    (rcirc-buffer-nick)
-                                    rcirc-target))))
+        (when rcirc-target
+          (rcirc-remove-nick-channel (rcirc-buffer-process)
+                                     (rcirc-buffer-nick)
+                                     rcirc-target))))
     (setq rcirc-target nil)))
 
 (defun rcirc-generate-new-buffer-name (process target)
@@ -1594,30 +1667,30 @@ If optional argument SERVER is non-nil, return the 
server buffer
 if there is no existing buffer for TARGET, otherwise return nil."
   (with-rcirc-process-buffer process
     (if (null target)
-       (current-buffer)
+        (current-buffer)
       (let ((buffer (cdr (assoc-string target rcirc-buffer-alist t))))
-       (or buffer (when server (current-buffer)))))))
+        (or buffer (when server (current-buffer)))))))
 
 (defun rcirc-get-buffer-create (process target)
   "Return the buffer associated with the PROCESS and TARGET.
 Create the buffer if it doesn't exist."
   (let ((buffer (rcirc-get-buffer process target)))
     (if (and buffer (buffer-live-p buffer))
-       (with-current-buffer buffer
-         (when (not rcirc-target)
-           (setq rcirc-target target))
-         buffer)
+        (with-current-buffer buffer
+          (when (not rcirc-target)
+            (setq rcirc-target target))
+          buffer)
       ;; create the buffer
       (with-rcirc-process-buffer process
-       (let ((new-buffer (get-buffer-create
-                          (rcirc-generate-new-buffer-name process target))))
-         (with-current-buffer new-buffer
+        (let ((new-buffer (get-buffer-create
+                           (rcirc-generate-new-buffer-name process target))))
+          (with-current-buffer new-buffer
             (unless (eq major-mode 'rcirc-mode)
-             (rcirc-mode process target))
+              (rcirc-mode process target))
             (setq mode-line-process nil))
-         (rcirc-put-nick-channel process (rcirc-nick process) target
-                                 rcirc-current-line)
-         new-buffer)))))
+          (rcirc-put-nick-channel process (rcirc-nick process) target
+                                  rcirc-current-line)
+          new-buffer)))))
 
 (defun rcirc-send-input ()
   "Send input to target associated with the current buffer."
@@ -1625,31 +1698,31 @@ Create the buffer if it doesn't exist."
   (if (< (point) rcirc-prompt-end-marker)
       ;; copy the line down to the input area
       (progn
-       (forward-line 0)
-       (let ((start (if (eq (point) (point-min))
-                        (point)
-                      (if (get-text-property (1- (point)) 'hard)
-                          (point)
-                        (previous-single-property-change (point) 'hard))))
-             (end (next-single-property-change (1+ (point)) 'hard)))
-         (goto-char (point-max))
-         (insert (replace-regexp-in-string
-                  "\n\\s-+" " "
-                  (buffer-substring-no-properties start end)))))
+        (forward-line 0)
+        (let ((start (if (eq (point) (point-min))
+                         (point)
+                       (if (get-text-property (1- (point)) 'hard)
+                           (point)
+                         (previous-single-property-change (point) 'hard))))
+              (end (next-single-property-change (1+ (point)) 'hard)))
+          (goto-char (point-max))
+          (insert (replace-regexp-in-string
+                   "\n\\s-+" " "
+                   (buffer-substring-no-properties start end)))))
     ;; process input
     (goto-char (point-max))
     (when (not (equal 0 (- (point) rcirc-prompt-end-marker)))
       ;; delete a trailing newline
       (when (eq (point) (line-beginning-position))
-       (delete-char -1))
+        (delete-char -1))
       (let ((input (buffer-substring-no-properties
-                   rcirc-prompt-end-marker (point))))
-       (dolist (line (split-string input "\n"))
-         (rcirc-process-input-line line))
-       ;; add to input-ring
-       (save-excursion
-         (ring-insert rcirc-input-ring input)
-         (setq rcirc-input-ring-index 0))))))
+                    rcirc-prompt-end-marker (point))))
+        (dolist (line (split-string input "\n"))
+          (rcirc-process-input-line line))
+        ;; add to input-ring
+        (save-excursion
+          (ring-insert rcirc-input-ring input)
+          (setq rcirc-input-ring-index 0))))))
 
 (defun rcirc-fill-paragraph (&optional justify)
   "Implementation for `fill-paragraph-function'.
@@ -1659,14 +1732,14 @@ The argument JUSTIFY is passed on to `fill-region'."
     (save-restriction
       (narrow-to-region rcirc-prompt-end-marker (point-max))
       (let ((fill-column rcirc-max-message-length))
-       (fill-region (point-min) (point-max) justify)))))
+        (fill-region (point-min) (point-max) justify)))))
 
 (defun rcirc-process-input-line (line)
   "Process LINE as a message or a command."
   (if (string-match "^/\\([^/ ][^ ]*\\) ?\\(.*\\)$" line)
       (rcirc-process-command (match-string 1 line)
-                            (match-string 2 line)
-                            line)
+                             (match-string 2 line)
+                             line)
     (rcirc-process-message line)))
 
 (defun rcirc-process-message (line)
@@ -1687,19 +1760,19 @@ The argument JUSTIFY is passed on to `fill-region'."
 LINE is the raw input, from which COMMAND and ARGS was
 extracted."
   (let ((fun (intern-soft (concat "rcirc-cmd-" command)))
-       (process (rcirc-buffer-process)))
+        (process (rcirc-buffer-process)))
     (newline)
     (with-current-buffer (current-buffer)
       (delete-region rcirc-prompt-end-marker (point))
       (if (string= command "me")
-         (rcirc-print process (rcirc-buffer-nick)
-                      "ACTION" rcirc-target args)
-       (rcirc-print process (rcirc-buffer-nick)
-                    "COMMAND" rcirc-target line))
+          (rcirc-print process (rcirc-buffer-nick)
+                       "ACTION" rcirc-target args)
+        (rcirc-print process (rcirc-buffer-nick)
+                     "COMMAND" rcirc-target line))
       (set-marker rcirc-prompt-end-marker (point))
       (if (fboundp fun)
-         (funcall fun args process rcirc-target)
-       (rcirc-send-string process command : args)))))
+          (funcall fun args process rcirc-target)
+        (rcirc-send-string process command : args)))))
 
 (defvar-local rcirc-parent-buffer nil
   "Message buffer that requested a multiline buffer.")
@@ -1714,7 +1787,7 @@ extracted."
   (let ((pos (1+ (- (point) rcirc-prompt-end-marker))))
     (goto-char (point-max))
     (let ((text (buffer-substring-no-properties rcirc-prompt-end-marker
-                                               (point)))
+                                                (point)))
           (parent (buffer-name)))
       (delete-region rcirc-prompt-end-marker (point))
       (setq rcirc-window-configuration (current-window-configuration))
@@ -1731,6 +1804,13 @@ extracted."
 
 (defvar-keymap rcirc-multiline-minor-mode-map
   :doc "Keymap for multiline mode in rcirc."
+  "C-c C-f C-b" #'rcirc-format-bold
+  "C-c C-f C-i" #'rcirc-format-italic
+  "C-c C-f C-u" #'rcirc-format-underline
+  "C-c C-f C-s" #'rcirc-format-strike-trough
+  "C-c C-f C-f" #'rcirc-format-fixed-width
+  "C-c C-f C-t" #'rcirc-format-fixed-width ;as in AucTeX
+  "C-c C-f C-d" #'rcirc-unformat
   "C-c C-c"     #'rcirc-multiline-minor-submit
   "C-x C-s"     #'rcirc-multiline-minor-submit
   "C-c C-k"     #'rcirc-multiline-minor-cancel
@@ -1768,11 +1848,11 @@ extracted."
       (process-buffer process)
     (let ((buffer (window-buffer)))
       (if (and buffer
-              (with-current-buffer buffer
-                (and (eq major-mode 'rcirc-mode)
-                     (eq (rcirc-buffer-process) process))))
-         buffer
-       (process-buffer process)))))
+               (with-current-buffer buffer
+                 (and (eq major-mode 'rcirc-mode)
+                      (eq (rcirc-buffer-process) process))))
+          buffer
+        (process-buffer process)))))
 
 (defcustom rcirc-response-formats
   '(("PRIVMSG" . "<%N> %m")
@@ -1803,7 +1883,7 @@ the of the following escape sequences replaced by the 
described values:
   %f-       Following text uses the default face
   %%        A literal `%' character"
   :type '(alist :key-type (choice (string :tag "Type")
-                                 (const :tag "Default" t))
+                                  (const :tag "Default" t))
                 :value-type string))
 
 (defun rcirc-format-response-string (process sender response target text)
@@ -1813,55 +1893,55 @@ The specific formatting used is found by looking up 
RESPONSE in
 communication."
   (with-temp-buffer
     (insert (or (cdr (assoc response rcirc-response-formats))
-               (cdr (assq t rcirc-response-formats))))
+                (cdr (assq t rcirc-response-formats))))
     (goto-char (point-min))
     (let ((start (point-min))
-         (sender (if (or (not sender)
-                         (string= (rcirc-server-name process) sender))
-                     ""
-                   (funcall rcirc-nick-filter sender)))
-         face)
+          (sender (if (or (not sender)
+                          (string= (rcirc-server-name process) sender))
+                      ""
+                    (funcall rcirc-nick-filter sender)))
+          face)
       (while (re-search-forward "%\\(\\(f\\(.\\)\\)\\|\\(.\\)\\)" nil t)
-       (rcirc-add-face start (match-beginning 0) face)
-       (setq start (match-beginning 0))
-       (replace-match
-        (cl-case (aref (match-string 1) 0)
-           (?f (setq face
-                     (cl-case (string-to-char (match-string 3))
-                       (?w 'font-lock-warning-face)
-                       (?p 'rcirc-server-prefix)
-                       (?s 'rcirc-server)
-                       (t nil)))
-               "")
-           (?n sender)
-           (?N (let ((my-nick (rcirc-nick process)))
-                 (save-match-data
-                   (with-syntax-table rcirc-nick-syntax-table
-                     (rcirc-facify sender
-                                   (cond ((string= sender my-nick)
-                                          'rcirc-my-nick)
-                                         ((and rcirc-bright-nicks
-                                               (string-match
-                                                (regexp-opt rcirc-bright-nicks
-                                                            'words)
-                                                sender))
-                                          'rcirc-bright-nick)
-                                         ((and rcirc-dim-nicks
-                                               (string-match
-                                                (regexp-opt rcirc-dim-nicks
-                                                            'words)
-                                                sender))
-                                          'rcirc-dim-nick)
-                                         (t
-                                          'rcirc-other-nick)))))))
-           (?m (propertize text 'rcirc-text text))
-           (?r response)
-           (?t (or target ""))
-           (t (concat "UNKNOWN CODE:" (match-string 0))))
-        t t nil 0)
-       (rcirc-add-face (match-beginning 0) (match-end 0) face))
+        (rcirc-add-face start (match-beginning 0) face)
+        (setq start (match-beginning 0))
+        (replace-match
+         (cl-case (aref (match-string 1) 0)
+           (?f (setq face
+                     (cl-case (string-to-char (match-string 3))
+                       (?w 'font-lock-warning-face)
+                       (?p 'rcirc-server-prefix)
+                       (?s 'rcirc-server)
+                       (t nil)))
+               "")
+           (?n sender)
+           (?N (let ((my-nick (rcirc-nick process)))
+                 (save-match-data
+                   (with-syntax-table rcirc-nick-syntax-table
+                     (rcirc-facify sender
+                                   (cond ((string= sender my-nick)
+                                          'rcirc-my-nick)
+                                         ((and rcirc-bright-nicks
+                                               (string-match
+                                                (regexp-opt rcirc-bright-nicks
+                                                            'words)
+                                                sender))
+                                          'rcirc-bright-nick)
+                                         ((and rcirc-dim-nicks
+                                               (string-match
+                                                (regexp-opt rcirc-dim-nicks
+                                                            'words)
+                                                sender))
+                                          'rcirc-dim-nick)
+                                         (t
+                                          'rcirc-other-nick)))))))
+           (?m (propertize text 'rcirc-text text))
+           (?r response)
+           (?t (or target ""))
+           (t (concat "UNKNOWN CODE:" (match-string 0))))
+         t t nil 0)
+        (rcirc-add-face (match-beginning 0) (match-end 0) face))
       (rcirc-add-face start (match-beginning 0) face))
-      (buffer-substring (point-min) (point-max))))
+    (buffer-substring (point-min) (point-max))))
 
 (defun rcirc-target-buffer (process sender response target _text)
   "Return a buffer to print the server response from SENDER.
@@ -1869,17 +1949,17 @@ PROCESS is the process object for the current 
connection."
   (cl-assert (not (bufferp target)))
   (with-rcirc-process-buffer process
     (cond ((not target)
-          (rcirc-any-buffer process))
-         ((not (rcirc-channel-p target))
-          ;; message from another user
-          (if (or (string= response "PRIVMSG")
-                  (string= response "ACTION"))
-              (rcirc-get-buffer-create process (if (string= sender rcirc-nick)
-                                                   target
-                                                 sender))
-            (rcirc-get-buffer process target t)))
-         ((or (rcirc-get-buffer process target)
-              (rcirc-any-buffer process))))))
+           (rcirc-any-buffer process))
+          ((not (rcirc-channel-p target))
+           ;; message from another user
+           (if (or (string= response "PRIVMSG")
+                   (string= response "ACTION"))
+               (rcirc-get-buffer-create process (if (string= sender rcirc-nick)
+                                                    target
+                                                  sender))
+             (rcirc-get-buffer process target t)))
+          ((or (rcirc-get-buffer process target)
+               (rcirc-any-buffer process))))))
 
 (defvar-local rcirc-last-sender nil)
 (defvar-local rcirc-activity-types nil
@@ -1908,11 +1988,11 @@ PROCESS is the process object for the current 
connection."
   "Return the line from the last activity from NICK in TARGET.
 PROCESS is the process object for the current connection."
   (let ((line (or (cdr (assoc-string target
-                                    (gethash nick (with-rcirc-server-buffer
-                                                    rcirc-nick-table)) t))
-                 (rcirc-last-quit-line process nick target))))
+                                     (gethash nick (with-rcirc-server-buffer
+                                                     rcirc-nick-table)) t))
+                  (rcirc-last-quit-line process nick target))))
     (if line
-       line
+        line
       ;;(message "line is nil for %s in %s" nick target)
       nil)))
 
@@ -1921,7 +2001,7 @@ PROCESS is the process object for the current connection."
 PROCESS is the process object for the current connection."
   (let ((last-activity-line (rcirc-last-line process nick target)))
     (when (and last-activity-line
-              (> last-activity-line 0))
+               (> last-activity-line 0))
       (- rcirc-current-line last-activity-line))))
 
 (defvar rcirc-markup-text-functions
@@ -1931,7 +2011,8 @@ PROCESS is the process object for the current connection."
     rcirc-markup-my-nick
     rcirc-markup-urls
     rcirc-markup-keywords
-    rcirc-markup-bright-nicks)
+    rcirc-markup-bright-nicks
+    rcirc-markup-bridge-bots)
   "List of functions used to manipulate text before it is printed.
 
 Each function takes two arguments, SENDER, and RESPONSE.  The
@@ -1945,33 +2026,33 @@ record activity.  PROCESS is the process object for the 
current
 connection."
   (or text (setq text ""))
   (unless (and (or (member sender rcirc-ignore-list)
-                  (member (with-syntax-table rcirc-nick-syntax-table
-                            (when (string-match "^\\([^/]\\w*\\)[:,]" text)
-                              (match-string 1 text)))
-                          rcirc-ignore-list))
-              ;; do not ignore if we sent the message
-              (not (string= sender (rcirc-nick process))))
+                   (member (with-syntax-table rcirc-nick-syntax-table
+                             (when (string-match "^\\([^/]\\w*\\)[:,]" text)
+                               (match-string 1 text)))
+                           rcirc-ignore-list))
+               ;; do not ignore if we sent the message
+               (not (string= sender (rcirc-nick process))))
     (let* ((buffer (rcirc-target-buffer process sender response target text))
            (time (if-let ((time (rcirc-get-tag "time")))
                      (parse-iso8601-time-string time)
                    (current-time)))
-          (inhibit-read-only t))
+           (inhibit-read-only t))
       (with-current-buffer buffer
-       (let ((moving (= (point) rcirc-prompt-end-marker))
-             (old-point (point-marker)))
-
-         (setq text (decode-coding-string text rcirc-decode-coding-system))
-         (unless (string= sender (rcirc-nick process))
-           ;; mark the line with overlay arrow
-           (unless (or (marker-position overlay-arrow-position)
-                       (get-buffer-window (current-buffer))
-                       (member response rcirc-omit-responses))
-             (set-marker overlay-arrow-position
-                         (marker-position rcirc-prompt-start-marker))))
-
-         ;; temporarily set the marker insertion-type because
-         ;; insert-before-markers results in hidden text in new buffers
-         (goto-char rcirc-prompt-start-marker)
+        (let ((moving (= (point) rcirc-prompt-end-marker))
+              (old-point (point-marker)))
+
+          (setq text (decode-coding-string text rcirc-decode-coding-system))
+          (unless (string= sender (rcirc-nick process))
+            ;; mark the line with overlay arrow
+            (unless (or (marker-position overlay-arrow-position)
+                        (get-buffer-window (current-buffer))
+                        (member response rcirc-omit-responses))
+              (set-marker overlay-arrow-position
+                          (marker-position rcirc-prompt-start-marker))))
+
+          ;; temporarily set the marker insertion-type because
+          ;; insert-before-markers results in hidden text in new buffers
+          (goto-char rcirc-prompt-start-marker)
           (catch 'exit
             (while (not (bobp))
               (goto-char (or (previous-single-property-change (point) 'hard)
@@ -1981,8 +2062,8 @@ connection."
                 (next-single-property-change (point) 'hard)
                 (forward-char 1)
                 (throw 'exit nil))))
-         (set-marker-insertion-type rcirc-prompt-start-marker t)
-         (set-marker-insertion-type rcirc-prompt-end-marker t)
+          (set-marker-insertion-type rcirc-prompt-start-marker t)
+          (set-marker-insertion-type rcirc-prompt-end-marker t)
 
           ;; run markup functions
           (cl-assert (bolp))
@@ -1990,32 +2071,29 @@ connection."
             (save-restriction
               (narrow-to-region (point) (point))
               (insert (propertize (rcirc-format-response-string process sender 
response
-                                                              nil text)
-                                'rcirc-msgid (rcirc-get-tag "msgid"))
-                     (propertize "\n" 'hard t))
-
-              ;; squeeze spaces out of text before rcirc-text
-              (fill-region (point-min) (point-max))
+                                                                nil text)
+                                  'rcirc-msgid (rcirc-get-tag "msgid"))
+                      (propertize "\n" 'hard t))
 
               (goto-char (or (next-single-property-change (point-min) 
'rcirc-text)
-                              (point)))
-               (when (rcirc-buffer-process)
-                 (save-excursion (rcirc-markup-timestamp sender response))
-                 (dolist (fn rcirc-markup-text-functions)
-                   (save-excursion (funcall fn sender response)))
-                 (when rcirc-fill-flag
-                   (save-excursion (rcirc-markup-fill sender response))))
-
-               (when rcirc-read-only-flag
-                 (add-text-properties (point-min) (point-max)
+                             (point)))
+              (when (rcirc-buffer-process)
+                (save-excursion (rcirc-markup-timestamp sender response))
+                (dolist (fn rcirc-markup-text-functions)
+                  (save-excursion (funcall fn sender response)))
+                (when rcirc-fill-flag
+                  (save-excursion (rcirc-markup-fill sender response))))
+
+              (when rcirc-read-only-flag
+                (add-text-properties (point-min) (point-max)
                                      '(read-only t front-sticky t)))
 
               (add-text-properties (point-min) (point-max)
                                    (list 'rcirc-time time))
 
               ;; make text omittable
-             (let ((last-activity-lines (rcirc-elapsed-lines process sender 
target)))
-               (if (and (not (string= (rcirc-nick process) sender))
+              (let ((last-activity-lines (rcirc-elapsed-lines process sender 
target)))
+                (if (and (not (string= (rcirc-nick process) sender))
                          (or (member response rcirc-omit-responses)
                              (and (member response rcirc-omit-unless-requested)
                                   (if (member response rcirc-pending-requests)
@@ -2025,50 +2103,50 @@ connection."
                          (or (member response rcirc-omit-unless-requested)
                              (not last-activity-lines)
                              (< rcirc-omit-threshold last-activity-lines)))
-                  (put-text-property (point-min) (point-max)
-                                      'invisible 'rcirc-omit)
-                 ;; otherwise increment the line count
-                 (setq rcirc-current-line (1+ rcirc-current-line))))))
-
-         (set-marker-insertion-type rcirc-prompt-start-marker nil)
-         (set-marker-insertion-type rcirc-prompt-end-marker nil)
-
-         ;; truncate buffer if it is very long
-         (save-excursion
-           (when (and rcirc-buffer-maximum-lines
-                      (> rcirc-buffer-maximum-lines 0)
-                      (= (forward-line (- rcirc-buffer-maximum-lines)) 0))
-             (delete-region (point-min) (point))))
-
-         ;; set the window point for buffers show in windows
-         (walk-windows (lambda (w)
-                         (when (and (not (eq (selected-window) w))
-                                    (eq (current-buffer)
-                                        (window-buffer w))
-                                    (>= (window-point w)
-                                        rcirc-prompt-end-marker))
-                           (set-window-point w (point-max))))
-                       nil t)
-
-         ;; restore the point
-         (goto-char (if moving rcirc-prompt-end-marker old-point)))
-
-         ;; keep window on bottom line if it was already there
-         (when rcirc-scroll-show-maximum-output
-           (let ((window (get-buffer-window)))
-             (when window
-               (with-selected-window window
-                 (when (eq major-mode 'rcirc-mode)
-                   (when (<= (- (window-height)
-                                (count-screen-lines (window-point)
-                                                    (window-start))
-                                1)
-                             0)
-                     (recenter -1)))))))
-
-         ;; flush undo (can we do something smarter here?)
-         (buffer-disable-undo)
-         (buffer-enable-undo)
+                    (put-text-property (point-min) (point-max)
+                                       'invisible 'rcirc-omit)
+                  ;; otherwise increment the line count
+                  (setq rcirc-current-line (1+ rcirc-current-line))))))
+
+          (set-marker-insertion-type rcirc-prompt-start-marker nil)
+          (set-marker-insertion-type rcirc-prompt-end-marker nil)
+
+          ;; truncate buffer if it is very long
+          (save-excursion
+            (when (and rcirc-buffer-maximum-lines
+                       (> rcirc-buffer-maximum-lines 0)
+                       (= (forward-line (- rcirc-buffer-maximum-lines)) 0))
+              (delete-region (point-min) (point))))
+
+          ;; set the window point for buffers show in windows
+          (walk-windows (lambda (w)
+                          (when (and (not (eq (selected-window) w))
+                                     (eq (current-buffer)
+                                         (window-buffer w))
+                                     (>= (window-point w)
+                                         rcirc-prompt-end-marker))
+                            (set-window-point w (point-max))))
+                        nil t)
+
+          ;; restore the point
+          (goto-char (if moving rcirc-prompt-end-marker old-point)))
+
+        ;; keep window on bottom line if it was already there
+        (when rcirc-scroll-show-maximum-output
+          (let ((window (get-buffer-window)))
+            (when window
+              (with-selected-window window
+                (when (eq major-mode 'rcirc-mode)
+                  (when (<= (- (window-height)
+                               (count-screen-lines (window-point)
+                                                   (window-start))
+                               1)
+                            0)
+                    (recenter -1)))))))
+
+        ;; flush undo (can we do something smarter here?)
+        (buffer-disable-undo)
+        (buffer-enable-undo)
 
         ;; record mode line activity
         (when (and activity
@@ -2076,16 +2154,16 @@ connection."
                    (not (and rcirc-dim-nicks sender
                              (string-match (regexp-opt rcirc-dim-nicks) sender)
                              (rcirc-channel-p target))))
-            (rcirc-record-activity (current-buffer)
-                                   (when (not (rcirc-channel-p rcirc-target))
-                                     'nick)))
+          (rcirc-record-activity (current-buffer)
+                                 (when (not (rcirc-channel-p rcirc-target))
+                                   'nick)))
 
         (when (and rcirc-log-flag
                    (or target
                        rcirc-log-process-buffers))
           (rcirc-log process sender response target text))
 
-        (sit-for 0)                    ; displayed text before hook
+        (sit-for 0)                     ; displayed text before hook
         (run-hook-with-args 'rcirc-print-functions
                             process sender response target text)))))
 
@@ -2127,15 +2205,15 @@ disk.  PROCESS is the process object for the current 
connection."
                 (parse-iso8601-time-string time))))
     (unless (null filename)
       (let ((cell (assoc-string filename rcirc-log-alist))
-           (line (concat (format-time-string rcirc-time-format time)
-                         (substring-no-properties
-                          (rcirc-format-response-string process sender
-                                                        response target text))
-                         "\n")))
-       (if cell
-           (setcdr cell (concat (cdr cell) line))
-         (setq rcirc-log-alist
-               (cons (cons filename line) rcirc-log-alist)))))))
+            (line (concat (format-time-string rcirc-time-format time)
+                          (substring-no-properties
+                           (rcirc-format-response-string process sender
+                                                         response target text))
+                          "\n")))
+        (if cell
+            (setcdr cell (concat (cdr cell) line))
+          (setq rcirc-log-alist
+                (cons (cons filename line) rcirc-log-alist)))))))
 
 (defun rcirc-log-write ()
   "Flush `rcirc-log-alist' data to disk.
@@ -2146,11 +2224,11 @@ log-files with absolute names (see 
`rcirc-log-filename-function')."
     (let ((filename (convert-standard-filename
                      (expand-file-name (car cell)
                                        rcirc-log-directory)))
-         (coding-system-for-write 'utf-8))
+          (coding-system-for-write 'utf-8))
       (make-directory (file-name-directory filename) t)
       (with-temp-buffer
-       (insert (cdr cell))
-       (write-region (point-min) (point-max) filename t 'quiet))))
+        (insert (cdr cell))
+        (write-region (point-min) (point-max) filename t 'quiet))))
   (setq rcirc-log-alist nil))
 
 (defun rcirc-view-log-file ()
@@ -2158,8 +2236,8 @@ log-files with absolute names (see 
`rcirc-log-filename-function')."
   (interactive)
   (find-file-other-window
    (expand-file-name (funcall rcirc-log-filename-function
-                             (rcirc-buffer-process) rcirc-target)
-                    rcirc-log-directory)))
+                              (rcirc-buffer-process) rcirc-target)
+                     rcirc-log-directory)))
 
 (defun rcirc-join-channels (process channels)
   "Join CHANNELS.
@@ -2167,7 +2245,7 @@ PROCESS is the process object for the current connection."
   (save-window-excursion
     (dolist (channel channels)
       (with-rcirc-process-buffer process
-       (rcirc-cmd-join channel process)))))
+        (rcirc-cmd-join channel process)))))
 
 ;;; nick management
 (defvar rcirc-nick-prefix-chars '(?~ ?& ?@ ?% ?+)
@@ -2177,9 +2255,9 @@ PROCESS is the process object for the current connection."
   "Return the nick from USER.  Remove any non-nick junk."
   (save-match-data
     (if (string-match (concat "^[" rcirc-nick-prefix-chars
-                             "]*\\([^! ]+\\)!?")
+                              "]*\\([^! ]+\\)!?")
                       (or user ""))
-       (match-string 1 user)
+        (match-string 1 user)
       user)))
 
 (defun rcirc-nick-channels (process nick)
@@ -2187,7 +2265,7 @@ PROCESS is the process object for the current connection."
 PROCESS is the process object for the current connection."
   (with-rcirc-process-buffer process
     (mapcar (lambda (x) (car x))
-           (gethash nick rcirc-nick-table))))
+            (gethash nick rcirc-nick-table))))
 
 (defun rcirc-put-nick-channel (process nick channel &optional line)
   "Add CHANNEL to list associated with NICK.
@@ -2198,12 +2276,12 @@ to zero.  PROCESS is the process object for the current 
connection."
   (let ((nick (rcirc-user-nick nick)))
     (with-rcirc-process-buffer process
       (let* ((chans (gethash nick rcirc-nick-table))
-            (record (assoc-string channel chans t)))
-       (if record
-           (when line (setcdr record line))
-         (puthash nick (cons (cons channel (or line 0))
-                             chans)
-                  rcirc-nick-table))))))
+             (record (assoc-string channel chans t)))
+        (if record
+            (when line (setcdr record line))
+          (puthash nick (cons (cons channel (or line 0))
+                              chans)
+                   rcirc-nick-table))))))
 
 (defun rcirc-nick-remove (process nick)
   "Remove NICK from table.
@@ -2217,33 +2295,36 @@ PROCESS is the process object for the current 
connection."
   (with-rcirc-process-buffer process
     (let* ((chans (gethash nick rcirc-nick-table))
            (newchans
-           ;; instead of assoc-string-delete-all:
-           (let ((record (assoc-string channel chans t)))
-             (when record
-               (setcar record 'delete)
-               (assq-delete-all 'delete chans)))))
+            ;; instead of assoc-string-delete-all:
+            (let ((record (assoc-string channel chans t)))
+              (when record
+                (setcar record 'delete)
+                (assq-delete-all 'delete chans)))))
       (if newchans
           (puthash nick newchans rcirc-nick-table)
         (remhash nick rcirc-nick-table)))))
 
+(defvar rcirc-pseudo-nicks)
 (defun rcirc-channel-nicks (process target)
   "Return the list of nicks associated with TARGET sorted by last activity.
 PROCESS is the process object for the current connection."
   (when target
     (if (rcirc-channel-p target)
-       (with-rcirc-process-buffer process
-         (let (nicks)
-           (maphash
-            (lambda (k v)
-              (let ((record (assoc-string target v t)))
-                (if record
-                    (setq nicks (cons (cons k (cdr record)) nicks)))))
-            rcirc-nick-table)
-           (mapcar (lambda (x) (car x))
-                   (sort nicks (lambda (x y)
-                                 (let ((lx (or (cdr x) 0))
-                                       (ly (or (cdr y) 0)))
-                                   (< ly lx)))))))
+        (let ((pseudo-nicks (mapcar #'list rcirc-pseudo-nicks)))
+          (with-rcirc-process-buffer process
+            (let (nicks)
+              (maphash
+               (lambda (k v)
+                 (let ((record (assoc-string target v t)))
+                   (if record
+                       (setq nicks (cons (cons k (cdr record)) nicks)))))
+               rcirc-nick-table)
+              (mapcar (lambda (x) (car x))
+                      (sort (nconc pseudo-nicks nicks)
+                            (lambda (x y)
+                              (let ((lx (or (cdr x) 0))
+                                    (ly (or (cdr y) 0)))
+                                (< ly lx))))))))
       (list target))))
 
 (defun rcirc-ignore-update-automatic (nick)
@@ -2251,10 +2332,10 @@ PROCESS is the process object for the current 
connection."
 If so, remove from `rcirc-ignore-list'.  PROCESS is the process
 object for the current connection."
   (when (member nick rcirc-ignore-list-automatic)
-      (setq rcirc-ignore-list-automatic
-           (delete nick rcirc-ignore-list-automatic)
-           rcirc-ignore-list
-           (delete nick rcirc-ignore-list))))
+    (setq rcirc-ignore-list-automatic
+          (delete nick rcirc-ignore-list-automatic)
+          rcirc-ignore-list
+          (delete nick rcirc-ignore-list))))
 
 (defun rcirc-nickname< (s1 s2)
   "Return non-nil if IRC nickname S1 is less than S2, and nil otherwise.
@@ -2300,15 +2381,15 @@ This function does not alter the INPUT string."
   ;; toggle the mode-line channel indicator
   (if rcirc-track-minor-mode
       (progn
-       (and (not (memq 'rcirc-activity-string global-mode-string))
-            (setq global-mode-string
-                  (append global-mode-string '(rcirc-activity-string))))
-       (add-hook 'window-configuration-change-hook
-                 'rcirc-window-configuration-change))
+        (and (not (memq 'rcirc-activity-string global-mode-string))
+             (setq global-mode-string
+                   (append global-mode-string '(rcirc-activity-string))))
+        (add-hook 'window-configuration-change-hook
+                  'rcirc-window-configuration-change))
     (setq global-mode-string
-         (delete 'rcirc-activity-string global-mode-string))
+          (delete 'rcirc-activity-string global-mode-string))
     (remove-hook 'window-configuration-change-hook
-                'rcirc-window-configuration-change)))
+                 'rcirc-window-configuration-change)))
 
 (add-to-list 'minor-mode-alist '(rcirc-ignore-buffer-activity-flag " Ignore"))
 (add-to-list 'minor-mode-alist '(rcirc-low-priority-flag " LowPri"))
@@ -2317,20 +2398,20 @@ This function does not alter the INPUT string."
   "Toggle the value of `rcirc-ignore-buffer-activity-flag'."
   (interactive)
   (setq rcirc-ignore-buffer-activity-flag
-       (not rcirc-ignore-buffer-activity-flag))
+        (not rcirc-ignore-buffer-activity-flag))
   (message (if rcirc-ignore-buffer-activity-flag
-              "Ignore activity in this buffer"
-            "Notice activity in this buffer"))
+               "Ignore activity in this buffer"
+             "Notice activity in this buffer"))
   (force-mode-line-update))
 
 (defun rcirc-toggle-low-priority ()
   "Toggle the value of `rcirc-low-priority-flag'."
   (interactive)
   (setq rcirc-low-priority-flag
-       (not rcirc-low-priority-flag))
+        (not rcirc-low-priority-flag))
   (message (if rcirc-low-priority-flag
-              "Activity in this buffer is low priority"
-            "Activity in this buffer is normal priority"))
+               "Activity in this buffer is low priority"
+             "Activity in this buffer is normal priority"))
   (force-mode-line-update))
 
 (defun rcirc-switch-to-server-buffer ()
@@ -2358,14 +2439,14 @@ This function does not alter the INPUT string."
 With prefix ARG, go to the next low priority buffer with activity."
   (interactive "P")
   (let* ((pair (rcirc-split-activity rcirc-activity))
-        (lopri (car pair))
-        (hipri (cdr pair)))
+         (lopri (car pair))
+         (hipri (cdr pair)))
     (if (or (and (not arg) hipri)
-           (and arg lopri))
-       (progn
-         (switch-to-buffer (car (if arg lopri hipri)))
-         (when (> (point) rcirc-prompt-start-marker)
-           (recenter -1)))
+            (and arg lopri))
+        (progn
+          (switch-to-buffer (car (if arg lopri hipri)))
+          (when (> (point) rcirc-prompt-start-marker)
+            (recenter -1)))
       (rcirc-bury-buffers)
       (message "No IRC activity.%s"
                (if lopri
@@ -2375,8 +2456,6 @@ With prefix ARG, go to the next low priority buffer with 
activity."
                  ""))))
   (rcirc-update-activity-string))
 
-(define-obsolete-variable-alias 'rcirc-activity-hooks
-  'rcirc-activity-functions "24.3")
 (defvar rcirc-activity-functions nil
   "Hook to be run when there is channel activity.
 
@@ -2388,21 +2467,21 @@ activity.  Only run if the buffer is not visible and
   "Record BUFFER activity with TYPE."
   (with-current-buffer buffer
     (let ((old-activity rcirc-activity)
-         (old-types rcirc-activity-types))
+          (old-types rcirc-activity-types))
       (when (and (not (get-buffer-window (current-buffer) t))
                  (not (and rcirc-track-ignore-server-buffer-flag
                            (eq rcirc-server-buffer (current-buffer)))))
-       (setq rcirc-activity
-             (sort (if (memq (current-buffer) rcirc-activity) rcirc-activity
+        (setq rcirc-activity
+              (sort (if (memq (current-buffer) rcirc-activity) rcirc-activity
                       (cons (current-buffer) rcirc-activity))
-                   (lambda (b1 b2)
-                     (let ((t1 (buffer-local-value 'rcirc-last-post-time b1))
-                           (t2 (buffer-local-value 'rcirc-last-post-time b2)))
-                       (time-less-p t2 t1)))))
-       (cl-pushnew type rcirc-activity-types)
-       (unless (and (equal rcirc-activity old-activity)
-                    (member type old-types))
-         (rcirc-update-activity-string)))))
+                    (lambda (b1 b2)
+                      (let ((t1 (buffer-local-value 'rcirc-last-post-time b1))
+                            (t2 (buffer-local-value 'rcirc-last-post-time b2)))
+                        (time-less-p t2 t1)))))
+        (cl-pushnew type rcirc-activity-types)
+        (unless (and (equal rcirc-activity old-activity)
+                     (member type old-types))
+          (rcirc-update-activity-string)))))
   (run-hook-with-args 'rcirc-activity-functions buffer))
 
 (defun rcirc-clear-activity (buffer)
@@ -2422,10 +2501,10 @@ activity.  Only run if the buffer is not visible and
   (let (lopri hipri)
     (dolist (buf activity)
       (with-current-buffer buf
-       (if (and rcirc-low-priority-flag
-                (not (member 'nick rcirc-activity-types)))
-           (push buf lopri)
-         (push buf hipri))))
+        (if (and rcirc-low-priority-flag
+                 (not (member 'nick rcirc-activity-types)))
+            (push buf lopri)
+          (push buf hipri))))
     (cons (nreverse lopri) (nreverse hipri))))
 
 (defvar rcirc-update-activity-string-hook nil
@@ -2434,33 +2513,33 @@ activity.  Only run if the buffer is not visible and
 (defun rcirc-update-activity-string ()
   "Update mode-line string."
   (let* ((pair (rcirc-split-activity rcirc-activity))
-        (lopri (car pair))
-        (hipri (cdr pair)))
+         (lopri (car pair))
+         (hipri (cdr pair)))
     (setq rcirc-activity-string
-         (cond ((or hipri lopri)
-                (concat (and hipri "[")
-                        (rcirc-activity-string hipri)
-                        (and hipri lopri ",")
-                        (and lopri
-                             (concat "("
-                                     (rcirc-activity-string lopri)
-                                     ")"))
-                        (and hipri "]")))
-               ((not (null (rcirc-process-list)))
-                "[]")
-               (t "[]")))
+          (cond ((or hipri lopri)
+                 (concat (and hipri "[")
+                         (rcirc-activity-string hipri)
+                         (and hipri lopri ",")
+                         (and lopri
+                              (concat "("
+                                      (rcirc-activity-string lopri)
+                                      ")"))
+                         (and hipri "]")))
+                ((not (null (rcirc-process-list)))
+                 "[]")
+                (t "[]")))
     (run-hooks 'rcirc-update-activity-string-hook)
     (force-mode-line-update t)))
 
 (defun rcirc-activity-string (buffers)
   "Generate activity string for all BUFFERS."
   (mapconcat (lambda (b)
-              (let ((s (substring-no-properties (rcirc-short-buffer-name b))))
-                (with-current-buffer b
-                  (dolist (type rcirc-activity-types)
+               (let ((s (substring-no-properties (rcirc-short-buffer-name b))))
+                 (with-current-buffer b
+                   (dolist (type rcirc-activity-types)
                      (rcirc-facify s (cl-case type
-                                      (nick 'rcirc-track-nick)
-                                      (keyword 'rcirc-track-keyword)))))
+                                       (nick 'rcirc-track-nick)
+                                       (keyword 'rcirc-track-keyword)))))
                  (let ((map (make-mode-line-mouse-map
                              'mouse-1
                              (lambda ()
@@ -2469,7 +2548,7 @@ activity.  Only run if the buffer is not visible and
                    (propertize s
                                'mouse-face 'mode-line-highlight
                                'local-map map))))
-            buffers ","))
+             buffers ","))
 
 (defun rcirc-short-buffer-name (buffer)
   "Return a short name for BUFFER to use in the mode line indicator."
@@ -2485,9 +2564,9 @@ activity.  Only run if the buffer is not visible and
   "Return a list of the visible buffers that are in `rcirc-mode'."
   (let (acc)
     (walk-windows (lambda (w)
-                   (with-current-buffer (window-buffer w)
-                     (when (eq major-mode 'rcirc-mode)
-                       (push (current-buffer) acc)))))
+                    (with-current-buffer (window-buffer w)
+                      (when (eq major-mode 'rcirc-mode)
+                        (push (current-buffer) acc)))))
     acc))
 
 (defvar rcirc-visible-buffers nil
@@ -2501,7 +2580,7 @@ activity.  Only run if the buffer is not visible and
 (defun rcirc-window-configuration-change-1 ()
   "Clear activity and overlay arrows."
   (let* ((old-activity rcirc-activity)
-        (hidden-buffers rcirc-visible-buffers))
+         (hidden-buffers rcirc-visible-buffers))
 
     (setq rcirc-visible-buffers (rcirc-visible-buffers))
 
@@ -2516,8 +2595,8 @@ activity.  Only run if the buffer is not visible and
 
     ;; remove any killed buffers from list
     (setq rcirc-activity
-         (delq nil (mapcar (lambda (buf) (when (buffer-live-p buf) buf))
-                           rcirc-activity)))
+          (delq nil (mapcar (lambda (buf) (when (buffer-live-p buf) buf))
+                            rcirc-activity)))
     ;; update the mode-line string
     (unless (equal old-activity rcirc-activity)
       (rcirc-update-activity-string))))
@@ -2527,14 +2606,14 @@ activity.  Only run if the buffer is not visible and
 (defun rcirc-update-short-buffer-names ()
   "Update variable `rcirc-short-buffer-name' for IRC buffers."
   (let ((bufalist
-        (apply 'append (mapcar (lambda (process)
-                                 (with-rcirc-process-buffer process
-                                   rcirc-buffer-alist))
-                               (rcirc-process-list)))))
+         (apply 'append (mapcar (lambda (process)
+                                  (with-rcirc-process-buffer process
+                                    rcirc-buffer-alist))
+                                (rcirc-process-list)))))
     (dolist (i (rcirc-abbreviate bufalist))
       (when (buffer-live-p (cdr i))
-       (with-current-buffer (cdr i)
-         (setq rcirc-short-buffer-name (car i)))))))
+        (with-current-buffer (cdr i)
+          (setq rcirc-short-buffer-name (car i)))))))
 
 (defun rcirc-abbreviate (pairs)
   "Generate alist of abbreviated buffer names to buffers.
@@ -2548,12 +2627,12 @@ values, from each process."
         acc)
     (dolist (x (cdr tree))
       (if (listp x)
-         (setq acc (append acc
-                          (mapcar (lambda (y)
-                                    (cons (concat ch (car y))
-                                          (cdr y)))
-                                  (rcirc-rebuild-tree x))))
-       (setq acc (cons (cons ch x) acc))))
+          (setq acc (append acc
+                            (mapcar (lambda (y)
+                                      (cons (concat ch (car y))
+                                            (cdr y)))
+                                    (rcirc-rebuild-tree x))))
+        (setq acc (cons (cons ch x) acc))))
     acc))
 
 (defun rcirc-make-trees (pairs)
@@ -2565,31 +2644,31 @@ prefix could be found or another tree if it shares the 
same
 prefix with another element in PAIRS."
   (let (alist)
     (mapc (lambda (pair)
-           (if (consp pair)
-               (let* ((str (car pair))
-                      (data (cdr pair))
-                      (char (unless (zerop (length str))
-                              (aref str 0)))
-                      (rest (unless (zerop (length str))
-                              (substring str 1)))
-                      (part (if char (assq char alist))))
-                 (if part
-                     ;; existing partition
-                     (setcdr part (cons (cons rest data) (cdr part)))
-                   ;; new partition
-                   (setq alist (cons (if char
-                                         (list char (cons rest data))
-                                       data)
-                                     alist))))
-             (setq alist (cons pair alist))))
-         pairs)
+            (if (consp pair)
+                (let* ((str (car pair))
+                       (data (cdr pair))
+                       (char (unless (zerop (length str))
+                               (aref str 0)))
+                       (rest (unless (zerop (length str))
+                               (substring str 1)))
+                       (part (if char (assq char alist))))
+                  (if part
+                      ;; existing partition
+                      (setcdr part (cons (cons rest data) (cdr part)))
+                    ;; new partition
+                    (setq alist (cons (if char
+                                          (list char (cons rest data))
+                                        data)
+                                      alist))))
+              (setq alist (cons pair alist))))
+          pairs)
     ;; recurse into cdrs of alist
     (mapc (lambda (x)
-           (when (and (listp x) (listp (cadr x)))
-             (setcdr x (if (> (length (cdr x)) 1)
-                           (rcirc-make-trees (cdr x))
-                         (setcdr x (list (cdadr x)))))))
-         alist)))
+            (when (and (listp x) (listp (cadr x)))
+              (setcdr x (if (> (length (cdr x)) 1)
+                            (rcirc-make-trees (cdr x))
+                          (setcdr x (list (cdadr x)))))))
+          alist)))
 
 ;;; /commands these are called with 3 args: PROCESS, TARGET, which is
 ;; the current buffer/channel/user, and ARGS, which is a string
@@ -2666,7 +2745,7 @@ that, an interactive form can specified."
   "Send MESSAGE to CHAN-OR-NICK."
   (interactive (list (completing-read "Message nick: "
                                       (with-rcirc-server-buffer
-                                       rcirc-nick-table))
+                                        rcirc-nick-table))
                      (read-string "Message: ")))
   (rcirc-send-message process chan-or-nick message))
 
@@ -2677,7 +2756,7 @@ that, an interactive form can specified."
                                         rcirc-nick-table))))
   (let ((existing-buffer (rcirc-get-buffer process nick)))
     (switch-to-buffer (or existing-buffer
-                         (rcirc-get-buffer-create process nick)))
+                          (rcirc-get-buffer-create process nick)))
     (when (not existing-buffer)
       (rcirc-cmd-whois nick))))
 
@@ -2699,7 +2778,7 @@ CHANNELS is a comma- or space-separated string of channel 
names."
   "Invite NICK to CHANNEL."
   (interactive (list
                 (completing-read "Invite nick: "
-                                (with-rcirc-server-buffer rcirc-nick-table))
+                                 (with-rcirc-server-buffer rcirc-nick-table))
                 (read-string "Channel: ")))
   (rcirc-send-string process "INVITE" nick channel))
 
@@ -2778,8 +2857,8 @@ With a prefix arg, prompt for new topic."
   (interactive (list
                 (completing-read "Kick nick: "
                                  (rcirc-channel-nicks
-                                 (rcirc-buffer-process)
-                                 rcirc-target))
+                                  (rcirc-buffer-process)
+                                  rcirc-target))
                 (read-from-minibuffer "Kick reason: ")))
   (rcirc-send-string process "KICK" target nick : reason))
 
@@ -2811,9 +2890,9 @@ PROCESS is the process object for the current connection."
   "Toggle membership of ELEMENTS in SET."
   (dolist (elt elements)
     (if (and elt (not (string= "" elt)))
-       (setq set (if (member-ignore-case elt set)
-                     (delete elt set)
-                   (cons elt set)))))
+        (setq set (if (member-ignore-case elt set)
+                      (delete elt set)
+                    (cons elt set)))))
   set)
 
 
@@ -2824,33 +2903,33 @@ nicks when no NICK is given.  When listing ignored 
nicks, the
 ones added to the list automatically are marked with an asterisk."
   (interactive "sToggle ignoring of nick: ")
   (setq rcirc-ignore-list
-       (apply #'rcirc-add-or-remove rcirc-ignore-list
-              (split-string nick nil t)))
+        (apply #'rcirc-add-or-remove rcirc-ignore-list
+               (split-string nick nil t)))
   (rcirc-print process nil "IGNORE" target
-              (mapconcat
-               (lambda (nick)
-                 (concat nick
-                         (if (member nick rcirc-ignore-list-automatic)
-                             "*" "")))
-               rcirc-ignore-list " ")))
+               (mapconcat
+                (lambda (nick)
+                  (concat nick
+                          (if (member nick rcirc-ignore-list-automatic)
+                              "*" "")))
+                rcirc-ignore-list " ")))
 
 (rcirc-define-command bright (nick)
   "Manage the bright nick list."
   (interactive "sToggle emphasis of nick: ")
   (setq rcirc-bright-nicks
-       (apply #'rcirc-add-or-remove rcirc-bright-nicks
-              (split-string nick nil t)))
+        (apply #'rcirc-add-or-remove rcirc-bright-nicks
+               (split-string nick nil t)))
   (rcirc-print process nil "BRIGHT" target
-              (mapconcat 'identity rcirc-bright-nicks " ")))
+               (mapconcat 'identity rcirc-bright-nicks " ")))
 
 (rcirc-define-command dim (nick)
   "Manage the dim nick list."
   (interactive "sToggle deemphasis of nick: ")
   (setq rcirc-dim-nicks
-       (apply #'rcirc-add-or-remove rcirc-dim-nicks
-              (split-string nick nil t)))
+        (apply #'rcirc-add-or-remove rcirc-dim-nicks
+               (split-string nick nil t)))
   (rcirc-print process nil "DIM" target
-              (mapconcat 'identity rcirc-dim-nicks " ")))
+               (mapconcat 'identity rcirc-dim-nicks " ")))
 
 (rcirc-define-command keyword (keyword)
   "Manage the keyword list.
@@ -2858,24 +2937,24 @@ Mark KEYWORD, unmark KEYWORD if already marked, or list 
marked
 keywords when no KEYWORD is given."
   (interactive "sToggle highlighting of keyword: ")
   (setq rcirc-keywords
-       (apply #'rcirc-add-or-remove rcirc-keywords
-              (split-string keyword nil t)))
+        (apply #'rcirc-add-or-remove rcirc-keywords
+               (split-string keyword nil t)))
   (rcirc-print process nil "KEYWORD" target
-              (mapconcat 'identity rcirc-keywords " ")))
+               (mapconcat 'identity rcirc-keywords " ")))
 
 
 (defun rcirc-add-face (start end name &optional object)
   "Add face NAME to the face text property of the text from START to END."
   (when name
     (let ((pos start)
-         next prop)
+          next prop)
       (while (< pos end)
-       (setq prop (get-text-property pos 'font-lock-face object)
-             next (next-single-property-change pos 'font-lock-face object end))
-       (unless (member name (get-text-property pos 'font-lock-face object))
-         (add-text-properties pos next
-                              (list 'font-lock-face (cons name prop)) object))
-       (setq pos next)))))
+        (setq prop (get-text-property pos 'font-lock-face object)
+              next (next-single-property-change pos 'font-lock-face object 
end))
+        (unless (member name (get-text-property pos 'font-lock-face object))
+          (add-text-properties pos next
+                               (list 'font-lock-face (cons name prop)) object))
+        (setq pos next)))))
 
 (defun rcirc-facify (string face)
   "Return a copy of STRING with FACE property added."
@@ -2917,7 +2996,79 @@ If ARG is given, opens the URL in a new browser window."
   (let ((time (and-let* ((time (rcirc-get-tag "time")))
                 (parse-iso8601-time-string time))))
     (insert (rcirc-facify (format-time-string rcirc-time-format time)
-                         'rcirc-timestamp))))
+                          'rcirc-timestamp))))
+
+(defvar-local rcirc-pseudo-nicks '()
+  "List of virtual nicks detected in a channel.
+These are collected by `rcirc-markup-bridge-bots' and don't
+constitute actual users in the current channel.  Usually these
+are bridged via a some bot as described in
+`rcirc-bridge-bot-alist'.")
+
+(defcustom rcirc-bridge-bot-alist '()
+  "Alist for handling bouncers by `rcirc-markup-bridge-bots'.
+Each entry has the form (NAME . REGEXP), where NAME is the user
+name of a bouncer and REGEXP is a pattern used to match the
+message.  The matching part of the message will be stripped from
+the message, and the first match group will replace the user name
+of the bot.  Any matched name will noted and used in some cases
+for nick completion."
+  :type '(alist :key-type (string :tag "Bot name")
+                :value-type regexp)
+  :version "29.1")
+
+(defface rcirc-bridged-nick
+  '((((class color) (min-colors 88) (background light)) :background 
"SlateGray1")
+    (((class color) (min-colors 88) (background dark))  :background 
"DarkSlateGray4")
+    (((class color) (min-colors 16) (background light)) :background 
"LightBlue")
+    (((class color) (min-colors 16) (background dark))  :background 
"DarkSlateGray")
+    (t :background "blue"))
+  "Face used for pseudo-nick ."
+  :version "29.1")
+
+(defun rcirc-markup-bridge-bots (sender response)
+  "Detect and reformat bridged messages to appear more natural.
+The user option `rcirc-bridge-bot-alist' specified what SENDER to
+handle.  This function only operates on direct messages (as
+indicated by RESPONSE)."
+  (catch 'quit
+    (atomic-change-group
+      (save-match-data
+        (when-let* (((string= response "PRIVMSG"))
+                    (regexp (alist-get sender rcirc-bridge-bot-alist
+                                       nil nil #'string=))
+                    ((search-forward-regexp regexp nil t))
+                    (nick (match-string-no-properties 1)))
+          (replace-match "")            ;delete the bot string
+          (unless (member nick rcirc-pseudo-nicks)
+            (push nick rcirc-pseudo-nicks))
+          (goto-char (point-min))
+          (let ((fmt (alist-get "PRIVMSG" rcirc-response-formats
+                                nil nil #'string=))
+                (hl-face (cond ((member sender rcirc-bright-nicks)
+                                'rcirc-bright-nick)
+                               ((member sender rcirc-dim-nicks)
+                                'rcirc-dim-nick)
+                               (t 'rcirc-other-nick)))
+                hl-username-p)
+            (when (string-match (rx (* "%%") "%" (group (or ?N ?n))) fmt)
+              (when (string= (match-string 1 fmt) "N")
+                (setq hl-username-p t))
+              (search-forward-regexp
+               (format-spec
+                (alist-get "PRIVMSG" rcirc-response-formats
+                           nil nil #'string=)
+                `((?m . "") (?r . "") (?t . "") (?f . "")
+                  (?N . ,(rx (group (+? nonl))))
+                  (?n . ,(rx (group (+? nonl))))))
+               nil t)
+              (replace-match
+               (propertize
+                nick
+                'help-echo (format "Message bridged via %s" sender)
+                'face `(,@(and hl-username-p (list hl-face))
+                        rcirc-bridged-nick))
+               nil t nil 1))))))))
 
 (defun rcirc-markup-attributes (_sender _response)
   "Highlight IRC markup, indicated by ASCII control codes."
@@ -2974,10 +3125,8 @@ If ARG is given, opens the URL in a new browser window."
                  ((<= 0 bg (1- (length rcirc-color-codes)))))
         (setq background (aref rcirc-color-codes bg)))
       (rcirc-add-face (match-beginning 0) (match-end 0)
-                           `(face (:foreground
-                                   ,foreground
-                                   :background
-                                   ,background))))))
+                      `(face (,@(and foreground (list :foreground foreground))
+                              ,@(and background (list :background 
background))))))))
 
 (defun rcirc-remove-markup-codes (_sender _response)
   "Remove ASCII control codes used to designate markup."
@@ -2993,16 +3142,16 @@ If RESPONSE indicates that the nick was mentioned in a 
message,
 highlight the entire line and record the activity."
   (with-syntax-table rcirc-nick-syntax-table
     (while (re-search-forward (concat "\\b"
-                                     (regexp-quote (rcirc-nick
-                                                    (rcirc-buffer-process)))
-                                     "\\b")
-                             nil t)
+                                      (regexp-quote (rcirc-nick
+                                                     (rcirc-buffer-process)))
+                                      "\\b")
+                              nil t)
       (rcirc-add-face (match-beginning 0) (match-end 0)
-                     'rcirc-nick-in-message)
+                      'rcirc-nick-in-message)
       (when (string= response "PRIVMSG")
-       (rcirc-add-face (point-min) (point-max)
-                       'rcirc-nick-in-message-full-line)
-       (rcirc-record-activity (current-buffer) 'nick)))))
+        (rcirc-add-face (point-min) (point-max)
+                        'rcirc-nick-in-message-full-line)
+        (rcirc-record-activity (current-buffer) 'nick)))))
 
 (defun rcirc-markup-urls (_sender _response)
   "Highlight and activate URLs."
@@ -3018,11 +3167,11 @@ highlight the entire line and record the activity."
       ;; rather than `make-button', as text-buttons are much faster in
       ;; large buffers.
       (make-text-button start (point)
-                       'face 'rcirc-url
-                       'follow-link t
-                       'rcirc-url url
-                       'action (lambda (button)
-                                 (browse-url-button-open-url
+                        'face 'rcirc-url
+                        'follow-link t
+                        'rcirc-url url
+                        'action (lambda (button)
+                                  (browse-url-button-open-url
                                    (button-get button 'rcirc-url))))
       ;; Record the URL if it is not already the latest stored URL.
       (unless (string= url (caar rcirc-urls))
@@ -3034,42 +3183,42 @@ Keywords are only highlighted in messages (as indicated 
by
 RESPONSE) when they were not written by the user (as indicated by
 SENDER)."
   (when (and (string= response "PRIVMSG")
-            (not (string= sender (rcirc-nick (rcirc-buffer-process)))))
+             (not (string= sender (rcirc-nick (rcirc-buffer-process)))))
     (let* ((target (or rcirc-target ""))
-          (keywords (delq nil (mapcar (lambda (keyword)
-                                        (when (not (string-match keyword
-                                                                 target))
-                                          keyword))
-                                      rcirc-keywords))))
+           (keywords (delq nil (mapcar (lambda (keyword)
+                                         (when (not (string-match keyword
+                                                                  target))
+                                           keyword))
+                                       rcirc-keywords))))
       (when keywords
-       (while (re-search-forward (regexp-opt keywords 'words) nil t)
-         (rcirc-add-face (match-beginning 0) (match-end 0) 'rcirc-keyword)
-         (rcirc-record-activity (current-buffer) 'keyword))))))
+        (while (re-search-forward (regexp-opt keywords 'words) nil t)
+          (rcirc-add-face (match-beginning 0) (match-end 0) 'rcirc-keyword)
+          (rcirc-record-activity (current-buffer) 'keyword))))))
 
 (defun rcirc-markup-bright-nicks (_sender response)
   "Highlight nicks brightly as specified by `rcirc-bright-nicks'.
 This highlighting only takes place in name lists (as indicated by
 RESPONSE)."
   (when (and rcirc-bright-nicks
-            (string= response "NAMES"))
+             (string= response "NAMES"))
     (with-syntax-table rcirc-nick-syntax-table
       (while (re-search-forward (regexp-opt rcirc-bright-nicks 'words) nil t)
-       (rcirc-add-face (match-beginning 0) (match-end 0)
-                       'rcirc-bright-nick)))))
+        (rcirc-add-face (match-beginning 0) (match-end 0)
+                        'rcirc-bright-nick)))))
 
 (defun rcirc-markup-fill (_sender response)
   "Fill messages as configured by `rcirc-fill-column'.
 MOTD messages are not filled (as indicated by RESPONSE)."
-  (when (not (string= response "372"))         ; /motd
+  (when (not (string= response "372"))          ; /motd
     (let ((fill-prefix
-          (or rcirc-fill-prefix
-              (make-string (- (point) (line-beginning-position)) ?\s)))
-         (fill-column (- (cond ((null rcirc-fill-column) fill-column)
+           (or rcirc-fill-prefix
+               (make-string (- (point) (line-beginning-position)) ?\s)))
+          (fill-column (- (cond ((null rcirc-fill-column) fill-column)
                                 ((functionp rcirc-fill-column)
-                                (funcall rcirc-fill-column))
-                               (t rcirc-fill-column))
-                         ;; make sure ... doesn't cause line wrapping
-                         3)))
+                                 (funcall rcirc-fill-column))
+                                (t rcirc-fill-column))
+                          ;; make sure ... doesn't cause line wrapping
+                          3)))
       (fill-region (point) (point-max) nil t))))
 
 ;;; handlers
@@ -3098,7 +3247,7 @@ PROCESS is the process object for the current connection."
                    (setq auth-required t)))))
         (if rcirc-authenticate-before-join
             (progn
-             (add-hook 'rcirc-authenticated-hook 
'rcirc-join-channels-post-auth t t)
+              (add-hook 'rcirc-authenticated-hook 
'rcirc-join-channels-post-auth t t)
               (rcirc-authenticate))
           (rcirc-authenticate)
           (rcirc-join-channels process rcirc-startup-channels))
@@ -3137,18 +3286,18 @@ PROCESS is the process object for the current 
connection."
         (message (cadr args)))
     (if (string-match "^\C-a\\(.*\\)\C-a$" message)
         (rcirc-handler-CTCP-response process target sender
-                                    (match-string 1 message))
+                                     (match-string 1 message))
       (rcirc-print process sender "NOTICE"
-                  (cond ((rcirc-channel-p target)
-                         target)
+                   (cond ((rcirc-channel-p target)
+                          target)
                          ;; -ChanServ- [#gnu] Welcome...
-                        ((string-match "\\[\\(#[^] ]+\\)\\]" message)
-                         (match-string 1 message))
-                        (sender
-                         (if (string= sender (rcirc-server-name process))
-                             nil       ; server notice
-                           sender)))
-                 message t))))
+                         ((string-match "\\[\\(#[^] ]+\\)\\]" message)
+                          (match-string 1 message))
+                         (sender
+                          (if (string= sender (rcirc-server-name process))
+                              nil       ; server notice
+                            sender)))
+                   message t))))
 
 (defun rcirc-check-auth-status (process sender args _text)
   "Check if the user just authenticated.
@@ -3200,10 +3349,10 @@ connection."
     (with-current-buffer (rcirc-get-buffer-create process channel)
       ;; when recently rejoining, restore the linestamp
       (rcirc-put-nick-channel process sender channel
-                             (let ((last-activity-lines
-                                    (rcirc-elapsed-lines process sender 
channel)))
-                               (when (and last-activity-lines
-                                          (< last-activity-lines 
rcirc-omit-threshold))
+                              (let ((last-activity-lines
+                                     (rcirc-elapsed-lines process sender 
channel)))
+                                (when (and last-activity-lines
+                                           (< last-activity-lines 
rcirc-omit-threshold))
                                   (rcirc-last-line process sender channel))))
       ;; reset mode-line-process in case joining a channel with an
       ;; already open buffer (after getting kicked e.g.)
@@ -3223,25 +3372,25 @@ PROCESS is the process object for the current 
connection."
   (if (not (string= nick (rcirc-nick process)))
       ;; this is someone else leaving
       (progn
-       (rcirc-maybe-remember-nick-quit process nick channel)
-       (rcirc-remove-nick-channel process nick channel))
+        (rcirc-maybe-remember-nick-quit process nick channel)
+        (rcirc-remove-nick-channel process nick channel))
     ;; this is us leaving
     (mapc (lambda (n)
-           (rcirc-remove-nick-channel process n channel))
-         (rcirc-channel-nicks process channel))
+            (rcirc-remove-nick-channel process n channel))
+          (rcirc-channel-nicks process channel))
 
     ;; if the buffer is still around, make it inactive
     (let ((buffer (rcirc-get-buffer process channel)))
       (when buffer
-       (rcirc-disconnect-buffer buffer)))))
+        (rcirc-disconnect-buffer buffer)))))
 
 (defun rcirc-handler-PART (process sender args _text)
   "Handle PART message from SENDER.
 ARGS should have the form (CHANNEL REASON).
 PROCESS is the process object for the current connection."
   (let* ((channel (car args))
-        (reason (cadr args))
-        (message (concat channel " " reason)))
+         (reason (cadr args))
+         (message (concat channel " " reason)))
     (rcirc-print process sender "PART" (funcall rcirc-channel-filter channel) 
message)
     ;; print in private chat buffer if it exists
     (when (rcirc-get-buffer (rcirc-buffer-process) sender)
@@ -3254,9 +3403,9 @@ PROCESS is the process object for the current connection."
 ARGS should have the form (CHANNEL NICK REASON).
 PROCESS is the process object for the current connection."
   (let* ((channel (car args))
-        (nick (cadr args))
-        (reason (nth 2 args))
-        (message (concat nick " " channel " " reason)))
+         (nick (cadr args))
+         (reason (nth 2 args))
+         (message (concat nick " " channel " " reason)))
     (rcirc-print process sender "KICK" (funcall rcirc-channel-filter channel) 
message t)
     ;; print in private chat buffer if it exists
     (when (rcirc-get-buffer (rcirc-buffer-process) nick)
@@ -3269,28 +3418,28 @@ PROCESS is the process object for the current 
connection."
 PROCESS is the process object for the current connection."
   (let ((elapsed-lines (rcirc-elapsed-lines process nick channel)))
     (when (and elapsed-lines
-              (< elapsed-lines rcirc-omit-threshold))
+               (< elapsed-lines rcirc-omit-threshold))
       (let ((buffer (rcirc-get-buffer process channel)))
-       (when buffer
-         (with-current-buffer buffer
-           (let ((record (assoc-string nick rcirc-recent-quit-alist t))
-                 (line (rcirc-last-line process nick channel)))
-             (if record
-                 (setcdr record line)
-               (setq rcirc-recent-quit-alist
-                     (cons (cons nick line)
-                           rcirc-recent-quit-alist))))))))))
+        (when buffer
+          (with-current-buffer buffer
+            (let ((record (assoc-string nick rcirc-recent-quit-alist t))
+                  (line (rcirc-last-line process nick channel)))
+              (if record
+                  (setcdr record line)
+                (setq rcirc-recent-quit-alist
+                      (cons (cons nick line)
+                            rcirc-recent-quit-alist))))))))))
 
 (defun rcirc-handler-QUIT (process sender args _text)
   "Handle QUIT message from SENDER.
 PROCESS is the process object for the current connection."
   (rcirc-ignore-update-automatic sender)
   (mapc (lambda (channel)
-         ;; broadcast quit message each channel
-         (rcirc-print process sender "QUIT" (funcall rcirc-channel-filter 
channel) (apply 'concat args))
-         ;; record nick in quit table if they recently spoke
-         (rcirc-maybe-remember-nick-quit process sender channel))
-       (rcirc-nick-channels process sender))
+          ;; broadcast quit message each channel
+          (rcirc-print process sender "QUIT" (funcall rcirc-channel-filter 
channel) (apply 'concat args))
+          ;; record nick in quit table if they recently spoke
+          (rcirc-maybe-remember-nick-quit process sender channel))
+        (rcirc-nick-channels process sender))
   (rcirc-nick-remove process sender))
 
 (defun rcirc-handler-NICK (process sender args _text)
@@ -3311,9 +3460,9 @@ PROCESS is the process object for the current connection."
     ;; update chat buffer, if it exists
     (when-let ((chat-buffer (rcirc-get-buffer process old-nick)))
       (with-current-buffer chat-buffer
-       (rcirc-print process sender "NICK" old-nick new-nick)
-       (setq rcirc-target new-nick)
-       (rename-buffer (rcirc-generate-new-buffer-name process new-nick) t))
+        (rcirc-print process sender "NICK" old-nick new-nick)
+        (setq rcirc-target new-nick)
+        (rename-buffer (rcirc-generate-new-buffer-name process new-nick) t))
       (setf rcirc-buffer-alist
             (cons (cons new-nick chat-buffer)
                   (delq (assoc-string old-nick rcirc-buffer-alist t)
@@ -3326,7 +3475,7 @@ PROCESS is the process object for the current connection."
       ;; if this is our nick...
       (when (string= old-nick rcirc-nick)
         (setq rcirc-nick new-nick)
-       (rcirc-update-prompt t)
+        (rcirc-update-prompt t)
         ;; reauthenticate
         (when rcirc-auto-authenticate-flag (rcirc-authenticate))))))
 
@@ -3356,16 +3505,16 @@ PROCESS is the process object for the current 
connection."
 ARGS should have the form (NICK AWAY-MESSAGE).
 PROCESS is the process object for the current connection."
   (let* ((nick (cadr args))
-        (rec (assoc-string nick rcirc-nick-away-alist))
-        (away-message (nth 2 args)))
+         (rec (assoc-string nick rcirc-nick-away-alist))
+         (away-message (nth 2 args)))
     (when (or (not rec)
-             (not (string= (cdr rec) away-message)))
+              (not (string= (cdr rec) away-message)))
       ;; away message has changed
       (rcirc-handler-generic process "AWAY" nick (cdr args) text)
       (if rec
-         (setcdr rec away-message)
-       (setq rcirc-nick-away-alist (cons (cons nick away-message)
-                                         rcirc-nick-away-alist))))))
+          (setcdr rec away-message)
+        (setq rcirc-nick-away-alist (cons (cons nick away-message)
+                                          rcirc-nick-away-alist))))))
 
 (defun rcirc-handler-317 (process sender args _text)
   "Handle idle messages from SENDER (RPL_WHOISIDLE).
@@ -3374,7 +3523,7 @@ PROCESS is the process object for the current connection."
   (let* ((nick (nth 1 args))
          (idle-secs (string-to-number (nth 2 args)))
          (idle-string (format-seconds "%yy %dd %hh %mm %z%ss" idle-secs))
-        (signon-time (string-to-number (nth 3 args)))
+         (signon-time (string-to-number (nth 3 args)))
          (signon-string (format-time-string "%c" signon-time))
          (message (format "%s idle for %s, signed on %s"
                           nick idle-string signon-string)))
@@ -3385,7 +3534,7 @@ PROCESS is the process object for the current connection."
 ARGS should have the form (CHANNEL TOPIC).
 PROCESS is the process object for the current connection."
   (let ((buffer (or (rcirc-get-buffer process (cadr args))
-                   (rcirc-get-temp-buffer-create process (cadr args)))))
+                    (rcirc-get-temp-buffer-create process (cadr args)))))
     (with-current-buffer buffer
       (setq rcirc-topic (nth 2 args)))))
 
@@ -3396,13 +3545,13 @@ ARGS has the form (CHANNEL SETTER TIME).  SENDER is 
passed on to
 connection.  This is a non-standard extension, not specified in
 RFC1459."
   (let ((buffer (or (rcirc-get-buffer process (cadr args))
-                   (rcirc-get-temp-buffer-create process (cadr args)))))
+                    (rcirc-get-temp-buffer-create process (cadr args)))))
     (with-current-buffer buffer
       (let ((setter (nth 2 args))
-           (time (current-time-string
-                  (string-to-number (cadddr args)))))
-       (rcirc-print process sender "TOPIC" (cadr args)
-                    (format "%s (%s on %s)" rcirc-topic setter time))))))
+            (time (current-time-string
+                   (string-to-number (cadddr args)))))
+        (rcirc-print process sender "TOPIC" (cadr args)
+                     (format "%s (%s on %s)" rcirc-topic setter time))))))
 
 (defun rcirc-handler-477 (process sender args _text)
   "Notify user that CHANNEL does not support modes (ERR_NOCHANMODES).
@@ -3426,9 +3575,9 @@ PROCESS is the process object for the current connection."
 
     ;; print in private chat buffers if they exist
     (mapc (lambda (nick)
-           (when (rcirc-get-buffer process nick)
-             (rcirc-print process sender "MODE" nick msg)))
-         (cddr args))))
+            (when (rcirc-get-buffer process nick)
+              (rcirc-print process sender "MODE" nick msg)))
+          (cddr args))))
 
 (defun rcirc-get-temp-buffer-create (process channel)
   "Return a buffer based on PROCESS and CHANNEL."
@@ -3440,7 +3589,7 @@ PROCESS is the process object for the current connection."
 ARGS should have the form (TYPE CHANNEL . NICK-LIST).
 PROCESS is the process object for the current connection."
   (let ((channel (nth 2 args))
-       (names (or (nth 3 args) "")))
+        (names (or (nth 3 args) "")))
     (mapc (lambda (nick)
             (rcirc-put-nick-channel process nick channel))
           (split-string names " " t))
@@ -3459,7 +3608,7 @@ PROCESS is the process object for the current connection."
     (with-current-buffer buffer
       (rcirc-print process sender "NAMES" channel
                    (let ((content (buffer-substring (point-min) (point-max))))
-                    (rcirc-sort-nicknames-join content " "))))
+                     (rcirc-sort-nicknames-join content " "))))
     (kill-buffer buffer)))
 
 (defun rcirc-handler-433 (process sender args text)
@@ -3507,11 +3656,11 @@ Passwords are stored in `rcirc-authinfo' (which see)."
   (with-rcirc-server-buffer
     (dolist (i rcirc-authinfo)
       (let ((process (rcirc-buffer-process))
-           (server (car i))
-           (nick (nth 2 i))
-           (method (cadr i))
-           (args (cdddr i)))
-       (when (and (string-match server rcirc-server))
+            (server (car i))
+            (nick (nth 2 i))
+            (method (cadr i))
+            (args (cdddr i)))
+        (when (and (string-match server rcirc-server))
           (if (and (memq method '(nickserv chanserv bitlbee))
                    (string-match nick rcirc-nick))
               ;; the following methods rely on the user's nickname.
@@ -3581,12 +3730,12 @@ current connection."
         (if (not (fboundp handler))
             (rcirc-print process sender "ERROR" target
                          (format "%s sent unsupported ctcp: %s" sender text)
-                        t)
+                         t)
           (funcall handler process target sender args)
           (unless (or (string= request "ACTION")
-                     (string= request "KEEPALIVE"))
-              (rcirc-print process sender "CTCP" target
-                          (format "%s" text) t))))))
+                      (string= request "KEEPALIVE"))
+            (rcirc-print process sender "CTCP" target
+                         (format "%s" text) t))))))
 
 (defun rcirc-handler-ctcp-VERSION (process _target sender _message)
   "Handle a CTCP VERSION message from SENDER.
@@ -3733,7 +3882,7 @@ PROCESS is the process object for the current connection."
   '((t :family "Monospace"))
   "Face used for monospace text in messages.")
 
-(defface rcirc-my-nick                 ; font-lock-function-name-face
+(defface rcirc-my-nick                  ; font-lock-function-name-face
   '((((class color) (min-colors 88) (background light)) :foreground "Blue1")
     (((class color) (min-colors 88) (background dark))  :foreground 
"LightSkyBlue")
     (((class color) (min-colors 16) (background light)) :foreground "Blue")
@@ -3742,7 +3891,7 @@ PROCESS is the process object for the current connection."
     (t :inverse-video t :weight bold))
   "Rcirc face for my messages.")
 
-(defface rcirc-other-nick           ; font-lock-variable-name-face
+(defface rcirc-other-nick            ; font-lock-variable-name-face
   '((((class grayscale) (background light))
      :foreground "Gray90" :weight bold :slant italic)
     (((class grayscale) (background dark))
@@ -3772,7 +3921,7 @@ PROCESS is the process object for the current connection."
   '((t :inherit default))
   "Rcirc face for nicks in `rcirc-dim-nicks'.")
 
-(defface rcirc-server                  ; font-lock-comment-face
+(defface rcirc-server                   ; font-lock-comment-face
   '((((class grayscale) (background light))
      :foreground "DimGray" :weight bold :slant italic)
     (((class grayscale) (background dark))
@@ -3790,7 +3939,7 @@ PROCESS is the process object for the current connection."
     (t :weight bold :slant italic))
   "Rcirc face for server messages.")
 
-(defface rcirc-server-prefix    ; font-lock-comment-delimiter-face
+(defface rcirc-server-prefix     ; font-lock-comment-delimiter-face
   '((default :inherit rcirc-server)
     (((class grayscale)))
     (((class color) (min-colors 16)))
@@ -3804,7 +3953,7 @@ PROCESS is the process object for the current connection."
   '((t :inherit default))
   "Rcirc face for timestamps.")
 
-(defface rcirc-nick-in-message         ; font-lock-keyword-face
+(defface rcirc-nick-in-message          ; font-lock-keyword-face
   '((((class grayscale) (background light)) :foreground "LightGray" :weight 
bold)
     (((class grayscale) (background dark)) :foreground "DimGray" :weight bold)
     (((class color) (min-colors 88) (background light)) :foreground "Purple")
@@ -3818,7 +3967,7 @@ PROCESS is the process object for the current connection."
 (defface rcirc-nick-in-message-full-line '((t :weight bold))
   "Rcirc face for emphasizing the entire message when your nick is mentioned.")
 
-(defface rcirc-prompt                  ; comint-highlight-prompt
+(defface rcirc-prompt                   ; comint-highlight-prompt
   '((((min-colors 88) (background dark)) :foreground "cyan1")
     (((background dark)) :foreground "cyan")
     (t :foreground "dark blue"))
diff --git a/lisp/net/shr.el b/lisp/net/shr.el
index 248faeb223..75992bc62a 100644
--- a/lisp/net/shr.el
+++ b/lisp/net/shr.el
@@ -295,6 +295,11 @@ and other things:
               (make-composed-keymap shr-map image-map)
             shr-map))
 
+(defvar shr-url-transformer #'identity
+  "Function to transform URLs.
+It's called with the URL as the parameter, and should return the
+ URL to use.")
+
 ;; Public functions and commands.
 (declare-function libxml-parse-html-region "xml.c"
                  (start end &optional base-url discard-comments))
@@ -368,7 +373,6 @@ DOM should be a parse tree as generated by
                 shr-width
               (* shr-width (frame-char-width)))
            (shr--window-width)))
-        (max-specpdl-size max-specpdl-size)
         (shr--link-targets nil)
         (hscroll (window-hscroll))
         ;; `bidi-display-reordering' is supposed to be only used for
@@ -499,7 +503,7 @@ Value is a pair of positions (START . END) if there is a 
non-nil
 (defun shr-next-link ()
   "Skip to the next link."
   (interactive)
-  (let ((match (text-property-search-forward 'shr-url nil nil t)))
+  (let ((match (text-property-search-forward 'shr-tab-stop nil nil t)))
     (if (not match)
         (message "No next link")
       (goto-char (prop-match-beginning match))
@@ -508,7 +512,7 @@ Value is a pair of positions (START . END) if there is a 
non-nil
 (defun shr-previous-link ()
   "Skip to the previous link."
   (interactive)
-  (if (not (text-property-search-backward 'shr-url nil nil t))
+  (if (not (text-property-search-backward 'shr-tab-stop nil nil t))
       (message "No previous link")
     (message "%s" (get-text-property (point) 'help-echo))))
 
@@ -620,41 +624,34 @@ size, and full-buffer size."
        (shr-stylesheet shr-stylesheet)
        (shr-depth (1+ shr-depth))
        (start (point)))
-    ;; shr uses many frames per nested node.
-    (if (and (> shr-depth (/ max-specpdl-size 15))
-             (not (and shr-offer-extend-specpdl
-                       (y-or-n-p "Too deeply nested to render properly; 
increase `max-specpdl-size'?")
-                       (setq max-specpdl-size (* max-specpdl-size 2)))))
-        (setq shr-warning
-              "Not rendering the complete page because of too-deep nesting")
+    (when style
+      (if (string-match-p "color\\|display\\|border-collapse" style)
+         (setq shr-stylesheet (nconc (shr-parse-style style)
+                                     shr-stylesheet))
+       (setq style nil)))
+    ;; If we have a display:none, then just ignore this part of the DOM.
+    (unless (or (equal (cdr (assq 'display shr-stylesheet)) "none")
+                (and shr-discard-aria-hidden
+                     (equal (dom-attr dom 'aria-hidden) "true")))
+      ;; We don't use shr-indirect-call here, since shr-descend is
+      ;; the central bit of shr.el, and should be as fast as
+      ;; possible.  Having one more level of indirection with its
+      ;; negative effect on performance is deemed unjustified in
+      ;; this case.
+      (cond (external
+             (funcall external dom))
+            ((fboundp function)
+             (funcall function dom))
+            (t
+             (shr-generic dom)))
+      (when-let ((id (dom-attr dom 'id)))
+        (push (cons id (set-marker (make-marker) start)) shr--link-targets))
+      ;; If style is set, then this node has set the color.
       (when style
-       (if (string-match-p "color\\|display\\|border-collapse" style)
-           (setq shr-stylesheet (nconc (shr-parse-style style)
-                                       shr-stylesheet))
-         (setq style nil)))
-      ;; If we have a display:none, then just ignore this part of the DOM.
-      (unless (or (equal (cdr (assq 'display shr-stylesheet)) "none")
-                  (and shr-discard-aria-hidden
-                       (equal (dom-attr dom 'aria-hidden) "true")))
-        ;; We don't use shr-indirect-call here, since shr-descend is
-        ;; the central bit of shr.el, and should be as fast as
-        ;; possible.  Having one more level of indirection with its
-        ;; negative effect on performance is deemed unjustified in
-        ;; this case.
-        (cond (external
-               (funcall external dom))
-              ((fboundp function)
-               (funcall function dom))
-              (t
-               (shr-generic dom)))
-        (when-let ((id (dom-attr dom 'id)))
-          (push (cons id (set-marker (make-marker) start)) shr--link-targets))
-       ;; If style is set, then this node has set the color.
-       (when style
-         (shr-colorize-region
-          start (point)
-          (cdr (assq 'color shr-stylesheet))
-          (cdr (assq 'background-color shr-stylesheet))))))))
+       (shr-colorize-region
+        start (point)
+        (cdr (assq 'color shr-stylesheet))
+        (cdr (assq 'background-color shr-stylesheet)))))))
 
 (defun shr-fill-text (text)
   (if (zerop (length text))
@@ -1019,6 +1016,8 @@ the mouse click event."
                (widen)
                (let ((alt (buffer-substring start end))
                      (properties (text-properties-at start))
+                      ;; We don't want to record these changes.
+                      (buffer-undo-list t)
                      (inhibit-read-only t))
                  (delete-region start end)
                  (goto-char start)
@@ -1216,6 +1215,7 @@ START, and END.  Note that START and END should be 
markers."
   (add-text-properties
    start (point)
    (list 'shr-url url
+         'shr-tab-stop t
          'button t
          'category 'shr                ; For button.el button buffers.
         'help-echo (let ((parsed (url-generic-parse-url
@@ -1487,7 +1487,9 @@ ones, in case fg and bg are nil."
                          (dom-attr dom 'name)))) ; Obsolete since HTML5.
       (push (cons id (set-marker (make-marker) start)) shr--link-targets))
     (when url
-      (shr-urlify (or shr-start start) (shr-expand-url url) title)
+      (shr-urlify (or shr-start start)
+                  (funcall shr-url-transformer (shr-expand-url url))
+                  title)
       ;; Check whether the URL is suspicious.
       (when-let ((warning (or (textsec-suspicious-p
                                (shr-expand-url url) 'url)
diff --git a/lisp/net/sieve-manage.el b/lisp/net/sieve-manage.el
index a39e35a53a..381e1fcd4f 100644
--- a/lisp/net/sieve-manage.el
+++ b/lisp/net/sieve-manage.el
@@ -167,7 +167,52 @@ Valid states are `closed', `initial', `nonauth', and 
`auth'.")
 (defvar sieve-manage-capability nil)
 
 ;; Internal utility functions
-(autoload 'mm-enable-multibyte "mm-util")
+(defun sieve-manage--append-to-log (&rest args)
+  "Append ARGS to sieve-manage log buffer.
+
+ARGS can be a string or a list of strings.
+The buffer to use for logging is specifified via
+`sieve-manage-log'. If it is nil, logging is disabled."
+  (when sieve-manage-log
+    (with-current-buffer (or (get-buffer sieve-manage-log)
+                             (with-current-buffer
+                                 (get-buffer-create sieve-manage-log)
+                               (set-buffer-multibyte nil)
+                               (buffer-disable-undo)))
+      (goto-char (point-max))
+      (apply #'insert args))))
+
+(defun sieve-manage--message (format-string &rest args)
+  "Wrapper around `message' which also logs to sieve manage log.
+
+See `sieve-manage--append-to-log'."
+  (let ((ret (apply #'message
+                    (concat "sieve-manage: " format-string)
+                    args)))
+    (sieve-manage--append-to-log ret "\n")
+    ret))
+
+(defun sieve-manage--error (format-string &rest args)
+  "Wrapper around `error' which also logs to sieve manage log.
+
+See `sieve-manage--append-to-log'."
+  (let ((msg (apply #'format
+                    (concat "sieve-manage/ERROR: " format-string)
+                    args)))
+    (sieve-manage--append-to-log msg "\n")
+    (error msg)))
+
+(defun sieve-manage-encode (utf8-string)
+  "Convert UTF8-STRING to managesieve protocol octets."
+  (encode-coding-string utf8-string 'raw-text t))
+
+(defun sieve-manage-decode (octets &optional buffer)
+  "Convert managesieve protocol OCTETS to utf-8 string.
+
+If optional BUFFER is non-nil, insert decoded string into BUFFER."
+  (when octets
+    ;; eol type unix is required to preserve "\r\n"
+    (decode-coding-string octets 'utf-8-unix t buffer)))
 
 (defun sieve-manage-make-process-buffer ()
   (with-current-buffer
@@ -175,22 +220,19 @@ Valid states are `closed', `initial', `nonauth', and 
`auth'.")
                                    sieve-manage-server
                                    sieve-manage-port))
     (mapc #'make-local-variable sieve-manage-local-variables)
-    (mm-enable-multibyte)
+    (set-buffer-multibyte nil)
+    (setq-local after-change-functions nil)
     (buffer-disable-undo)
     (current-buffer)))
 
 (defun sieve-manage-erase (&optional p buffer)
-  (let ((buffer (or buffer (current-buffer))))
-    (and sieve-manage-log
-        (with-current-buffer (get-buffer-create sieve-manage-log)
-          (mm-enable-multibyte)
-          (buffer-disable-undo)
-          (goto-char (point-max))
-          (insert-buffer-substring buffer (with-current-buffer buffer
-                                            (point-min))
-                                   (or p (with-current-buffer buffer
-                                           (point-max)))))))
-  (delete-region (point-min) (or p (point-max))))
+  (with-current-buffer (or buffer (current-buffer))
+    (let* ((start (point-min))
+           (end (or p (point-max)))
+           (logdata (buffer-substring-no-properties start end)))
+      (sieve-manage--append-to-log logdata)
+      (delete-region start end)
+      logdata)))
 
 (defun sieve-manage-open-server (server port &optional stream buffer)
   "Open network connection to SERVER on PORT.
@@ -202,6 +244,8 @@ Return the buffer associated with the connection."
                  (open-network-stream
                   "SIEVE" buffer server port
                   :type stream
+                  ;; eol type unix is required to preserve "\r\n"
+                  :coding 'raw-text-unix
                   :capability-command "CAPABILITY\r\n"
                   :end-of-command "^\\(OK\\|NO\\).*\n"
                   :success "^OK.*\n"
@@ -224,7 +268,7 @@ Return the buffer associated with the connection."
 ;; Authenticators
 (defun sieve-sasl-auth (buffer mech)
   "Login to server using the SASL MECH method."
-  (message "sieve: Authenticating using %s..." mech)
+  (sieve-manage--message "Authenticating using %s..." mech)
   (with-current-buffer buffer
     (let* ((auth-info (auth-source-search :host sieve-manage-server
                                           :port "sieve"
@@ -275,11 +319,15 @@ Return the buffer associated with the connection."
             (if (and (setq step (sasl-next-step client step))
                      (setq data (sasl-step-data step)))
                 ;; We got data for server but it's finished
-                (error "Server not ready for SASL data: %s" data)
+                (sieve-manage--error
+                 "Server not ready for SASL data: %s" data)
               ;; The authentication process is finished.
+              (sieve-manage--message "Logged in as %s using %s"
+                                     user-name mech)
               (throw 'done t)))
           (unless (stringp rsp)
-            (error "Server aborted SASL authentication: %s" (caddr rsp)))
+            (sieve-manage--error
+             "Server aborted SASL authentication: %s" (caddr rsp)))
           (sasl-step-set-data step (base64-decode-string rsp))
           (setq step (sasl-next-step client step))
           (sieve-manage-send
@@ -288,8 +336,7 @@ Return the buffer associated with the connection."
                        (base64-encode-string (sasl-step-data step)
                                              'no-line-break)
                        "\"")
-             ""))))
-      (message "sieve: Login using %s...done" mech))))
+             "")))))))
 
 (defun sieve-manage-cram-md5-p (buffer)
   (sieve-manage-capability "SASL" "CRAM-MD5" buffer))
@@ -353,7 +400,7 @@ to work in."
                                   sieve-manage-default-stream)
           sieve-manage-auth   (or auth
                                   sieve-manage-auth))
-    (message "sieve: Connecting to %s..." sieve-manage-server)
+    (sieve-manage--message "Connecting to %s..." sieve-manage-server)
     (sieve-manage-open-server sieve-manage-server
                               sieve-manage-port
                               sieve-manage-stream
@@ -368,7 +415,8 @@ to work in."
             (setq sieve-manage-auth auth)
             (cl-return)))
         (unless sieve-manage-auth
-          (error "Couldn't figure out authenticator for server")))
+          (sieve-manage--error
+           "Couldn't figure out authenticator for server")))
       (sieve-manage-erase)
       (current-buffer))))
 
@@ -433,11 +481,7 @@ If NAME is nil, return the full server list of 
capabilities."
 (defun sieve-manage-putscript (name content &optional buffer)
   (with-current-buffer (or buffer (current-buffer))
     (sieve-manage-send (format "PUTSCRIPT \"%s\" {%d+}%s%s" name
-                               ;; Here we assume that the coding-system will
-                               ;; replace each char with a single byte.
-                               ;; This is always the case if `content' is
-                               ;; a unibyte string.
-                              (length content)
+                              (length (sieve-manage-encode content))
                               sieve-manage-client-eol content))
     (sieve-manage-parse-okno)))
 
@@ -449,11 +493,10 @@ If NAME is nil, return the full server list of 
capabilities."
 (defun sieve-manage-getscript (name output-buffer &optional buffer)
   (with-current-buffer (or buffer (current-buffer))
     (sieve-manage-send (format "GETSCRIPT \"%s\"" name))
-    (let ((script (sieve-manage-parse-string)))
-      (sieve-manage-parse-crlf)
-      (with-current-buffer output-buffer
-       (insert script))
-      (sieve-manage-parse-okno))))
+    (sieve-manage-decode (sieve-manage-parse-string)
+                         output-buffer)
+    (sieve-manage-parse-crlf)
+    (sieve-manage-parse-okno)))
 
 (defun sieve-manage-setactive (name &optional buffer)
   (with-current-buffer (or buffer (current-buffer))
@@ -478,6 +521,9 @@ If NAME is nil, return the full server list of 
capabilities."
 (defun sieve-manage-ok-p (rsp)
   (string= (downcase (or (car-safe rsp) "")) "ok"))
 
+(defun sieve-manage-no-p (rsp)
+  (string= (downcase (or (car-safe rsp) "")) "no"))
+
 (defun sieve-manage-is-okno ()
   (when (looking-at (concat
                     "^\\(OK\\|NO\\)\\( (\\([^)]+\\))\\)?\\( \\(.*\\)\\)?"
@@ -528,7 +574,11 @@ to local variable `sieve-manage-capability'."
     (while (null rsp)
       (accept-process-output (get-buffer-process (current-buffer)) 1)
       (goto-char (point-min))
-      (setq rsp (sieve-manage-is-string)))
+      (unless (setq rsp (sieve-manage-is-string))
+        (when (sieve-manage-no-p (sieve-manage-is-okno))
+          ;; simple `error' is enough since `sieve-manage-erase'
+          ;; already adds the server response to the log
+          (error (sieve-manage-erase)))))
     (sieve-manage-erase (point))
     rsp))
 
@@ -540,7 +590,8 @@ to local variable `sieve-manage-capability'."
   (let (tmp rsp data)
     (while (null rsp)
       (while (null (or (setq rsp (sieve-manage-is-okno))
-                      (setq tmp (sieve-manage-is-string))))
+                       (setq tmp (sieve-manage-decode
+                                  (sieve-manage-is-string)))))
        (accept-process-output (get-buffer-process (current-buffer)) 1)
        (goto-char (point-min)))
       (when tmp
@@ -559,13 +610,9 @@ to local variable `sieve-manage-capability'."
       rsp)))
 
 (defun sieve-manage-send (cmdstr)
-  (setq cmdstr (concat cmdstr sieve-manage-client-eol))
-  (and sieve-manage-log
-       (with-current-buffer (get-buffer-create sieve-manage-log)
-        (mm-enable-multibyte)
-        (buffer-disable-undo)
-        (goto-char (point-max))
-        (insert cmdstr)))
+  (setq cmdstr (sieve-manage-encode
+                (concat cmdstr sieve-manage-client-eol)))
+  (sieve-manage--append-to-log cmdstr)
   (process-send-string sieve-manage-process cmdstr))
 
 (provide 'sieve-manage)
diff --git a/lisp/net/sieve-mode.el b/lisp/net/sieve-mode.el
index f62af03534..695a3235a7 100644
--- a/lisp/net/sieve-mode.el
+++ b/lisp/net/sieve-mode.el
@@ -200,7 +200,13 @@ Turning on Sieve mode runs `sieve-mode-hook'."
     (let ((depth (car (syntax-ppss))))
       (when (looking-at "[ \t]*}")
         (setq depth (1- depth)))
-      (indent-line-to (* 2 depth)))))
+      (indent-line-to (* 2 depth))))
+  ;; Skip to the end of the indentation if at the beginning of the
+  ;; line.
+  (when (save-excursion
+          (skip-chars-backward " \t")
+          (bolp))
+    (skip-chars-forward " \t")))
 
 (provide 'sieve-mode)
 
diff --git a/lisp/net/sieve.el b/lisp/net/sieve.el
index 3a6067ee10..c2faeaef54 100644
--- a/lisp/net/sieve.el
+++ b/lisp/net/sieve.el
@@ -152,7 +152,8 @@ require \"fileinto\";
   (interactive)
   (sieve-manage-close sieve-manage-buffer)
   (kill-buffer sieve-manage-buffer)
-  (kill-buffer (current-buffer)))
+  (when-let ((buffer (get-buffer sieve-buffer)))
+    (kill-buffer buffer)))
 
 (defun sieve-bury-buffer ()
   "Bury the Manage Sieve buffer without closing the connection."
diff --git a/lisp/net/tramp-adb.el b/lisp/net/tramp-adb.el
index b38b908edb..49cbf526ec 100644
--- a/lisp/net/tramp-adb.el
+++ b/lisp/net/tramp-adb.el
@@ -55,7 +55,7 @@ It is used for TCP/IP devices."
 (defconst tramp-adb-method "adb"
   "When this method name is used, forward all calls to Android Debug Bridge.")
 
-(defcustom tramp-adb-prompt (rx bol (* (not (any "#$\n\r"))) (any "#$") space)
+(defcustom tramp-adb-prompt (rx bol (* (not (any "#$\n\r"))) (any "#$") blank)
   "Regexp used as prompt in almquist shell."
   :type 'regexp
   :version "28.1"
@@ -71,20 +71,22 @@ It is used for TCP/IP devices."
   "Regexp for date time format in ls output."))
 
 (defconst tramp-adb-ls-date-regexp
-  (rx space (regexp tramp-adb-ls-date-year-regexp)
-      space (regexp tramp-adb-ls-date-time-regexp)
-      space)
+  (tramp-compat-rx
+   blank (regexp tramp-adb-ls-date-year-regexp)
+   blank (regexp tramp-adb-ls-date-time-regexp)
+   blank)
   "Regexp for date format in ls output.")
 
 (defconst tramp-adb-ls-toolbox-regexp
-  (rx bol (* space) (group (+ (any ".-" alpha)))               ; \1 permissions
-      (? (+ space) (+ digit))                        ; links (Android 7/toybox)
-      (* space) (group (+ (not space)))                                ; \2 
username
-      (+ space) (group (+ (not space)))                                ; \3 
group
-      (+ space) (group (+ digit))                              ; \4 size
-      (+ space) (group (regexp tramp-adb-ls-date-year-regexp)
-                space (regexp tramp-adb-ls-date-time-regexp))  ; \5 date
-      space (group (* nonl)) eol)                              ; \6 filename
+  (tramp-compat-rx
+   bol (* blank) (group (+ (any ".-" alpha)))                  ; \1 permissions
+   (? (+ blank) (+ digit))                           ; links (Android 7/toybox)
+   (* blank) (group (+ (not blank)))                           ; \2 username
+   (+ blank) (group (+ (not blank)))                           ; \3 group
+   (+ blank) (group (+ digit))                                 ; \4 size
+   (+ blank) (group (regexp tramp-adb-ls-date-year-regexp)
+             blank (regexp tramp-adb-ls-date-time-regexp))     ; \5 date
+   blank (group (* nonl)) eol)                                 ; \6 filename
   "Regexp for ls output.")
 
 ;;;###tramp-autoload
@@ -127,7 +129,7 @@ It is used for TCP/IP devices."
     (file-directory-p . tramp-handle-file-directory-p)
     (file-equal-p . tramp-handle-file-equal-p)
     (file-executable-p . tramp-adb-handle-file-executable-p)
-    (file-exists-p . tramp-handle-file-exists-p)
+    (file-exists-p . tramp-adb-handle-file-exists-p)
     (file-in-directory-p . tramp-handle-file-in-directory-p)
     (file-local-copy . tramp-adb-handle-file-local-copy)
     (file-locked-p . tramp-handle-file-locked-p)
@@ -180,6 +182,7 @@ It is used for TCP/IP devices."
     (temporary-file-directory . tramp-handle-temporary-file-directory)
     (tramp-get-home-directory . ignore)
     (tramp-get-remote-gid . tramp-adb-handle-get-remote-gid)
+    (tramp-get-remote-groups . tramp-adb-handle-get-remote-groups)
     (tramp-get-remote-uid . tramp-adb-handle-get-remote-uid)
     (tramp-set-file-uid-gid . ignore)
     (unhandled-file-name-directory . ignore)
@@ -218,7 +221,7 @@ arguments to pass to the OPERATION."
        (mapcar
         (lambda (line)
           (when (string-match
-                 (rx bol (group (+ (not space))) (+ space) "device" eol) line)
+                 (rx bol (group (+ (not blank))) (+ blank) "device" eol) line)
             ;; Replace ":" by "#".
             `(nil ,(tramp-compat-string-replace
                     ":" tramp-prefix-port-format (match-string 1 line)))))
@@ -235,10 +238,10 @@ arguments to pass to the OPERATION."
        (goto-char (point-min))
        (forward-line)
        (when (looking-at
-              (rx (* space) (+ (not space))
-                  (+ space) (group (+ digit))
-                  (+ space) (group (+ digit))
-                  (+ space) (group (+ digit))))
+              (rx (* blank) (+ (not blank))
+                  (+ blank) (group (+ digit))
+                  (+ blank) (group (+ digit))
+                  (+ blank) (group (+ digit))))
          ;; The values are given as 1k numbers, so we must change
          ;; them to number of bytes.
          (list (* 1024 (string-to-number (match-string 1)))
@@ -278,10 +281,10 @@ arguments to pass to the OPERATION."
               (name (match-string 6))
               (symlink-target
                (and is-symlink
-                    (cadr (split-string name (rx (group (| " -> " "\n"))))))))
+                    (cadr (split-string name (rx (| " -> " "\n")))))))
          (push (list
                 (if is-symlink
-                    (car (split-string name (rx (group (| " -> " "\n")))))
+                    (car (split-string name (rx (| " -> " "\n"))))
                   name)
                 (or is-dir symlink-target)
                 1     ;link-count
@@ -323,8 +326,8 @@ arguments to pass to the OPERATION."
                     (tramp-shell-quote-argument
                      (tramp-compat-file-name-concat localname ".."))))
          (tramp-compat-replace-regexp-in-region
-          (rx (literal (tramp-compat-file-name-unquote
-                        (file-name-as-directory localname))))
+          (tramp-compat-rx (literal (tramp-compat-file-name-unquote
+                                     (file-name-as-directory localname))))
           "" (point-min))
          (widen)))
       (tramp-adb-sh-fix-ls-output)
@@ -362,12 +365,14 @@ Emacs dired can't find files."
     (goto-char (point-min))
     (while
        (search-forward-regexp
-        (rx space (group space (regexp tramp-adb-ls-date-year-regexp) space))
+        (tramp-compat-rx
+         blank (group blank (regexp tramp-adb-ls-date-year-regexp) blank))
         nil t)
       (replace-match "0\\1" "\\1" nil)
       ;; Insert missing "/".
       (when (looking-at-p
-            (rx (regexp tramp-adb-ls-date-time-regexp) (+ space) eol))
+            (tramp-compat-rx
+             (regexp tramp-adb-ls-date-time-regexp) (+ blank) eol))
        (end-of-line)
        (insert "/")))
     ;; Sort entries.
@@ -466,7 +471,7 @@ Emacs dired can't find files."
             nil
             (mapcar
              (lambda (l)
-               (and (not (string-match-p (rx bol (* space) eol) l)) l))
+               (and (not (string-match-p (rx bol (* blank) eol) l)) l))
              (split-string (buffer-string) "\n")))))))))))
 
 (defun tramp-adb-handle-file-local-copy (filename)
@@ -486,26 +491,52 @@ Emacs dired can't find files."
 
 (defun tramp-adb-handle-file-executable-p (filename)
   "Like `file-executable-p' for Tramp files."
-  (with-parsed-tramp-file-name filename nil
+  (with-parsed-tramp-file-name (expand-file-name filename) nil
     (with-tramp-file-property v localname "file-executable-p"
-      (tramp-adb-send-command-and-check
-       v (format "test -x %s" (tramp-shell-quote-argument localname))))))
+      ;; Examine `file-attributes' cache to see if request can be
+      ;; satisfied without remote operation.
+      (if (tramp-file-property-p v localname "file-attributes")
+         (or (tramp-check-cached-permissions v ?x)
+             (tramp-check-cached-permissions v ?s))
+       (tramp-adb-send-command-and-check
+        v (format "test -x %s" (tramp-shell-quote-argument localname)))))))
+
+(defun tramp-adb-handle-file-exists-p (filename)
+  "Like `file-exists-p' for Tramp files."
+  ;; `file-exists-p' is used as predicate in file name completion.
+  ;; We don't want to run it when `non-essential' is t, or there is
+  ;; no connection process yet.
+  (when (tramp-connectable-p filename)
+    (with-parsed-tramp-file-name (expand-file-name filename) nil
+      (with-tramp-file-property v localname "file-exists-p"
+       (if (tramp-file-property-p v localname "file-attributes")
+           (not (null (tramp-get-file-property v localname "file-attributes")))
+         (tramp-adb-send-command-and-check
+          v (format "test -e %s" (tramp-shell-quote-argument localname))))))))
 
 (defun tramp-adb-handle-file-readable-p (filename)
   "Like `file-readable-p' for Tramp files."
-  (with-parsed-tramp-file-name filename nil
+  (with-parsed-tramp-file-name (expand-file-name filename) nil
     (with-tramp-file-property v localname "file-readable-p"
-      (or (tramp-handle-file-readable-p filename)
-         (tramp-adb-send-command-and-check
-          v (format "test -r %s" (tramp-shell-quote-argument localname)))))))
+      ;; Examine `file-attributes' cache to see if request can be
+      ;; satisfied without remote operation.
+      (if (tramp-file-property-p v localname "file-attributes")
+         (tramp-handle-file-readable-p filename)
+       (tramp-adb-send-command-and-check
+        v (format "test -r %s" (tramp-shell-quote-argument localname)))))))
 
 (defun tramp-adb-handle-file-writable-p (filename)
   "Like `file-writable-p' for Tramp files."
-  (with-parsed-tramp-file-name filename nil
+  (with-parsed-tramp-file-name (expand-file-name filename) nil
     (with-tramp-file-property v localname "file-writable-p"
       (if (file-exists-p filename)
-         (tramp-adb-send-command-and-check
-          v (format "test -w %s" (tramp-shell-quote-argument localname)))
+         (if (tramp-file-property-p v localname "file-attributes")
+             ;; Examine `file-attributes' cache to see if request can
+             ;; be satisfied without remote operation.
+             (tramp-check-cached-permissions v ?w)
+           (tramp-adb-send-command-and-check
+            v (format "test -w %s" (tramp-shell-quote-argument localname))))
+       ;; If file doesn't exist, check if directory is writable.
        (and
         (file-directory-p (file-name-directory filename))
         (file-writable-p (file-name-directory filename)))))))
@@ -560,10 +591,9 @@ Emacs dired can't find files."
       ;; (introduced in POSIX.1-2008) fails.
       (tramp-adb-send-command-and-check
        v (format
-         (eval-when-compile
-           (concat "touch -d %s %s %s 2>%s || "
-                   "touch -d %s %s %s 2>%s || "
-                   "touch -t %s %s %s"))
+         (concat "touch -d %s %s %s 2>%s || "
+                 "touch -d %s %s %s 2>%s || "
+                 "touch -t %s %s %s")
          (format-time-string "%Y-%m-%dT%H:%M:%S.%NZ" time t)
          nofollow quoted-name (tramp-get-remote-null-device v)
          (format-time-string "%Y-%m-%dT%H:%M:%S" time t)
@@ -718,9 +748,9 @@ PRESERVE-UID-GID and PRESERVE-EXTENDED-ATTRIBUTES are 
completely ignored."
       (setcar result 0)
       (dolist (line signals)
        (when (string-match
-              (rx bol (* space) (group (+ digit))
-                  (+ space) (+ (not space))
-                  (+ space) (group alpha (* nonl)) eol)
+              (rx bol (* blank) (group (+ digit))
+                  (+ blank) (+ (not blank))
+                  (+ blank) (group alpha (* nonl)) eol)
               line)
          (setcar
           (nthcdr (string-to-number (match-string 1 line)) result)
@@ -918,7 +948,7 @@ implementation will be used."
                 (i 0)
                 p)
 
-           (when (string-match-p (rx multibyte) command)
+           (when (string-match-p (tramp-compat-rx multibyte) command)
              (tramp-error
               v 'file-error "Cannot apply multi-byte command `%s'" command))
 
@@ -1040,32 +1070,23 @@ implementation will be used."
 (defun tramp-adb-handle-get-remote-uid (vec id-format)
   "Like `tramp-get-remote-uid' for Tramp files.
  ID-FORMAT valid values are `string' and `integer'."
- ;; The result is cached in `tramp-get-remote-uid'.
-  (tramp-adb-send-command
-   vec
-   (format "id -u%s %s"
-          (if (equal id-format 'integer) "" "n")
-          (if (equal id-format 'integer)
-              "" "| sed -e s/^/\\\"/ -e s/\\$/\\\"/")))
-  (with-current-buffer (tramp-get-connection-buffer vec)
-    ;; Read the expression.
-    (goto-char (point-min))
-    (read (current-buffer))))
+  (tramp-adb-send-command vec "id")
+  (tramp-read-id-output vec)
+  (tramp-get-connection-property vec (format "uid-%s" id-format)))
 
 (defun tramp-adb-handle-get-remote-gid (vec id-format)
   "Like `tramp-get-remote-gid' for Tramp files.
 ID-FORMAT valid values are `string' and `integer'."
-  ;; The result is cached in `tramp-get-remote-gid'.
-  (tramp-adb-send-command
-   vec
-   (format "id -g%s %s"
-          (if (equal id-format 'integer) "" "n")
-          (if (equal id-format 'integer)
-              "" "| sed -e s/^/\\\"/ -e s/\\$/\\\"/")))
-  (with-current-buffer (tramp-get-connection-buffer vec)
-    ;; Read the expression.
-    (goto-char (point-min))
-    (read (current-buffer))))
+  (tramp-adb-send-command vec "id")
+  (tramp-read-id-output vec)
+  (tramp-get-connection-property vec (format "gid-%s" id-format)))
+
+(defun tramp-adb-handle-get-remote-groups (vec id-format)
+  "Like `tramp-get-remote-groups' for Tramp files.
+ID-FORMAT valid values are `string' and `integer'."
+  (tramp-adb-send-command vec "id")
+  (tramp-read-id-output vec)
+  (tramp-get-connection-property vec (format "groups-%s" id-format)))
 
 (defun tramp-adb-get-device (vec)
   "Return full host name from VEC to be used in shell execution.
@@ -1119,7 +1140,7 @@ error and non-nil on success."
 
 (defun tramp-adb-send-command (vec command &optional neveropen nooutput)
   "Send the COMMAND to connection VEC."
-  (if (string-match-p (rx multibyte) command)
+  (if (string-match-p (tramp-compat-rx multibyte) command)
       ;; Multibyte codepoints with four bytes are not supported at
       ;; least by toybox.
 
@@ -1143,7 +1164,7 @@ error and non-nil on success."
          ;; We can't use stty to disable echo of command.  stty is said
          ;; to be added to toybox 0.7.6.  busybox shall have it, but this
          ;; isn't used any longer for Android.
-         (delete-matching-lines (rx (literal command)))
+         (delete-matching-lines (tramp-compat-rx bol (literal command) eol))
          ;; When the local machine is W32, there are still trailing ^M.
          ;; There must be a better solution by setting the correct coding
          ;; system, but this requires changes in core Tramp.
@@ -1266,7 +1287,7 @@ connection if a previous connection has died for some 
reason."
 
            ;; Change prompt.
            (tramp-set-connection-property
-            p "prompt" (rx "///" (literal prompt) "#$"))
+            p "prompt" (tramp-compat-rx "///" (literal prompt) "#$"))
            (tramp-adb-send-command
             vec (format "PS1=\"///\"\"%s\"\"#$\"" prompt))
 
@@ -1284,11 +1305,10 @@ connection if a previous connection has died for some 
reason."
            (tramp-message vec 5 "Checking system information")
            (tramp-adb-send-command
             vec
-            (eval-when-compile
-              (concat
-               "echo \\\"`getprop ro.product.model` "
-               "`getprop ro.product.version` "
-               "`getprop ro.build.version.release`\\\"")))
+            (concat
+             "echo \\\"`getprop ro.product.model` "
+             "`getprop ro.product.version` "
+             "`getprop ro.build.version.release`\\\""))
            (let ((old-getprop (tramp-get-connection-property vec "getprop"))
                  (new-getprop
                   (tramp-set-connection-property
diff --git a/lisp/net/tramp-archive.el b/lisp/net/tramp-archive.el
index 0d931b42da..646ae86452 100644
--- a/lisp/net/tramp-archive.el
+++ b/lisp/net/tramp-archive.el
@@ -112,8 +112,10 @@
 (eval-when-compile (require 'cl-lib))
 ;; Sometimes, compilation fails with "Variable binding depth exceeds
 ;; max-specpdl-size".  Shall be fixed in Emacs 27.
-(eval-and-compile
-  (let ((max-specpdl-size (* 2 max-specpdl-size))) (require 'tramp-gvfs)))
+(with-no-warnings ;; max-specpdl-size
+  (eval-and-compile
+    (let ((max-specpdl-size (* 2 max-specpdl-size)))
+      (require 'tramp-gvfs))))
 
 (autoload 'dired-uncache "dired")
 (autoload 'url-tramp-convert-url-to-tramp "url-tramp")
@@ -184,21 +186,22 @@ It must be supported by libarchive(3).")
 ;;;###autoload
 (progn (defmacro tramp-archive-autoload-file-name-regexp ()
   "Regular expression matching archive file names."
-  '(rx bos
-       ;; \1
-       (group
-       (+ nonl)
-       ;; Default suffixes ...
-       "." (regexp (regexp-opt tramp-archive-suffixes))
-       ;; ... with compression.
-       (? "." (regexp (regexp-opt tramp-archive-compression-suffixes))))
-       ;; \2
-       (group "/" (* nonl))
-       eos)))
+  `(rx
+    bos
+    ;; This group is used in `tramp-archive-file-name-archive'.
+    (group
+     (+ nonl)
+     ;; Default suffixes ...
+     "." ,(cons '| tramp-archive-suffixes)
+     ;; ... with compression.
+     (? "." ,(cons '| tramp-archive-compression-suffixes)))
+    ;; This group is used in `tramp-archive-file-name-localname'.
+    (group "/" (* nonl))
+    eos)))
 
 (put #'tramp-archive-autoload-file-name-regexp 'tramp-autoload t)
 
-;; In older Emacsen (prior 27.1), `tramp-archive-autoload-file-name-regexp'
+;; In older Emacs (prior 27.1), `tramp-archive-autoload-file-name-regexp'
 ;; is not autoloaded.  So we cannot expect it to be known in
 ;; tramp-loaddefs.el.  But it exists, when tramp-archive.el is loaded.
 ;;;###tramp-autoload
@@ -297,6 +300,7 @@ It must be supported by libarchive(3).")
     (temporary-file-directory . tramp-archive-handle-temporary-file-directory)
     (tramp-get-home-directory . ignore)
     (tramp-get-remote-gid . ignore)
+    (tramp-get-remote-groups . ignore)
     (tramp-get-remote-uid . ignore)
     (tramp-set-file-uid-gid . ignore)
     (unhandled-file-name-directory . ignore)
@@ -328,7 +332,7 @@ arguments to pass to the OPERATION."
 
 ;; Starting with Emacs 29, `tramp-archive-file-name-handler' is
 ;; autoloaded.  But it must still be in tramp-loaddefs.el for older
-;; Emacsen.
+;; versions of Emacs.
 ;;;###autoload(autoload 'tramp-archive-file-name-handler "tramp-archive")
 ;;;###tramp-autoload
 (defun tramp-archive-file-name-handler (operation &rest args)
@@ -341,6 +345,7 @@ arguments to pass to the OPERATION."
           (tramp-register-file-name-handlers)
           (tramp-archive-run-real-handler operation args))
 
+      (with-no-warnings ;; max-specpdl-size
       (let* ((filename (apply #'tramp-archive-file-name-for-operation
                              operation args))
             (archive (tramp-archive-file-name-archive filename))
@@ -374,7 +379,7 @@ arguments to pass to the OPERATION."
              (setq args (cons operation args)))
            (if fn
                (save-match-data (apply (cdr fn) args))
-             (tramp-archive-run-real-handler operation args)))))))
+             (tramp-archive-run-real-handler operation args))))))))
 
 ;;;###autoload
 (progn (defun tramp-archive-autoload-file-name-handler (operation &rest args)
diff --git a/lisp/net/tramp-cache.el b/lisp/net/tramp-cache.el
index 6a3e60f703..4d7d35a4de 100644
--- a/lisp/net/tramp-cache.el
+++ b/lisp/net/tramp-cache.el
@@ -28,7 +28,7 @@
 ;; An implementation of information caching for remote files.
 
 ;; Each connection, identified by a `tramp-file-name' structure or by
-;; a process, has a unique cache.  We distinguish 4 kind of caches,
+;; a process, has a unique cache.  We distinguish 6 kind of caches,
 ;; depending on the key:
 ;;
 ;; - localname is nil.  These are reusable properties.  Examples:
@@ -37,10 +37,10 @@
 ;;   host when starting a Perl script.  These properties are saved in
 ;;   the file `tramp-persistency-file-name'.
 ;;
-;; - localname is a string.  These are temporary properties, which are
-;;   related to the file localname is referring to.  Examples:
-;;   "file-exists-p" is t or nil, depending on the file existence, or
-;;   "file-attributes" caches the result of the function
+;; - localname is an absolute file name.  These are temporary
+;;   properties, which are related to the file localname is referring
+;;   to.  Examples: "file-exists-p" is t or nil, depending on the file
+;;   existence, or "file-attributes" caches the result of the function
 ;;   `file-attributes'.  These entries have a timestamp, and they
 ;;   expire after `remote-file-name-inhibit-cache' seconds if this
 ;;   variable is set.
@@ -56,6 +56,10 @@
 ;;   "{uid,gid}-{integer,string}" are the local uid and gid, and
 ;;   "locale" is the used shell locale.
 ;;
+;; - The key is `tramp-cache-version'.  It keeps the Tramp version the
+;;   cache data was produced with.  If the cache is read by another
+;;   Tramp version, it is flushed.
+;;
 ;; - The key is `tramp-cache-undefined'.  All functions return the
 ;;   expected values, but nothing is cached.
 
@@ -105,6 +109,10 @@ details see the info pages."
   :group 'tramp
   :type 'file)
 
+;;;###tramp-autoload
+(defconst tramp-cache-version (make-tramp-file-name :method "cache")
+"Virtual connection vector for Tramp version.")
+
 (defvar tramp-cache-data-changed nil
   "Whether persistent cache data have been changed.")
 
@@ -135,39 +143,41 @@ If KEY is `tramp-cache-undefined', don't create anything, 
and return nil."
 Return DEFAULT if not set."
   ;; Unify localname.  Remove hop from `tramp-file-name' structure.
   (setq key (tramp-file-name-unify key file))
-  (let* ((hash (tramp-get-hash-table key))
-        (cached (and (hash-table-p hash) (gethash property hash)))
-        (cached-at (and (consp cached) (format-time-string "%T" (car cached))))
-        (value default)
-        cache-used)
-
-    (when ;; We take the value only if there is any, and
-         ;; `remote-file-name-inhibit-cache' indicates that it is
-         ;; still valid.  Otherwise, DEFAULT is set.
-       (and (consp cached)
-            (or (null remote-file-name-inhibit-cache)
-                (and (integerp remote-file-name-inhibit-cache)
-                     (time-less-p
-                      nil
-                      (time-add (car cached) remote-file-name-inhibit-cache)))
-                (and (consp remote-file-name-inhibit-cache)
-                     (time-less-p
-                      remote-file-name-inhibit-cache (car cached)))))
-      (setq value (cdr cached)
-           cache-used t))
-
-    (tramp-message
-     key 8 "%s %s %s; inhibit: %s; cache used: %s; cached at: %s"
-     (tramp-file-name-localname key)
-     property value remote-file-name-inhibit-cache cache-used cached-at)
-    ;; For analysis purposes, count the number of getting this file attribute.
-    (when (>= tramp-verbose 10)
-      (let* ((var (intern (concat "tramp-cache-get-count-" property)))
-            (val (or (and (boundp var) (numberp (symbol-value var))
-                          (symbol-value var))
-                     0)))
-       (set var (1+ val))))
-    value))
+  (if (eq key tramp-cache-undefined) default
+    (let* ((hash (tramp-get-hash-table key))
+          (cached (and (hash-table-p hash) (gethash property hash)))
+          (cached-at
+           (and (consp cached) (format-time-string "%T" (car cached))))
+          (value default)
+          cache-used)
+
+      (when ;; We take the value only if there is any, and
+           ;; `remote-file-name-inhibit-cache' indicates that it is
+           ;; still valid.  Otherwise, DEFAULT is set.
+         (and (consp cached)
+              (or (null remote-file-name-inhibit-cache)
+                  (and (integerp remote-file-name-inhibit-cache)
+                       (time-less-p
+                        nil
+                        (time-add (car cached) 
remote-file-name-inhibit-cache)))
+                  (and (consp remote-file-name-inhibit-cache)
+                       (time-less-p
+                        remote-file-name-inhibit-cache (car cached)))))
+       (setq value (cdr cached)
+             cache-used t))
+
+      (tramp-message
+       key 8 "%s %s %s; inhibit: %s; cache used: %s; cached at: %s"
+       (tramp-file-name-localname key)
+       property value remote-file-name-inhibit-cache cache-used cached-at)
+      ;; For analysis purposes, count the number of getting this file 
attribute.
+      (when (>= tramp-verbose 10)
+       (let* ((var (intern (concat "tramp-cache-get-count-" property)))
+              (val (or (and (boundp var) (numberp (symbol-value var))
+                            (symbol-value var))
+                       0)))
+         (set var (1+ val))))
+      value)))
 
 (add-hook 'tramp-cache-unload-hook
          (lambda ()
@@ -180,19 +190,20 @@ Return DEFAULT if not set."
 Return VALUE."
   ;; Unify localname.  Remove hop from `tramp-file-name' structure.
   (setq key (tramp-file-name-unify key file))
-  (let ((hash (tramp-get-hash-table key)))
-    ;; We put the timestamp there.
-    (puthash property (cons (current-time) value) hash)
-    (tramp-message
-     key 8 "%s %s %s" (tramp-file-name-localname key) property value)
-    ;; For analysis purposes, count the number of setting this file attribute.
-    (when (>= tramp-verbose 10)
-      (let* ((var (intern (concat "tramp-cache-set-count-" property)))
-            (val (or (and (boundp var) (numberp (symbol-value var))
-                          (symbol-value var))
-                     0)))
-       (set var (1+ val))))
-    value))
+  (if (eq key tramp-cache-undefined) value
+    (let ((hash (tramp-get-hash-table key)))
+      ;; We put the timestamp there.
+      (puthash property (cons (current-time) value) hash)
+      (tramp-message
+       key 8 "%s %s %s" (tramp-file-name-localname key) property value)
+      ;; For analysis purposes, count the number of setting this file 
attribute.
+      (when (>= tramp-verbose 10)
+       (let* ((var (intern (concat "tramp-cache-set-count-" property)))
+              (val (or (and (boundp var) (numberp (symbol-value var))
+                            (symbol-value var))
+                       0)))
+         (set var (1+ val))))
+      value)))
 
 (add-hook 'tramp-cache-unload-hook
          (lambda ()
@@ -202,19 +213,22 @@ Return VALUE."
 ;;;###tramp-autoload
 (defun tramp-file-property-p (key file property)
   "Check whether PROPERTY of FILE is defined in the cache context of KEY."
-  (not (eq (tramp-get-file-property key file property tramp-cache-undefined)
-          tramp-cache-undefined)))
+  (and
+   (not (eq key tramp-cache-undefined))
+   (not (eq (tramp-get-file-property key file property tramp-cache-undefined)
+           tramp-cache-undefined))))
 
 ;;;###tramp-autoload
 (defun tramp-flush-file-property (key file property)
   "Remove PROPERTY of FILE in the cache context of KEY."
   ;; Unify localname.  Remove hop from `tramp-file-name' structure.
   (setq key (tramp-file-name-unify key file))
-  (remhash property (tramp-get-hash-table key))
-  (tramp-message key 8 "%s %s" (tramp-file-name-localname key) property)
-  (when (>= tramp-verbose 10)
-    (let ((var (intern (concat "tramp-cache-set-count-" property))))
-      (makunbound var))))
+  (unless (eq key tramp-cache-undefined)
+    (remhash property (tramp-get-hash-table key))
+    (tramp-message key 8 "%s %s" (tramp-file-name-localname key) property)
+    (when (>= tramp-verbose 10)
+      (let ((var (intern (concat "tramp-cache-set-count-" property))))
+       (makunbound var)))))
 
 (defun tramp-flush-file-upper-properties (key file)
   "Remove some properties of FILE's upper directory."
@@ -224,12 +238,14 @@ Return VALUE."
               (file (directory-file-name file)))
       ;; Unify localname.  Remove hop from `tramp-file-name' structure.
       (setq key (tramp-file-name-unify key file))
-      (dolist (property (hash-table-keys (tramp-get-hash-table key)))
-       (when (string-match-p
-              (rx
-               bos (| "directory-" "file-name-all-completions" "file-entries"))
-              property)
-         (tramp-flush-file-property key file property))))))
+      (unless (eq key tramp-cache-undefined)
+       (dolist (property (hash-table-keys (tramp-get-hash-table key)))
+         (when (string-match-p
+                (rx
+                 bos (| "directory-" "file-name-all-completions"
+                        "file-entries"))
+                property)
+           (tramp-flush-file-property key file property)))))))
 
 ;;;###tramp-autoload
 (defun tramp-flush-file-properties (key file)
@@ -237,14 +253,15 @@ Return VALUE."
   (let ((truename (tramp-get-file-property key file "file-truename")))
     ;; Unify localname.  Remove hop from `tramp-file-name' structure.
     (setq key (tramp-file-name-unify key file))
-    (tramp-message key 8 "%s" (tramp-file-name-localname key))
-    (remhash key tramp-cache-data)
-    ;; Remove file properties of symlinks.
-    (when (and (stringp truename)
-              (not (string-equal file (directory-file-name truename))))
-      (tramp-flush-file-properties key truename))
-    ;; Remove selected properties of upper directory.
-    (tramp-flush-file-upper-properties key file)))
+    (unless (eq key tramp-cache-undefined)
+      (tramp-message key 8 "%s" (tramp-file-name-localname key))
+      (remhash key tramp-cache-data)
+      ;; Remove file properties of symlinks.
+      (when (and (stringp truename)
+                (not (string-equal file (directory-file-name truename))))
+       (tramp-flush-file-properties key truename))
+      ;; Remove selected properties of upper directory.
+      (tramp-flush-file-upper-properties key file))))
 
 ;;;###tramp-autoload
 (defun tramp-flush-directory-properties (key directory)
@@ -278,7 +295,7 @@ Remove also properties of all files in subdirectories."
 This is suppressed for temporary buffers."
   (save-match-data
     (unless (or (null (buffer-name))
-               (string-match-p (rx bos (| " " "*")) (buffer-name)))
+               (string-match-p (rx bos (| blank "*")) (buffer-name)))
       (let ((bfn (if (stringp (buffer-file-name))
                     (buffer-file-name)
                   default-directory))
@@ -623,9 +640,16 @@ for all methods.  Resulting data are derived from 
connection history."
                ;; initialized properly by side effect.
                (unless (tramp-connection-property-p key (car item))
                  (tramp-set-connection-property key (pop item) (car item)))))))
+       ;; Check Tramp version.  Clear cache in case of mismatch.
+       (unless (string-equal
+                (tramp-get-connection-property
+                 tramp-cache-version "tramp-version" "")
+                tramp-version)
+         (signal 'file-error nil))
        (setq tramp-cache-data-changed nil))
     (file-error
-     ;; Most likely because the file doesn't exist yet.  No message.
+     ;; Most likely because the file doesn't exist yet, or the Tramp
+     ;; version doesn't match.  No message.
      (clrhash tramp-cache-data))
     (error
      ;; File is corrupted.
@@ -633,6 +657,9 @@ for all methods.  Resulting data are derived from 
connection history."
              tramp-persistency-file-name (error-message-string err))
      (clrhash tramp-cache-data))))
 
+;; Initialize the cache version.
+(tramp-set-connection-property tramp-cache-version "tramp-version" 
tramp-version)
+
 (add-hook 'tramp-unload-hook
          (lambda ()
            (unload-feature 'tramp-cache 'force)))
diff --git a/lisp/net/tramp-cmds.el b/lisp/net/tramp-cmds.el
index a7ac135266..0442fa7409 100644
--- a/lisp/net/tramp-cmds.el
+++ b/lisp/net/tramp-cmds.el
@@ -179,6 +179,10 @@ This includes password cache, file cache, connection 
cache, buffers."
   ;; Flush file and connection cache.
   (clrhash tramp-cache-data)
 
+  ;; Initialize the cache version.
+  (tramp-set-connection-property
+   tramp-cache-version "tramp-version" tramp-version)
+
   ;; Remove ad-hoc proxies.
   (let ((proxies tramp-default-proxies-alist))
     (while proxies
@@ -355,7 +359,7 @@ The remote connection identified by SOURCE is flushed by
                      (dir (tramp-rename-read-file-name-dir default))
                      (init (tramp-rename-read-file-name-init default))
                      (tramp-ignored-file-name-regexp
-                      (regexp-quote (file-remote-p source))))
+                      (tramp-compat-rx (literal (file-remote-p source)))))
                 (read-file-name-default
                  "Enter new Tramp connection: "
                  dir default 'confirm init #'file-directory-p)))))
@@ -466,7 +470,7 @@ For details, see `tramp-rename-files'."
                      (dir (tramp-rename-read-file-name-dir default))
                      (init (tramp-rename-read-file-name-init default))
                      (tramp-ignored-file-name-regexp
-                      (regexp-quote (file-remote-p source))))
+                      (tramp-compat-rx (literal (file-remote-p source)))))
                 (read-file-name-default
                  (format "Change Tramp connection `%s': " source)
                  dir default 'confirm init #'file-directory-p)))))
@@ -621,10 +625,11 @@ buffer in your bug report.
     (unless (hash-table-p val)
       ;; Remove string quotation.
       (when (looking-at
-            (rx bol (group (* anychar)) "\""          ;; \1 "
-                (group "(base64-decode-string ") "\\" ;; \2 \
-                (group "\"" (* anychar)) "\\"         ;; \3 \
-                (group "\")") "\"" eol))              ;; \4 "
+            (tramp-compat-rx
+             bol (group (* anychar)) "\""          ;; \1 "
+             (group "(base64-decode-string ") "\\" ;; \2 \
+             (group "\"" (* anychar)) "\\"         ;; \3 \
+             (group "\")") "\"" eol))              ;; \4 "
        (replace-match "\\1\\2\\3\\4")
        (beginning-of-line)
        (insert " ;; Variable encoded due to non-printable characters.\n")))
diff --git a/lisp/net/tramp-compat.el b/lisp/net/tramp-compat.el
index b7c0a3113e..a1d1d284ed 100644
--- a/lisp/net/tramp-compat.el
+++ b/lisp/net/tramp-compat.el
@@ -36,6 +36,7 @@
 (require 'shell)
 (require 'subr-x)
 
+(declare-function tramp-compat-rx "tramp")
 (declare-function tramp-error "tramp")
 (declare-function tramp-file-name-handler "tramp")
 (declare-function tramp-tramp-file-p "tramp")
@@ -49,6 +50,13 @@
   (warn "Tramp has been compiled with Emacs %s, this is Emacs %s"
        tramp-compat-emacs-compiled-version emacs-version))
 
+(with-eval-after-load 'docker-tramp
+  (warn (concat "Package `docker-tramp' has been obsoleted, "
+               "please use integrated package `tramp-container'")))
+(with-eval-after-load 'kubernetes-tramp
+  (warn (concat "Package `kubernetes-tramp' has been obsoleted, "
+               "please use integrated package `tramp-container'")))
+
 ;; For not existing functions, obsolete functions, or functions with a
 ;; changed argument list, there are compiler warnings.  We want to
 ;; avoid them in cases we know what we do.
@@ -180,6 +188,50 @@ CONDITION can also be a list of error conditions."
   (declare (debug t) (indent 1))
   `(condition-case nil (progn ,@body) (,condition nil)))
 
+;; `rx' in Emacs 26 doesn't know the `literal', `anychar' and
+;; `multibyte' constructs.  The `not' construct requires an `any'
+;; construct as argument.  The `regexp' construct requires a literal
+;; string.
+(defvar tramp-compat-rx--runtime-params)
+
+(defun tramp-compat-rx--transform-items (items)
+  (mapcar #'tramp-compat-rx--transform-item items))
+
+;; There is an error in Emacs 26.  `(rx "a" (? ""))' => "a?".
+;; We must protect the string in regexp and literal, therefore.
+(defun tramp-compat-rx--transform-item (item)
+  (pcase item
+    ('anychar 'anything)
+    ('multibyte 'nonascii)
+    (`(not ,expr)
+     (if (consp expr) item (list 'not (list 'any expr))))
+    (`(regexp ,expr)
+     (setq tramp-compat-rx--runtime-params t)
+     `(regexp ,(list '\, `(concat "\\(?:" ,expr "\\)"))))
+    (`(literal ,expr)
+     (setq tramp-compat-rx--runtime-params t)
+     `(regexp ,(list '\, `(concat "\\(?:" (regexp-quote ,expr) "\\)"))))
+    (`(eval . ,_) item)
+    (`(,head . ,rest) (cons head (tramp-compat-rx--transform-items rest)))
+    (_ item)))
+
+(defun tramp-compat-rx--transform (items)
+  (let* ((tramp-compat-rx--runtime-params nil)
+         (new-rx (cons ': (tramp-compat-rx--transform-items items))))
+    (if tramp-compat-rx--runtime-params
+        `(rx-to-string ,(list '\` new-rx) t)
+      (rx-to-string new-rx t))))
+
+(if (ignore-errors (rx-to-string '(literal "a"))) ;; Emacs 27+.
+    (defalias 'tramp-compat-rx #'rx)
+  (defmacro tramp-compat-rx (&rest items)
+    (tramp-compat-rx--transform items)))
+
+;; This is needed for compilation in the Emacs source tree.
+;;;###autoload (defalias 'tramp-compat-rx #'rx)
+
+(put #'tramp-compat-rx 'tramp-autoload t)
+
 ;; `file-modes', `set-file-modes' and `set-file-times' got argument
 ;; FLAG in Emacs 28.1.
 (defalias 'tramp-compat-file-modes
@@ -237,7 +289,7 @@ CONDITION can also be a list of error conditions."
     (lambda (from-string to-string in-string)
       (let (case-fold-search)
         (replace-regexp-in-string
-         (rx (literal from-string)) to-string in-string t t)))))
+         (regexp-quote from-string) to-string in-string t t)))))
 
 ;; Function `string-search' is new in Emacs 28.1.
 (defalias 'tramp-compat-string-search
@@ -245,7 +297,7 @@ CONDITION can also be a list of error conditions."
       #'string-search
     (lambda (needle haystack &optional start-pos)
       (let (case-fold-search)
-        (string-match-p (rx (literal needle)) haystack start-pos)))))
+        (string-match-p (regexp-quote needle) haystack start-pos)))))
 
 ;; Function `make-lock-file-name' is new in Emacs 28.1.
 (defalias 'tramp-compat-make-lock-file-name
diff --git a/lisp/net/tramp-container.el b/lisp/net/tramp-container.el
new file mode 100644
index 0000000000..e104babed2
--- /dev/null
+++ b/lisp/net/tramp-container.el
@@ -0,0 +1,190 @@
+;;; tramp-container.el --- Tramp integration for Docker-like containers  -*- 
lexical-binding: t; -*-
+
+;; Copyright © 2022 Free Software Foundation, Inc.
+
+;; Author: Brian Cully <bjc@kublai.com>
+;; Keywords: comm, processes
+;; Package: tramp
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; Allows Tramp access to environments provided by Docker and similar
+;; programs.
+;;
+;; ## Usage
+;;
+;; Open a file on a running Docker container:
+;;
+;;     C-x C-f /docker:USER@CONTAINER:/path/to/file
+;;
+;; or Podman:
+;;
+;;     C-x C-f /podman:USER@CONTAINER:/path/to/file
+;;
+;; Where:
+;;     USER          is the user on the container to connect as (optional)
+;;     CONTAINER     is the container to connect to
+;;
+;;
+;; Open file in a Kubernetes container:
+;;
+;;     C-x C-f /kubernetes:POD:/path/to/file
+;;
+;; Where:
+;;     POD     is the pod to connect to.
+;;             By default, the first container in that pod will be
+;;             used.
+;;
+;; Completion for POD and accessing it operate in the current
+;; namespace, use this command to change it:
+;;
+;; "kubectl config set-context --current --namespace=<name>"
+
+;;; Code:
+
+(require 'tramp)
+
+;;;###tramp-autoload
+(defcustom tramp-docker-program "docker"
+  "Name of the Docker client program."
+  :group 'tramp
+  :version "29.1"
+  :type '(choice (const "docker")
+                 (string)))
+
+;;;###tramp-autoload
+(defcustom tramp-podman-program "podman"
+  "Name of the Podman client program."
+  :group 'tramp
+  :version "29.1"
+  :type '(choice (const "podman")
+                 (string)))
+
+;;;###tramp-autoload
+(defcustom tramp-kubernetes-program "kubectl"
+  "Name of the Kubernetes client program."
+  :group 'tramp
+  :version "29.1"
+  :type '(choice (const "kubectl")
+                 (string)))
+
+;;;###tramp-autoload
+(defconst tramp-docker-method "docker"
+  "Tramp method name to use to connect to Docker containers.")
+
+;;;###tramp-autoload
+(defconst tramp-podman-method "podman"
+  "Tramp method name to use to connect to Podman containers.")
+
+;;;###tramp-autoload
+(defconst tramp-kubernetes-method "kubernetes"
+  "Tramp method name to use to connect to Kubernetes containers.")
+
+;;;###tramp-autoload
+(defun tramp-docker--completion-function (&rest _args)
+  "List Docker-like containers available for connection.
+
+This function is used by `tramp-set-completion-function', please
+see its function help for a description of the format."
+  (when-let ((raw-list (shell-command-to-string
+                       (concat tramp-docker-program
+                               " ps --format '{{.ID}}\t{{.Names}}'")))
+             (lines (split-string raw-list "\n" 'omit))
+             (names (mapcar
+                    (lambda (line)
+                       (when (string-match
+                             (rx bol (group (1+ nonl))
+                                 "\t" (? (group (1+ nonl))) eol)
+                             line)
+                        (or (match-string 2 line) (match-string 1 line))))
+                     lines)))
+    (mapcar (lambda (m) (list nil m)) (delq nil names))))
+
+;;;###tramp-autoload
+(defun tramp-kubernetes--completion-function (&rest _args)
+  "List Kubernetes pods available for connection.
+
+This function is used by `tramp-set-completion-function', please
+see its function help for a description of the format."
+  (when-let ((raw-list (shell-command-to-string
+                       (concat tramp-kubernetes-program
+                                " get pods --no-headers "
+                                "-o custom-columns=NAME:.metadata.name")))
+             (names (split-string raw-list "\n" 'omit)))
+    (mapcar (lambda (name)
+              (list nil name))
+            names)))
+
+;;;###tramp-autoload
+(defvar tramp-default-remote-shell) ;; Silence byte compiler.
+
+;;;###tramp-autoload
+(tramp--with-startup
+ (add-to-list 'tramp-methods
+              `(,tramp-docker-method
+                (tramp-login-program ,tramp-docker-program)
+                (tramp-login-args (("exec")
+                                   ("-it")
+                                   ("-u" "%u")
+                                   ("%h")
+                                  ("%l")))
+                (tramp-remote-shell ,tramp-default-remote-shell)
+                (tramp-remote-shell-login ("-l"))
+                (tramp-remote-shell-args ("-i" "-c"))))
+ (add-to-list 'tramp-methods
+              `(,tramp-podman-method
+                (tramp-login-program ,tramp-podman-program)
+                (tramp-login-args (("exec")
+                                   ("-it")
+                                   ("-u" "%u")
+                                   ("%h")
+                                  ("%l")))
+                (tramp-remote-shell ,tramp-default-remote-shell)
+                (tramp-remote-shell-login ("-l"))
+                (tramp-remote-shell-args ("-i" "-c"))))
+ (add-to-list 'tramp-methods
+              `(,tramp-kubernetes-method
+                (tramp-login-program ,tramp-kubernetes-program)
+                (tramp-login-args (("exec")
+                                   ("%h")
+                                   ("-it")
+                                   ("--")
+                                  ("%l")))
+                (tramp-remote-shell ,tramp-default-remote-shell)
+                (tramp-remote-shell-login ("-l"))
+                (tramp-remote-shell-args ("-i" "-c"))))
+
+ (tramp-set-completion-function
+  tramp-docker-method
+  '((tramp-docker--completion-function "")))
+
+ (tramp-set-completion-function
+  tramp-podman-method
+  '((tramp-docker--completion-function "")))
+
+ (tramp-set-completion-function
+  tramp-kubernetes-method
+  '((tramp-kubernetes--completion-function ""))))
+
+(add-hook 'tramp-unload-hook
+         (lambda ()
+           (unload-feature 'tramp-container 'force)))
+
+(provide 'tramp-container)
+
+;;; tramp-container.el ends here
diff --git a/lisp/net/tramp-crypt.el b/lisp/net/tramp-crypt.el
index e7bb1ebe33..16c4049a68 100644
--- a/lisp/net/tramp-crypt.el
+++ b/lisp/net/tramp-crypt.el
@@ -233,6 +233,7 @@ If NAME doesn't belong to an encrypted remote directory, 
return nil."
     (temporary-file-directory . tramp-handle-temporary-file-directory)
     ;; `tramp-get-home-directory' performed by default-handler.
     ;; `tramp-get-remote-gid' performed by default handler.
+    ;; `tramp-get-remote-groups' performed by default handler.
     ;; `tramp-get-remote-uid' performed by default handler.
     (tramp-set-file-uid-gid . tramp-crypt-handle-set-file-uid-gid)
     (unhandled-file-name-directory . ignore)
@@ -554,7 +555,7 @@ localname."
 (defun tramp-crypt-handle-access-file (filename string)
   "Like `access-file' for Tramp files."
   (let* ((encrypt-filename (tramp-crypt-encrypt-file-name filename))
-        (encrypt-regexp (rx (literal encrypt-filename) eos))
+        (encrypt-regexp (tramp-compat-rx (literal encrypt-filename) eos))
         tramp-crypt-enabled)
     (condition-case err
        (access-file encrypt-filename string)
@@ -583,6 +584,7 @@ This function is invoked by `tramp-crypt-handle-copy-file' 
and
 `tramp-crypt-handle-rename-file'.  It is an error if OP is
 neither of `copy' and `rename'.  FILENAME and NEWNAME must be
 absolute file names."
+  ;; FILENAME and NEWNAME are already expanded.
   (unless (memq op '(copy rename))
     (error "Unknown operation `%s', must be `copy' or `rename'" op))
 
@@ -706,7 +708,7 @@ absolute file names."
       (mapcar
        (lambda (x)
         (replace-regexp-in-string
-         (rx bos (literal directory)) ""
+         (tramp-compat-rx bos (literal directory)) ""
          (tramp-crypt-decrypt-file-name x)))
        (directory-files (tramp-crypt-encrypt-file-name directory) 'full)))))
 
@@ -850,6 +852,14 @@ WILDCARD is not supported."
     (tramp-compat-funcall
      'unlock-file (tramp-crypt-encrypt-file-name filename))))
 
+(with-eval-after-load 'bookmark
+  (add-hook 'bookmark-inhibit-context-functions
+           #'tramp-crypt-file-name-p)
+  (add-hook 'tramp-crypt-unload-hook
+           (lambda ()
+             (remove-hook 'bookmark-inhibit-context-functions
+                          #'tramp-crypt-file-name-p))))
+
 (add-hook 'tramp-unload-hook
          (lambda ()
            (unload-feature 'tramp-crypt 'force)))
diff --git a/lisp/net/tramp-fuse.el b/lisp/net/tramp-fuse.el
index 4b51af070a..ea6b5a0622 100644
--- a/lisp/net/tramp-fuse.el
+++ b/lisp/net/tramp-fuse.el
@@ -69,10 +69,11 @@
               (tramp-fuse-local-file-name directory))))))))
     (if full
        ;; Massage the result.
-       (let ((local (rx bol
-                        (literal
-                         (tramp-fuse-mount-point
-                          (tramp-dissect-file-name directory)))))
+       (let ((local (tramp-compat-rx
+                     bol
+                     (literal
+                      (tramp-fuse-mount-point
+                       (tramp-dissect-file-name directory)))))
              (remote (directory-file-name
                       (funcall
                        (if (tramp-compat-file-name-quoted-p directory)
@@ -179,7 +180,8 @@ It has the same meaning as 
`remote-file-name-inhibit-cache'.")
           (tramp-set-file-property
           vec "/" "mounted"
            (when (string-match
-                 (rx bol (group (literal (tramp-fuse-mount-spec vec))) space)
+                 (tramp-compat-rx
+                  bol (group (literal (tramp-fuse-mount-spec vec))) blank)
                  mount)
              (match-string 1 mount)))))))
 
diff --git a/lisp/net/tramp-gvfs.el b/lisp/net/tramp-gvfs.el
index 9060f37ed5..477f8fb3fd 100644
--- a/lisp/net/tramp-gvfs.el
+++ b/lisp/net/tramp-gvfs.el
@@ -316,6 +316,10 @@ It has been changed in GVFS 1.14.")
 (defconst tramp-gvfs-password-anonymous-supported 16
   "Operation supports anonymous users.")
 
+;; Since: 2.58
+(defconst tramp-gvfs-password-tcrypt 32
+  "Operation takes TCRYPT parameters.")
+
 ;; For the time being, we just need org.goa.Account and org.goa.Files
 ;; interfaces.  We document the other ones, just in case.
 
@@ -410,9 +414,10 @@ It has been changed in GVFS 1.14.")
 ;; </interface>
 
 (defconst tramp-goa-identity-regexp
-  (rx bol (? (group (regexp tramp-user-regexp)))
-      "@" (? (group (regexp tramp-host-regexp)))
-      (? ":" (group (regexp tramp-port-regexp))))
+  (tramp-compat-rx
+   bol (? (group (regexp tramp-user-regexp)))
+   "@" (? (group (regexp tramp-host-regexp)))
+   (? ":" (group (regexp tramp-port-regexp))))
   "Regexp matching GNOME Online Accounts \"PresentationIdentity\" property.")
 
 (defconst tramp-goa-interface-mail "org.gnome.OnlineAccounts.Mail"
@@ -710,15 +715,16 @@ It has been changed in GVFS 1.14.")
       "unix::device")
     "GVFS file attributes."))
 
-(eval-and-compile
-  (defconst tramp-gvfs-file-attributes-with-gvfs-ls-regexp
-    (rx blank (group (regexp (regexp-opt tramp-gvfs-file-attributes)))
-       "=" (group (+? nonl)))
-    "Regexp to parse GVFS file attributes with `gvfs-ls'."))
+(defconst tramp-gvfs-file-attributes-with-gvfs-ls-regexp
+  (tramp-compat-rx
+   blank (group (regexp (regexp-opt tramp-gvfs-file-attributes)))
+   "=" (group (+? nonl)))
+  "Regexp to parse GVFS file attributes with `gvfs-ls'.")
 
 (defconst tramp-gvfs-file-attributes-with-gvfs-info-regexp
-  (rx bol (* blank) (group (regexp (regexp-opt tramp-gvfs-file-attributes)))
-      ":" (+ blank) (group (* nonl)) eol)
+  (tramp-compat-rx
+   bol (* blank) (group (regexp (regexp-opt tramp-gvfs-file-attributes)))
+   ":" (+ blank) (group (* nonl)) eol)
   "Regexp to parse GVFS file attributes with `gvfs-info'.")
 
 (defconst tramp-gvfs-file-system-attributes
@@ -728,16 +734,17 @@ It has been changed in GVFS 1.14.")
   "GVFS file system attributes.")
 
 (defconst tramp-gvfs-file-system-attributes-regexp
-  (rx bol (* blank)
-      (group (regexp (regexp-opt tramp-gvfs-file-system-attributes)))
-      ":" (+ blank) (group (* nonl)) eol)
+  (tramp-compat-rx
+   bol (* blank)
+   (group (regexp (regexp-opt tramp-gvfs-file-system-attributes)))
+   ":" (+ blank) (group (* nonl)) eol)
   "Regexp to parse GVFS file system attributes with `gvfs-info'.")
 
 (defconst tramp-gvfs-nextcloud-default-prefix "/remote.php/webdav"
   "Default prefix for owncloud / nextcloud methods.")
 
 (defconst tramp-gvfs-nextcloud-default-prefix-regexp
-  (rx (literal tramp-gvfs-nextcloud-default-prefix) eol)
+  (tramp-compat-rx (literal tramp-gvfs-nextcloud-default-prefix) eol)
   "Regexp of default prefix for owncloud / nextcloud methods.")
 
 
@@ -820,6 +827,7 @@ It has been changed in GVFS 1.14.")
     (temporary-file-directory . tramp-handle-temporary-file-directory)
     (tramp-get-home-directory . tramp-gvfs-handle-get-home-directory)
     (tramp-get-remote-gid . tramp-gvfs-handle-get-remote-gid)
+    (tramp-get-remote-groups . ignore)
     (tramp-get-remote-uid . tramp-gvfs-handle-get-remote-uid)
     (tramp-set-file-uid-gid . tramp-gvfs-handle-set-file-uid-gid)
     (unhandled-file-name-directory . ignore)
@@ -963,7 +971,7 @@ The global value will always be nil; it is bound where 
needed.")
 (defun tramp-gvfs-info (filename &optional arg)
   "Check FILENAME via `gvfs-info'.
 Set file property \"file-exists-p\" with the result."
-  (with-parsed-tramp-file-name filename nil
+  (with-parsed-tramp-file-name (expand-file-name filename) nil
     (tramp-set-file-property
      v localname "file-exists-p"
      (tramp-gvfs-send-command
@@ -986,6 +994,7 @@ This function is invoked by `tramp-gvfs-handle-copy-file' 
and
 `tramp-gvfs-handle-rename-file'.  It is an error if OP is neither
 of `copy' and `rename'.  FILENAME and NEWNAME must be absolute
 file names."
+  ;; FILENAME and NEWNAME are already expanded.
   (unless (memq op '(copy rename))
     (error "Unknown operation `%s', must be `copy' or `rename'" op))
 
@@ -1129,7 +1138,7 @@ file names."
 
 (defun tramp-gvfs-handle-delete-file (filename &optional trash)
   "Like `delete-file' for Tramp files."
-  (with-parsed-tramp-file-name filename nil
+  (with-parsed-tramp-file-name (expand-file-name filename) nil
     (tramp-flush-file-properties v localname)
     (if (and delete-by-moving-to-trash trash)
        (move-file-to-trash filename)
@@ -1158,7 +1167,7 @@ file names."
     (with-parsed-tramp-file-name name nil
       ;; If there is a default location, expand tilde.
       (when (string-match
-            (rx bos "~" (group (* (not (any "/")))) (group (* nonl)) eos)
+            (tramp-compat-rx bos "~" (group (* (not "/"))) (group (* nonl)) 
eos)
              localname)
        (let ((uname (match-string 1 localname))
              (fname (match-string 2 localname))
@@ -1176,7 +1185,7 @@ file names."
       ;; We do not pass "/..".
       (if (string-match-p (rx bos (| "afp" (: "dav" (? "s")) "smb") eos) 
method)
          (when (string-match
-                (rx bos "/" (+ (not (any "/"))) (group "/.." (? "/")))
+                (tramp-compat-rx bos "/" (+ (not "/")) (group "/.." (? "/")))
                 localname)
            (setq localname (replace-match "/" t t localname 1)))
        (when (string-match (rx bol "/.." (? "/")) localname)
@@ -1199,7 +1208,7 @@ file names."
   ;; Don't modify `last-coding-system-used' by accident.
   (let ((last-coding-system-used last-coding-system-used)
        result)
-    (with-parsed-tramp-file-name directory nil
+    (with-parsed-tramp-file-name (expand-file-name directory) nil
       (with-tramp-file-property v localname "directory-attributes"
        (tramp-message v 5 "directory gvfs attributes: %s" localname)
        ;; Send command.
@@ -1212,20 +1221,22 @@ file names."
        (with-current-buffer (tramp-get-connection-buffer v)
          (goto-char (point-min))
          (while (looking-at
-                 (rx bol (group (+ nonl)) blank
-                     (group (+ digit)) blank
-                     "(" (group (+? nonl)) ")"
-                     (regexp tramp-gvfs-file-attributes-with-gvfs-ls-regexp)))
+                 (tramp-compat-rx
+                  bol (group (+ nonl)) blank
+                  (group (+ digit)) blank
+                  "(" (group (+? nonl)) ")"
+                  (regexp tramp-gvfs-file-attributes-with-gvfs-ls-regexp)))
            (let ((item (list (cons "type" (match-string 3))
                              (cons "standard::size" (match-string 2))
                              (cons "name" (match-string 1)))))
              (goto-char (1+ (match-end 3)))
              (while (looking-at
-                     (rx (regexp 
tramp-gvfs-file-attributes-with-gvfs-ls-regexp)
-                         (group
-                          (| (regexp
-                              tramp-gvfs-file-attributes-with-gvfs-ls-regexp)
-                             eol))))
+                     (tramp-compat-rx
+                      (regexp tramp-gvfs-file-attributes-with-gvfs-ls-regexp)
+                      (group
+                       (| (regexp
+                           tramp-gvfs-file-attributes-with-gvfs-ls-regexp)
+                          eol))))
                (push (cons (match-string 1) (match-string 2)) item)
                (goto-char (match-end 2)))
              ;; Add display name as head.
@@ -1243,7 +1254,7 @@ If FILE-SYSTEM is non-nil, return file system attributes."
   ;; Don't modify `last-coding-system-used' by accident.
   (let ((last-coding-system-used last-coding-system-used)
        result)
-    (with-parsed-tramp-file-name filename nil
+    (with-parsed-tramp-file-name (expand-file-name filename) nil
       (with-tramp-file-property
          v localname
          (if file-system "file-system-attributes" "file-attributes")
@@ -1273,7 +1284,7 @@ If FILE-SYSTEM is non-nil, return file system attributes."
     (if (or (and (string-match-p
                  (rx bol (| "afp" (: "dav" (? "s")) "smb") eol) method)
                 (string-match-p
-                 (rx bol (? "/") (+ (not (any "/"))) eol) localname))
+                 (tramp-compat-rx bol (? "/") (+ (not "/")) eol) localname))
            (string-equal localname "/"))
        (tramp-gvfs-get-root-attributes filename)
       (assoc
@@ -1317,7 +1328,7 @@ If FILE-SYSTEM is non-nil, return file system attributes."
            (if (eq id-format 'integer)
                (string-to-number
                 (or (cdr (assoc "unix::uid" attributes))
-                    (eval-when-compile (format "%s" 
tramp-unknown-id-integer))))
+                    (number-to-string tramp-unknown-id-integer)))
              (or (cdr (assoc "owner::user" attributes))
                  (cdr (assoc "unix::uid" attributes))
                  tramp-unknown-id-string)))
@@ -1325,7 +1336,7 @@ If FILE-SYSTEM is non-nil, return file system attributes."
            (if (eq id-format 'integer)
                (string-to-number
                 (or (cdr (assoc "unix::gid" attributes))
-                    (eval-when-compile (format "%s" 
tramp-unknown-id-integer))))
+                    (number-to-string tramp-unknown-id-integer)))
              (or (cdr (assoc "owner::group" attributes))
                  (cdr (assoc "unix::gid" attributes))
                  tramp-unknown-id-string)))
@@ -1402,7 +1413,7 @@ If FILE-SYSTEM is non-nil, return file system attributes."
 
 (defun tramp-gvfs-handle-file-executable-p (filename)
   "Like `file-executable-p' for Tramp files."
-  (with-parsed-tramp-file-name filename nil
+  (with-parsed-tramp-file-name (expand-file-name filename) nil
     (with-tramp-file-property v localname "file-executable-p"
       (or (tramp-check-cached-permissions v ?x)
          (tramp-check-cached-permissions v ?s)))))
@@ -1473,7 +1484,7 @@ If FILE-SYSTEM is non-nil, return file system attributes."
   (let* ((events (process-get proc 'events))
         (rest-string (process-get proc 'rest-string))
         (dd (tramp-get-default-directory (process-buffer proc)))
-        (ddu (rx (literal (tramp-gvfs-url-file-name dd)))))
+        (ddu (tramp-compat-rx (literal (tramp-gvfs-url-file-name dd)))))
     (when rest-string
       (tramp-message proc 10 "Previous string:\n%s" rest-string))
     (tramp-message proc 6 "%S\n%s" proc string)
@@ -1492,10 +1503,11 @@ If FILE-SYSTEM is non-nil, return file system 
attributes."
       (delete-process proc))
 
     (while (string-match
-           (rx bol (+ nonl) ":"
-               space (group (+ nonl)) ":"
-               space (group (regexp (regexp-opt tramp-gio-events)))
-               (? (group space (group (+ nonl)))) eol)
+           (tramp-compat-rx
+            bol (+ nonl) ":"
+            blank (group (+ nonl)) ":"
+            blank (group (regexp (regexp-opt tramp-gio-events)))
+            (? (group blank (group (+ nonl)))) eol)
            string)
 
       (let ((file (match-string 1 string))
@@ -1726,7 +1738,8 @@ ID-FORMAT valid values are `string' and `integer'."
   "Retrieve file name from D-Bus OBJECT-PATH."
   (dbus-unescape-from-identifier
    (replace-regexp-in-string
-    (rx bol (* nonl) "/" (+ (not (any "/"))) eol) "\\1" object-path)))
+    (tramp-compat-rx bol (* nonl) "/" (group (+ (not "/"))) eol) "\\1"
+    object-path)))
 
 (defun tramp-gvfs-url-host (url)
   "Return the host name part of URL, a string.
@@ -2001,8 +2014,9 @@ Their full names are \"org.gtk.vfs.MountTracker.mounted\" 
and
                (string-equal domain (tramp-file-name-domain vec))
                (string-equal host (tramp-file-name-host vec))
                (string-equal port (tramp-file-name-port vec))
-               (string-match-p (rx bol "/" (literal (or share "")))
-                               (tramp-file-name-unquote-localname vec)))
+               (string-match-p
+                (tramp-compat-rx bol "/" (literal (or share "")))
+                (tramp-file-name-unquote-localname vec)))
           ;; Set mountpoint and location.
           (tramp-set-file-property vec "/" "fuse-mountpoint" fuse-mountpoint)
           (tramp-set-connection-property
@@ -2046,7 +2060,8 @@ It was \"a(say)\", but has changed to \"a{sv})\"."
                   (tramp-media-device-port media) (tramp-file-name-port vec)))
         (localname (tramp-file-name-unquote-localname vec))
         (share (when (string-match
-                      (rx bol (? "/") (group (+ (not (any "/"))))) localname)
+                      (tramp-compat-rx bol (? "/") (group (+ (not "/"))))
+                      localname)
                  (match-string 1 localname)))
         (ssl (if (string-match-p (rx bol (| "davs" "nextcloud")) method)
                  "true" "false"))
@@ -2089,7 +2104,8 @@ It was \"a(say)\", but has changed to \"a{sv})\"."
                 (list (tramp-gvfs-mount-spec-entry "port" port)))))
         (mount-pref
           (if (and (string-match-p (rx bol "dav") method)
-                   (string-match (rx bol (? "/") (+ (not (any "/")))) 
localname))
+                   (string-match
+                   (tramp-compat-rx bol (? "/") (+ (not "/"))) localname))
               (match-string 0 localname)
            (tramp-gvfs-get-remote-prefix vec))))
 
@@ -2489,6 +2505,7 @@ This uses \"avahi-browse\" in case D-Bus is not enabled 
in Avahi."
       result))))
 
 (when tramp-gvfs-enabled
+  (with-no-warnings ;; max-specpdl-size
   ;; Suppress D-Bus error messages and Tramp traces.
   (let (;; Sometimes, it fails with "Variable binding depth exceeds
        ;; max-specpdl-size".  Shall be fixed in Emacs 27.
@@ -2546,7 +2563,7 @@ This uses \"avahi-browse\" in case D-Bus is not enabled 
in Avahi."
      "mtp"
      (mapcar
       (lambda (method) `(tramp-parse-media-names ,(format "_%s._tcp" method)))
-      tramp-media-methods))))
+      tramp-media-methods)))))
 
 (add-hook 'tramp-unload-hook
          (lambda ()
diff --git a/lisp/net/tramp-integration.el b/lisp/net/tramp-integration.el
index 946f972502..35c0636b1c 100644
--- a/lisp/net/tramp-integration.el
+++ b/lisp/net/tramp-integration.el
@@ -86,7 +86,7 @@ special handling of `substitute-in-file-name'."
 (defun tramp-rfn-eshadow-update-overlay-regexp ()
   "An overlay covering the shadowed part of the filename."
   (rx-to-string
-   `(: (* (not (any ,tramp-postfix-host-format "/~"))) (or "/" "~"))))
+   `(: (* (not (any ,tramp-postfix-host-format "/~"))) (| "/" "~"))))
 
 (defun tramp-rfn-eshadow-update-overlay ()
   "Update `rfn-eshadow-overlay' to cover shadowed part of minibuffer input.
@@ -130,8 +130,10 @@ been set up by `rfn-eshadow-setup-minibuffer'."
   ;; Remove last element of `(exec-path)', which is `exec-directory'.
   ;; Use `path-separator' as it does eshell.
   (setq eshell-path-env
-       (mapconcat
-        #'identity (butlast (tramp-compat-exec-path)) path-separator)))
+        (if (file-remote-p default-directory)
+            (mapconcat
+            #'identity (butlast (tramp-compat-exec-path)) path-separator)
+          (getenv "PATH"))))
 
 (with-eval-after-load 'esh-util
   (add-hook 'eshell-mode-hook
@@ -217,12 +219,12 @@ NAME must be equal to `tramp-current-connection'."
   (info-lookup-maybe-add-help
    :mode 'tramp-info-lookup-mode :topic 'symbol
    :regexp (rx (+ (not (any "\t\n \"'(),[]`‘’"))))
-   :doc-spec '(("(tramp)Function Index" nil
-               (rx bol " " (+ "-") " " (* nonl) ": ")
-               (rx (group (| " " eol))))
+   :doc-spec `(("(tramp)Function Index" nil
+               ,(rx bol blank (+ "-") blank (* nonl) ":" blank)
+               ,(rx (| blank eol)))
               ("(tramp)Variable Index" nil
-               (rx bol " " (+ "-") " " (* nonl) ": ")
-               (rx (group (| " " eol))))))
+               ,(rx bol blank (+ "-") blank (* nonl) ":" blank)
+               ,(rx (| blank eol)))))
 
   (add-hook
    'tramp-integration-unload-hook
diff --git a/lisp/net/tramp-rclone.el b/lisp/net/tramp-rclone.el
index 435faf8329..9e379da8c1 100644
--- a/lisp/net/tramp-rclone.el
+++ b/lisp/net/tramp-rclone.el
@@ -147,6 +147,7 @@
     (temporary-file-directory . tramp-handle-temporary-file-directory)
     (tramp-get-home-directory . ignore)
     (tramp-get-remote-gid . ignore)
+    (tramp-get-remote-groups . ignore)
     (tramp-get-remote-uid . ignore)
     (tramp-set-file-uid-gid . ignore)
     (unhandled-file-name-directory . ignore)
@@ -186,7 +187,7 @@ arguments to pass to the OPERATION."
     (delq nil
          (mapcar
           (lambda (line)
-            (when (string-match (rx bol (group (+ (not space))) ":" eol) line)
+            (when (string-match (rx bol (group (+ (not blank))) ":" eol) line)
               `(nil ,(match-string 1 line))))
           (tramp-process-lines nil tramp-rclone-program "listremotes")))))
 
@@ -210,6 +211,7 @@ This function is invoked by `tramp-rclone-handle-copy-file' 
and
 `tramp-rclone-handle-rename-file'.  It is an error if OP is neither
 of `copy' and `rename'.  FILENAME and NEWNAME must be absolute
 file names."
+  ;; FILENAME and NEWNAME are already expanded.
   (unless (memq op '(copy rename))
     (error "Unknown operation `%s', must be `copy' or `rename'" op))
 
@@ -300,11 +302,11 @@ file names."
        (let (total used free)
          (goto-char (point-min))
          (while (not (eobp))
-           (when (looking-at (rx "Total: " (+ space) (group (+ digit))))
+           (when (looking-at (rx "Total: " (+ blank) (group (+ digit))))
              (setq total (string-to-number (match-string 1))))
-           (when (looking-at (rx "Used: " (+ space) (group (+ digit))))
+           (when (looking-at (rx "Used: " (+ blank) (group (+ digit))))
              (setq used (string-to-number (match-string 1))))
-           (when (looking-at (rx "Free: " (+ space) (group (+ digit))))
+           (when (looking-at (rx "Free: " (+ blank) (group (+ digit))))
              (setq free (string-to-number (match-string 1))))
            (forward-line))
          (when used
diff --git a/lisp/net/tramp-sh.el b/lisp/net/tramp-sh.el
index 2489ac9aec..3240f5352a 100644
--- a/lisp/net/tramp-sh.el
+++ b/lisp/net/tramp-sh.el
@@ -411,18 +411,16 @@ The string is used in `tramp-methods'.")
 
  (add-to-list 'tramp-default-method-alist
              `(,tramp-local-host-regexp
-               ,(rx bos (literal tramp-root-id-string) eos) "su"))
+               ,(tramp-compat-rx bos (literal tramp-root-id-string) eos) "su"))
 
  (add-to-list 'tramp-default-user-alist
-             `(,(rx bos (regexp (regexp-opt '("su" "sudo" "doas" "ksu"))) eos)
+             `(,(rx bos (| "su" "sudo" "doas" "ksu") eos)
                nil ,tramp-root-id-string))
  ;; Do not add "ssh" based methods, otherwise ~/.ssh/config would be ignored.
  ;; Do not add "plink" based methods, they ask interactively for the user.
  (add-to-list 'tramp-default-user-alist
              `(,(rx bos
-                    (regexp
-                     (regexp-opt
-                      '("rcp" "remcp" "rsh" "telnet" "nc" "krlogin" "fcp")))
+                    (| "rcp" "remcp" "rsh" "telnet" "nc" "krlogin" "fcp")
                     eos)
                nil ,(user-login-name))))
 
@@ -785,6 +783,41 @@ characters need to be doubled.")
 Format specifiers are replaced by `tramp-expand-script', percent
 characters need to be doubled.")
 
+(defconst tramp-perl-id
+  "%p -e '
+use strict;
+use warnings;
+use POSIX qw(getgroups);
+
+my ($user, $passwd, $uid, $gid) = getpwuid $< ;
+my $group = getgrgid $gid ;
+my @groups = map { $_ . \"(\" . getgrgid ($_) . \")\" } getgroups ();
+
+printf \"uid=%%d(%%s) gid=%%d(%%s) groups=%%s\\n\",
+  $uid, $user, $gid, $group, join \",\", @groups;' %n"
+  "Perl script printing `id' output.
+Format specifiers are replaced by `tramp-expand-script', percent
+characters need to be doubled.")
+
+(defconst tramp-python-id
+  "%y -c '
+import os, pwd, grp;
+
+def idform(id):
+  return \"{:d}({:s})\".format(id, grp.getgrgid(id)[0]);
+
+uid = os.getuid();
+user = pwd.getpwuid(uid)[0];
+gid = os.getgid();
+group = grp.getgrgid(gid)[0]
+groups = map(idform, os.getgrouplist(user, gid));
+
+print(\"uid={:d}({:s}) gid={:d}({:s}) groups={:s}\"
+      .format(uid, user, gid, group, \",\".join(groups)));' %n"
+  "Python script printing `id' output.
+Format specifiers are replaced by `tramp-expand-script', percent
+characters need to be doubled.")
+
 ;; These two use base64 encoding.
 (defconst tramp-perl-encode-with-module
   "%p -MMIME::Base64 -0777 -ne 'print encode_base64($_)' %n"
@@ -1084,6 +1117,7 @@ Format specifiers \"%s\" are replaced before the script 
is used.")
     (temporary-file-directory . tramp-handle-temporary-file-directory)
     (tramp-get-home-directory . tramp-sh-handle-get-home-directory)
     (tramp-get-remote-gid . tramp-sh-handle-get-remote-gid)
+    (tramp-get-remote-groups . tramp-sh-handle-get-remote-groups)
     (tramp-get-remote-uid . tramp-sh-handle-get-remote-uid)
     (tramp-set-file-uid-gid . tramp-sh-handle-set-file-uid-gid)
     (unhandled-file-name-directory . ignore)
@@ -1102,66 +1136,63 @@ Operations not mentioned here will be handled by the 
normal Emacs functions.")
 If TARGET is a non-Tramp file, it is used verbatim as the target
 of the symlink.  If TARGET is a Tramp file, only the localname
 component is used as the target of the symlink."
-  (if (not (tramp-tramp-file-p (expand-file-name linkname)))
-      (tramp-run-real-handler
-       #'make-symbolic-link (list target linkname ok-if-already-exists))
-
-    (with-parsed-tramp-file-name linkname nil
-      ;; If TARGET is a Tramp name, use just the localname component.
-      ;; Don't check for a proper method.
-      (let ((non-essential t))
-       (when (and (tramp-tramp-file-p target)
-                  (tramp-file-name-equal-p v (tramp-dissect-file-name target)))
-         (setq target (tramp-file-local-name (expand-file-name target))))
-       ;; There could be a cyclic link.
-       (tramp-flush-file-properties
-        v (expand-file-name target (tramp-file-local-name default-directory))))
-
-      ;; If TARGET is still remote, quote it.
-      (if (tramp-tramp-file-p target)
-         (make-symbolic-link (tramp-compat-file-name-quote target 'top)
-          linkname ok-if-already-exists)
-
-       (let ((ln (tramp-get-remote-ln v))
-             (cwd (tramp-run-real-handler
-                   #'file-name-directory (list localname))))
-         (unless ln
-           (tramp-error
-            v 'file-error
-            (concat "Making a symbolic link. "
-                    "ln(1) does not exist on the remote host.")))
-
-         ;; Do the 'confirm if exists' thing.
-         (when (file-exists-p linkname)
-           ;; What to do?
-           (if (or (null ok-if-already-exists) ; not allowed to exist
-                   (and (numberp ok-if-already-exists)
-                        (not
-                         (yes-or-no-p
-                          (format
-                           "File %s already exists; make it a link anyway?"
-                           localname)))))
-               (tramp-error v 'file-already-exists localname)
-             (delete-file linkname)))
-
-         (tramp-flush-file-properties v localname)
-
-         ;; Right, they are on the same host, regardless of user,
-         ;; method, etc.  We now make the link on the remote
-         ;; machine.  This will occur as the user that TARGET belongs to.
-         (and (tramp-send-command-and-check
-               v (format "cd %s" (tramp-shell-quote-argument cwd)))
-               (tramp-send-command-and-check
-               v (format
-                  "%s -sf %s %s" ln
-                  (tramp-shell-quote-argument target)
-                  ;; The command could exceed PATH_MAX, so we use
-                  ;; relative file names.  However, relative file
-                  ;; names could start with "-".
-                  ;; `tramp-shell-quote-argument' does not handle
-                  ;; this, we must do it ourselves.
-                  (tramp-shell-quote-argument
-                    (concat "./" (file-name-nondirectory localname)))))))))))
+  (with-parsed-tramp-file-name (expand-file-name linkname) nil
+    ;; If TARGET is a Tramp name, use just the localname component.
+    ;; Don't check for a proper method.
+    (let ((non-essential t))
+      (when (and (tramp-tramp-file-p target)
+                (tramp-file-name-equal-p v (tramp-dissect-file-name target)))
+       (setq target (tramp-file-local-name (expand-file-name target))))
+      ;; There could be a cyclic link.
+      (tramp-flush-file-properties
+       v (expand-file-name target (tramp-file-local-name default-directory))))
+
+    ;; If TARGET is still remote, quote it.
+    (if (tramp-tramp-file-p target)
+       (make-symbolic-link
+        (tramp-compat-file-name-quote target 'top)
+        linkname ok-if-already-exists)
+
+      (let ((ln (tramp-get-remote-ln v))
+           (cwd (tramp-run-real-handler
+                 #'file-name-directory (list localname))))
+       (unless ln
+         (tramp-error
+          v 'file-error
+          (concat "Making a symbolic link. "
+                  "ln(1) does not exist on the remote host.")))
+
+       ;; Do the 'confirm if exists' thing.
+       (when (file-exists-p linkname)
+         ;; What to do?
+         (if (or (null ok-if-already-exists) ; not allowed to exist
+                 (and (numberp ok-if-already-exists)
+                      (not
+                       (yes-or-no-p
+                        (format
+                         "File %s already exists; make it a link anyway?"
+                         localname)))))
+             (tramp-error v 'file-already-exists localname)
+           (delete-file linkname)))
+
+       (tramp-flush-file-properties v localname)
+
+       ;; Right, they are on the same host, regardless of user,
+       ;; method, etc.  We now make the link on the remote machine.
+       ;; This will occur as the user that TARGET belongs to.
+       (and (tramp-send-command-and-check
+             v (format "cd %s" (tramp-shell-quote-argument cwd)))
+             (tramp-send-command-and-check
+             v (format
+                "%s -sf %s %s" ln
+                (tramp-shell-quote-argument target)
+                ;; The command could exceed PATH_MAX, so we use
+                ;; relative file names.  However, relative file names
+                ;; could start with "-".
+                ;; `tramp-shell-quote-argument' does not handle this,
+                ;; we must do it ourselves.
+                (tramp-shell-quote-argument
+                  (concat "./" (file-name-nondirectory localname))))))))))
 
 (defun tramp-sh-handle-file-truename (filename)
   "Like `file-truename' for Tramp files."
@@ -1225,7 +1256,7 @@ component is used as the target of the symlink."
   ;; We don't want to run it when `non-essential' is t, or there is
   ;; no connection process yet.
   (when (tramp-connectable-p filename)
-    (with-parsed-tramp-file-name filename nil
+    (with-parsed-tramp-file-name (expand-file-name filename) nil
       (with-tramp-file-property v localname "file-exists-p"
        (if (tramp-file-property-p v localname "file-attributes")
            (not (null (tramp-get-file-property v localname "file-attributes")))
@@ -1250,7 +1281,7 @@ component is used as the target of the symlink."
          (tramp-do-file-attributes-with-perl v localname))
         (t (tramp-do-file-attributes-with-ls v localname)))))))
 
-(defconst tramp-sunos-unames (regexp-opt '("SunOS 5.10" "SunOS 5.11"))
+(defconst tramp-sunos-unames (rx (| "SunOS 5.10" "SunOS 5.11"))
   "Regexp to determine remote SunOS.")
 
 (defun tramp-sh--quoting-style-options (vec)
@@ -1400,7 +1431,7 @@ component is used as the target of the symlink."
           (buffer-name)))
   (if time-list
       (tramp-run-real-handler #'set-visited-file-modtime (list time-list))
-    (let ((f (buffer-file-name))
+    (let ((f (expand-file-name (buffer-file-name)))
          coding-system-used)
       (with-parsed-tramp-file-name f nil
        (let* ((remote-file-name-inhibit-cache t)
@@ -1497,7 +1528,7 @@ of."
               time)))
        (tramp-send-command-and-check
         v (format
-           "env TZ=UTC %s %s %s %s"
+           "env TZ=UTC0 %s %s %s %s"
            (tramp-get-remote-touch v)
            (if (tramp-get-connection-property v "touch-t")
                (format "-t %s" (format-time-string "%Y%m%d%H%M.%S" time t))
@@ -1525,10 +1556,16 @@ ID-FORMAT valid values are `string' and `integer'."
   ;; The result is cached in `tramp-get-remote-uid'.
   (ignore-errors
     (cond
-     ((tramp-get-remote-id vec) (tramp-get-remote-uid-with-id vec id-format))
-     ((tramp-get-remote-perl vec) (tramp-get-remote-uid-with-perl vec 
id-format))
+     ((tramp-get-remote-id vec)
+      (tramp-send-command vec (tramp-get-remote-id vec)))
+     ((tramp-get-remote-perl vec)
+      (tramp-maybe-send-script vec tramp-perl-id "tramp_perl_id")
+      (tramp-send-command vec "tramp_perl_id"))
      ((tramp-get-remote-python vec)
-      (tramp-get-remote-uid-with-python vec id-format)))))
+      (tramp-maybe-send-script vec tramp-python-id "tramp_python_id")
+      (tramp-send-command vec "tramp_python_id")))
+    (tramp-read-id-output vec)
+    (tramp-get-connection-property vec (format "uid-%s" id-format))))
 
 (defun tramp-sh-handle-get-remote-gid (vec id-format)
   "The gid of the remote connection VEC, in ID-FORMAT.
@@ -1536,10 +1573,33 @@ ID-FORMAT valid values are `string' and `integer'."
   ;; The result is cached in `tramp-get-remote-gid'.
   (ignore-errors
     (cond
-     ((tramp-get-remote-id vec) (tramp-get-remote-gid-with-id vec id-format))
-     ((tramp-get-remote-perl vec) (tramp-get-remote-gid-with-perl vec 
id-format))
+     ((tramp-get-remote-id vec)
+      (tramp-send-command vec (tramp-get-remote-id vec)))
+     ((tramp-get-remote-perl vec)
+      (tramp-maybe-send-script vec tramp-perl-id "tramp_perl_id")
+      (tramp-send-command vec "tramp_perl_id"))
+     ((tramp-get-remote-python vec)
+      (tramp-maybe-send-script vec tramp-python-id "tramp_python_id")
+      (tramp-send-command vec "tramp_python_id")))
+    (tramp-read-id-output vec)
+    (tramp-get-connection-property vec (format "gid-%s" id-format))))
+
+(defun tramp-sh-handle-get-remote-groups (vec id-format)
+  "Like `tramp-get-remote-groups' for Tramp files.
+ID-FORMAT valid values are `string' and `integer'."
+  ;; The result is cached in `tramp-get-remote-groups'.
+  (ignore-errors
+    (cond
+     ((tramp-get-remote-id vec)
+      (tramp-send-command vec (tramp-get-remote-id vec)))
+     ((tramp-get-remote-perl vec)
+      (tramp-maybe-send-script vec tramp-perl-id "tramp_perl_id")
+      (tramp-send-command vec "tramp_perl_id"))
      ((tramp-get-remote-python vec)
-      (tramp-get-remote-gid-with-python vec id-format)))))
+      (tramp-maybe-send-script vec tramp-python-id "tramp_python_id")
+      (tramp-send-command vec "tramp_python_id")))
+    (tramp-read-id-output vec)
+    (tramp-get-connection-property vec (format "groups-%s" id-format))))
 
 (defun tramp-sh-handle-set-file-uid-gid (filename &optional uid gid)
   "Like `tramp-set-file-uid-gid' for Tramp files."
@@ -1569,13 +1629,14 @@ ID-FORMAT valid values are `string' and `integer'."
 
 (defun tramp-sh-handle-file-selinux-context (filename)
   "Like `file-selinux-context' for Tramp files."
-  (with-parsed-tramp-file-name filename nil
+  (with-parsed-tramp-file-name (expand-file-name filename) nil
     (with-tramp-file-property v localname "file-selinux-context"
       (let ((context '(nil nil nil nil))
-           (regexp (rx (group (+ (any "_" alnum))) ":"
-                       (group (+ (any "_" alnum))) ":"
-                       (group (+ (any "_" alnum))) ":"
-                       (group (+ (any "_" alnum))))))
+           (regexp (tramp-compat-rx
+                    (group (+ (any "_" alnum))) ":"
+                    (group (+ (any "_" alnum))) ":"
+                    (group (+ (any "_" alnum))) ":"
+                    (group (+ (any "_" alnum))))))
        (when (and (tramp-remote-selinux-p v)
                   (tramp-send-command-and-check
                    v (format
@@ -1592,7 +1653,7 @@ ID-FORMAT valid values are `string' and `integer'."
 
 (defun tramp-sh-handle-set-file-selinux-context (filename context)
   "Like `set-file-selinux-context' for Tramp files."
-  (with-parsed-tramp-file-name filename nil
+  (with-parsed-tramp-file-name (expand-file-name filename) nil
     (when (and (consp context)
               (tramp-remote-selinux-p v))
       (let ((user (and (stringp (nth 0 context)) (nth 0 context)))
@@ -1619,7 +1680,7 @@ ID-FORMAT valid values are `string' and `integer'."
 
 (defun tramp-sh-handle-file-acl (filename)
   "Like `file-acl' for Tramp files."
-  (with-parsed-tramp-file-name filename nil
+  (with-parsed-tramp-file-name (expand-file-name filename) nil
     (with-tramp-file-property v localname "file-acl"
       (when (and (tramp-remote-acl-p v)
                 (tramp-send-command-and-check
@@ -1656,7 +1717,7 @@ ID-FORMAT valid values are `string' and `integer'."
 
 (defun tramp-sh-handle-file-executable-p (filename)
   "Like `file-executable-p' for Tramp files."
-  (with-parsed-tramp-file-name filename nil
+  (with-parsed-tramp-file-name (expand-file-name filename) nil
     (with-tramp-file-property v localname "file-executable-p"
       ;; Examine `file-attributes' cache to see if request can be
       ;; satisfied without remote operation.
@@ -1667,8 +1728,10 @@ ID-FORMAT valid values are `string' and `integer'."
 
 (defun tramp-sh-handle-file-readable-p (filename)
   "Like `file-readable-p' for Tramp files."
-  (with-parsed-tramp-file-name filename nil
+  (with-parsed-tramp-file-name (expand-file-name filename) nil
     (with-tramp-file-property v localname "file-readable-p"
+      ;; Examine `file-attributes' cache to see if request can be
+      ;; satisfied without remote operation.
       (if (tramp-file-property-p v localname "file-attributes")
          (tramp-handle-file-readable-p filename)
        (tramp-run-test "-r" filename)))))
@@ -1677,7 +1740,7 @@ ID-FORMAT valid values are `string' and `integer'."
 
 (defun tramp-sh-handle-file-directory-p (filename)
   "Like `file-directory-p' for Tramp files."
-  (with-parsed-tramp-file-name filename nil
+  (with-parsed-tramp-file-name (expand-file-name filename) nil
     ;; `file-directory-p' is used as predicate for file name completion.
     ;; Sometimes, when a connection is not established yet, it is
     ;; desirable to return t immediately for "/method:foo:".  It can
@@ -1696,7 +1759,7 @@ ID-FORMAT valid values are `string' and `integer'."
 
 (defun tramp-sh-handle-file-writable-p (filename)
   "Like `file-writable-p' for Tramp files."
-  (with-parsed-tramp-file-name filename nil
+  (with-parsed-tramp-file-name (expand-file-name filename) nil
     (with-tramp-file-property v localname "file-writable-p"
       (if (file-exists-p filename)
          (if (tramp-file-property-p v localname "file-attributes")
@@ -1705,12 +1768,13 @@ ID-FORMAT valid values are `string' and `integer'."
              (tramp-check-cached-permissions v ?w)
            (tramp-run-test "-w" filename))
        ;; If file doesn't exist, check if directory is writable.
-       (and (file-exists-p (file-name-directory filename))
-            (tramp-run-test "-w" (file-name-directory filename)))))))
+       (and
+        (file-directory-p (file-name-directory filename))
+        (file-writable-p (file-name-directory filename)))))))
 
 (defun tramp-sh-handle-file-ownership-preserved-p (filename &optional group)
   "Like `file-ownership-preserved-p' for Tramp files."
-  (with-parsed-tramp-file-name filename nil
+  (with-parsed-tramp-file-name (expand-file-name filename) nil
     (with-tramp-file-property
        v localname
        (format "file-ownership-preserved-p%s" (if group "-group" ""))
@@ -1847,8 +1911,8 @@ ID-FORMAT valid values are `string' and `integer'."
        v 'file-error
        "add-name-to-file: %s"
        "only implemented for same method, same user, same host")))
-  (with-parsed-tramp-file-name filename v1
-    (with-parsed-tramp-file-name newname v2
+  (with-parsed-tramp-file-name (expand-file-name filename) v1
+    (with-parsed-tramp-file-name (expand-file-name newname) v2
       (let ((ln (when v1 (tramp-get-remote-ln v1))))
 
        ;; Do the 'confirm if exists' thing.
@@ -1944,7 +2008,7 @@ ID-FORMAT valid values are `string' and `integer'."
 
        ;; When newname did exist, we have wrong cached values.
        (when t2
-         (with-parsed-tramp-file-name newname nil
+         (with-parsed-tramp-file-name (expand-file-name newname) nil
            (tramp-flush-file-properties v localname)))))))
 
 (defun tramp-sh-handle-rename-file
@@ -1980,6 +2044,7 @@ This function is invoked by `tramp-sh-handle-copy-file' 
and
 `tramp-sh-handle-rename-file'.  It is an error if OP is neither
 of `copy' and `rename'.  FILENAME and NEWNAME must be absolute
 file names."
+  ;; FILENAME and NEWNAME are already expanded.
   (unless (memq op '(copy rename))
     (error "Unknown operation `%s', must be `copy' or `rename'" op))
 
@@ -2092,6 +2157,7 @@ file names."
 First arg OP is either `copy' or `rename' and indicates the operation.
 FILENAME is the source file, NEWNAME the target file.
 KEEP-DATE is non-nil if NEWNAME should have the same timestamp as FILENAME."
+  ;; FILENAME and NEWNAME are already expanded.
   ;; Check, whether file is too large.  Emacs checks in `insert-file-1'
   ;; and `find-file-noselect', but that's not called here.
   (abort-if-file-too-large
@@ -2134,6 +2200,7 @@ the file (for rename).  Both files must reside on the 
same host.
 KEEP-DATE means to make sure that NEWNAME has the same timestamp
 as FILENAME.  PRESERVE-UID-GID, when non-nil, instructs to keep
 the uid and gid from FILENAME."
+  ;; FILENAME and NEWNAME are already expanded.
   (let ((t1 (tramp-tramp-file-p filename))
        (t2 (tramp-tramp-file-p newname))
        (file-times (file-attribute-modification-time
@@ -2282,6 +2349,7 @@ the uid and gid from FILENAME."
     (op filename newname ok-if-already-exists keep-date)
   "Invoke `scp' program to copy.
 The method used must be an out-of-band method."
+  ;; FILENAME and NEWNAME are already expanded.
   (let* ((v1 (and (tramp-tramp-file-p filename)
                  (tramp-dissect-file-name filename)))
         (v2 (and (tramp-tramp-file-p newname)
@@ -2517,7 +2585,7 @@ The method used must be an out-of-band method."
 
 (defun tramp-sh-handle-delete-file (filename &optional trash)
   "Like `delete-file' for Tramp files."
-  (setq filename (expand-file-name filename))
+  (setq filename (expand-file-name (expand-file-name filename)))
   (with-parsed-tramp-file-name filename nil
     (if (and delete-by-moving-to-trash trash)
        (move-file-to-trash filename)
@@ -2535,7 +2603,7 @@ The method used must be an out-of-band method."
   (if (>= emacs-major-version 29)
       (tramp-run-real-handler #'dired-compress-file (list file))
     ;; Code stolen mainly from dired-aux.el.
-    (with-parsed-tramp-file-name file nil
+    (with-parsed-tramp-file-name (expand-file-name file) nil
       (tramp-flush-file-properties v localname)
       (let ((suffixes dired-compress-file-suffixes)
            suffix)
@@ -2662,7 +2730,7 @@ The method used must be an out-of-band method."
          (narrow-to-region beg-marker end-marker)
          ;; Check for "--dired" output.
          (when (re-search-backward
-                (rx bol "//DIRED//" (+ space) (group (+ nonl)) eol)
+                (rx bol "//DIRED//" (+ blank) (group (+ nonl)) eol)
                 nil 'noerror)
            (let ((beg (match-beginning 1))
                  (end (match-end 0)))
@@ -2735,7 +2803,7 @@ The method used must be an out-of-band method."
          ;; Try to insert the amount of free space.
          (goto-char (point-min))
          ;; First find the line to put it on.
-         (when (and (re-search-forward (rx bol (group (* space) "total")) nil 
t)
+         (when (and (re-search-forward (rx bol (group (* blank) "total")) nil 
t)
                     ;; Emacs 29.1 or later.
                     (not (fboundp 'dired--insert-disk-space)))
            (when-let ((available (get-free-disk-space ".")))
@@ -2762,7 +2830,8 @@ the result will be a local, non-Tramp, file name."
   ;; by `file-name-absolute-p'.
   (if (and (eq system-type 'windows-nt)
           (string-match-p
-           (rx bol (| (: alpha ":") (: (literal null-device) eol))) name))
+           (tramp-compat-rx bol (| (: alpha ":") (: (literal null-device) 
eol)))
+           name))
       (tramp-run-real-handler #'expand-file-name (list name dir))
     ;; Unless NAME is absolute, concat DIR and NAME.
     (unless (file-name-absolute-p name)
@@ -2779,7 +2848,8 @@ the result will be a local, non-Tramp, file name."
        ;; supposed to find such a shell on the remote host.  Please
        ;; tell me about it when this doesn't work on your system.
        (when (string-match
-              (rx bos "~" (group (* (not (any "/")))) (group (* nonl)) eos)
+              (tramp-compat-rx
+               bos "~" (group (* (not "/"))) (group (* nonl)) eos)
               localname)
          (let ((uname (match-string 1 localname))
                (fname (match-string 2 localname))
@@ -3744,6 +3814,7 @@ Fall back to normal file name handler if no Tramp handler 
exists."
                (concat "create,modify,move,moved_from,moved_to,move_self,"
                        "delete,delete_self,ignored"))
               ((memq 'attribute-change flags) "attrib,ignored"))
+             ;; "-P" has been added to version 3.21, so we cannot assume it 
yet.
              sequence `(,command "-mq" "-e" ,events ,localname)
              ;; Make events a list of symbols.
              events
@@ -3839,7 +3910,7 @@ Fall back to normal file name handler if no Tramp handler 
exists."
           ((string-match
            (rx "Supported arguments for "
                "GIO_USE_FILE_MONITOR environment variable:\n"
-               (* space) (group (+ alpha)) " - 20")
+               (* blank) (group (+ alpha)) " - 20")
            string)
           (setq pos (match-end 0))
            (intern
@@ -3851,10 +3922,11 @@ Fall back to normal file name handler if no Tramp 
handler exists."
       (setq string (tramp-compat-string-replace "\n\n" "\n" string))
 
       (while (string-match
-             (rx bol (+ (not (any ":"))) ":" space
-                 (group (+ (not (any ":")))) ":" space
-                 (group (regexp (regexp-opt tramp-gio-events)))
-                 (? space (group (+ (not (any ":"))))) eol)
+             (tramp-compat-rx
+              bol (+ (not ":")) ":" blank
+              (group (+ (not ":"))) ":" blank
+              (group (regexp (regexp-opt tramp-gio-events)))
+              (? blank (group (+ (not ":")))) eol)
              string)
 
         (let* ((file (match-string 1 string))
@@ -3928,9 +4000,9 @@ Fall back to normal file name handler if no Tramp handler 
exists."
          (goto-char (point-min))
          (forward-line)
          (when (looking-at
-                (rx (? bol "/" (* (not space)) space) (* space)
-                    (group (+ digit)) (+ space)
-                    (group (+ digit)) (+ space)
+                (rx (? bol "/" (* (not blank)) blank) (* blank)
+                    (group (+ digit)) (+ blank)
+                    (group (+ digit)) (+ blank)
                     (group (+ digit))))
            (mapcar
             (lambda (d)
@@ -3946,58 +4018,73 @@ Fall back to normal file name handler if no Tramp 
handler exists."
 
 (defun tramp-expand-script (vec script)
   "Expand SCRIPT with remote files or commands.
-\"%a\", \"%h\", \"%l\", \"%o\", \"%p\", \"%r\" and \"%s\" format
-specifiers are replaced by the respective `awk', `hexdump', `ls',
-`od', `perl', `readlink' and `stat' commands.  \"%n\" is replaced
-by \"2>/dev/null\", and \"%t\" is replaced by a temporary file
-name.  If VEC is nil, the respective local commands are used.  If
-there is a format specifier which cannot be expanded, this
-function returns nil."
+\"%a\", \"%h\", \"%l\", \"%o\", \"%p\", \"%r\", \"%s\" and \"%y\"
+format specifiers are replaced by the respective `awk',
+`hexdump', `ls', `od', `perl', `readlink', `stat' and `python'
+commands.  \"%n\" is replaced by \"2>/dev/null\", and \"%t\" is
+replaced by a temporary file name.  If VEC is nil, the respective
+local commands are used.  If there is a format specifier which
+cannot be expanded, this function returns nil."
   (if (not (string-match-p
-           (rx (| bol (not (any "%"))) "%" (any "ahlnoprst")) script))
+           (tramp-compat-rx (| bol (not "%")) "%" (any "ahlnoprsty")) script))
       script
     (catch 'wont-work
-      (let ((awk (when (string-match-p (rx (| bol (not (any "%"))) "%a") 
script)
+      (let ((awk (when (string-match-p
+                       (tramp-compat-rx (| bol (not "%")) "%a") script)
                   (or
                    (if vec (tramp-get-remote-awk vec) (executable-find "awk"))
                    (throw 'wont-work nil))))
-           (hdmp (when (string-match-p (rx (| bol (not (any "%"))) "%h") 
script)
+           (hdmp (when (string-match-p
+                        (tramp-compat-rx (| bol (not "%")) "%h") script)
                    (or
                     (if vec (tramp-get-remote-hexdump vec)
                       (executable-find "hexdump"))
                     (throw 'wont-work nil))))
-           (dev (when (string-match-p (rx (| bol (not (any "%"))) "%n") script)
+           (dev (when (string-match-p
+                       (tramp-compat-rx (| bol (not "%")) "%n") script)
                   (or
                    (if vec (concat "2>" (tramp-get-remote-null-device vec))
                      (if (eq system-type 'windows-nt) ""
                        (concat "2>" null-device)))
                    (throw 'wont-work nil))))
-           (ls (when (string-match-p (rx (| bol (not (any "%"))) "%l") script)
+           (ls (when (string-match-p
+                      (tramp-compat-rx (| bol (not "%")) "%l") script)
                  (format "%s %s"
                          (or (tramp-get-ls-command vec)
                              (throw 'wont-work nil))
                          (tramp-sh--quoting-style-options vec))))
-           (od (when (string-match-p (rx (| bol (not (any "%"))) "%o") script)
+           (od (when (string-match-p
+                      (tramp-compat-rx (| bol (not "%")) "%o") script)
                  (or (if vec (tramp-get-remote-od vec) (executable-find "od"))
                      (throw 'wont-work nil))))
-           (perl (when (string-match-p (rx (| bol (not (any "%"))) "%p") 
script)
+           (perl (when (string-match-p
+                        (tramp-compat-rx (| bol (not "%")) "%p") script)
                    (or
                     (if vec
                         (tramp-get-remote-perl vec) (executable-find "perl"))
                     (throw 'wont-work nil))))
+           (python (when (string-match-p
+                          (tramp-compat-rx (| bol (not "%")) "%y") script)
+                   (or
+                    (if vec
+                        (tramp-get-remote-python vec)
+                      (executable-find "python"))
+                    (throw 'wont-work nil))))
            (readlink (when (string-match-p
-                            (rx (| bol (not (any "%"))) "%r") script)
+                            (tramp-compat-rx (| bol (not "%")) "%r") script)
                        (or
                         (if vec
                         (tramp-get-remote-readlink vec)
                       (executable-find "readlink"))
                     (throw 'wont-work nil))))
-           (stat (when (string-match-p (rx (| bol (not (any "%"))) "%s") 
script)
+           (stat (when (string-match-p
+                        (tramp-compat-rx (| bol (not "%")) "%s") script)
                    (or
                     (if vec
                         (tramp-get-remote-stat vec) (executable-find "stat"))
                     (throw 'wont-work nil))))
-           (tmp (when (string-match-p (rx (| bol (not (any "%"))) "%t") script)
+           (tmp (when (string-match-p
+                       (tramp-compat-rx (| bol (not "%")) "%t") script)
                   (or
                    (if vec
                        (tramp-file-local-name (tramp-make-tramp-temp-name vec))
@@ -4007,7 +4094,7 @@ function returns nil."
         script
         (format-spec-make
          ?a awk ?h hdmp ?l ls ?n dev ?o od ?p perl
-         ?r readlink ?s stat ?t tmp))))))
+         ?r readlink ?s stat ?t tmp ?y python))))))
 
 (defun tramp-maybe-send-script (vec script name)
   "Define in remote shell function NAME implemented as SCRIPT.
@@ -4070,7 +4157,7 @@ This function expects to be in the right *tramp* buffer."
       (unless (or ignore-path (tramp-check-remote-uname vec 
tramp-sunos-unames))
        (tramp-send-command vec (format "which \\%s | wc -w" progname))
        (goto-char (point-min))
-       (if (looking-at-p (rx bol (* space) "1" eol))
+       (if (looking-at-p (rx bol (* blank) "1" eol))
            (setq result (concat "\\" progname))))
       (unless result
        (when ignore-tilde
@@ -4237,10 +4324,9 @@ file exists and nonzero exit status otherwise."
     ;; first.
     (tramp-send-command
      vec (format
-         (eval-when-compile
-           (concat
-            "exec env TERM='%s' INSIDE_EMACS='%s' "
-            "ENV=%s %s PROMPT_COMMAND='' PS1=%s PS2='' PS3='' %s %s -i"))
+         (concat
+          "exec env TERM='%s' INSIDE_EMACS='%s' "
+          "ENV=%s %s PROMPT_COMMAND='' PS1=%s PS2='' PS3='' %s %s -i")
           tramp-terminal-type (tramp-inside-emacs)
           (or (getenv-internal "ENV" tramp-remote-process-environment) "")
          (if (stringp tramp-histfile-override)
@@ -4259,7 +4345,8 @@ file exists and nonzero exit status otherwise."
      "Couldn't find remote shell prompt for %s" shell)
     (unless
        (tramp-check-for-regexp
-        (tramp-get-connection-process vec) (rx (literal tramp-end-of-output)))
+        (tramp-get-connection-process vec)
+        (tramp-compat-rx (literal tramp-end-of-output)))
       (tramp-wait-for-output (tramp-get-connection-process vec))
       (tramp-message vec 5 "Setting shell prompt")
       (tramp-send-command
@@ -4300,7 +4387,8 @@ file exists and nonzero exit status otherwise."
                (tramp-send-command
                 vec (format "echo ~%s" tramp-root-id-string) t)
                (if (or (string-match-p
-                        (rx bol "~" (literal tramp-root-id-string) eol)
+                        (tramp-compat-rx
+                         bol "~" (literal tramp-root-id-string) eol)
                         (buffer-string))
                        ;; The default shell (ksh93) of OpenSolaris
                        ;; and Solaris is buggy.  We've got reports
@@ -4316,10 +4404,9 @@ file exists and nonzero exit status otherwise."
                            default-shell
                          (tramp-message
                           vec 2
-                          (eval-when-compile
-                            (concat
-                             "Couldn't find a remote shell which groks tilde "
-                             "expansion, using `%s'"))
+                          (concat
+                           "Couldn't find a remote shell which groks tilde "
+                           "expansion, using `%s'")
                           default-shell)))
 
                  default-shell)))
@@ -4340,9 +4427,9 @@ seconds.  If not, it produces an error message with the 
given ERROR-ARGS."
     (condition-case nil
        (tramp-wait-for-regexp
         proc timeout
-        (rx (| (regexp shell-prompt-pattern)
-               (regexp tramp-shell-prompt-pattern))
-            eos))
+        (tramp-compat-rx
+         (| (regexp shell-prompt-pattern) (regexp tramp-shell-prompt-pattern))
+         eos))
       (error
        (delete-process proc)
        (apply #'tramp-error-with-buffer
@@ -4702,7 +4789,7 @@ Goes through the list `tramp-local-coding-commands' and
 
                  (with-current-buffer (tramp-get-connection-buffer vec)
                    (goto-char (point-min))
-                   (unless (looking-at-p (rx (literal magic)))
+                   (unless (looking-at-p (tramp-compat-rx (literal magic)))
                      (throw 'wont-work-remote nil)))
 
                  ;; `rem-enc' and `rem-dec' could be a string meanwhile.
@@ -4788,7 +4875,7 @@ Goes through the list `tramp-inline-compress-commands'."
                      nil t))
               (throw 'next nil))
            (goto-char (point-min))
-           (unless (looking-at-p (rx (literal magic)))
+           (unless (looking-at-p (tramp-compat-rx (literal magic)))
              (throw 'next nil)))
           (tramp-message
           vec 5
@@ -4799,7 +4886,7 @@ Goes through the list `tramp-inline-compress-commands'."
            (throw 'next nil))
          (with-current-buffer (tramp-get-buffer vec)
            (goto-char (point-min))
-           (unless (looking-at-p (rx (literal magic)))
+           (unless (looking-at-p (tramp-compat-rx (literal magic)))
              (throw 'next nil)))
          (setq found t)))
 
@@ -4980,8 +5067,9 @@ Goes through the list `tramp-inline-compress-commands'."
                     string
                     (and
                      (string-match
-                      (rx bol (+ (not (any " #"))) " " (+ (not space)) " "
-                          (group (+ (not space))) eol)
+                      (rx bol (+ (not (any blank "#"))) blank
+                          (+ (not blank)) blank
+                          (group (+ (not blank))) eol)
                       string)
                      (match-string 1 string))
                     found
@@ -5287,14 +5375,15 @@ function waits for output unless NOOUTPUT is set."
           ;; Busyboxes built with the EDITING_ASK_TERMINAL config
           ;; option send also escape sequences, which must be
           ;; ignored.
-          (regexp (rx (* (not (any "#$\n")))
-                      (literal tramp-end-of-output)
-                      (? (regexp tramp-device-escape-sequence-regexp))
-                      (? "\r") eol))
+          (regexp (tramp-compat-rx
+                   (* (not (any "#$\n")))
+                   (literal tramp-end-of-output)
+                   (? (regexp tramp-device-escape-sequence-regexp))
+                   (? "\r") eol))
           ;; Sometimes, the commands do not return a newline but a
           ;; null byte before the shell prompt, for example "git
           ;; ls-files -c -z ...".
-          (regexp1 (rx (| bol "\000") (regexp regexp)))
+          (regexp1 (tramp-compat-rx (| bol "\000") (regexp regexp)))
           (found (tramp-wait-for-regexp proc timeout regexp1)))
       (if found
          (let ((inhibit-read-only t))
@@ -5334,7 +5423,8 @@ the exit status."
   (let (cmd data)
     (if (and (stringp command)
             (string-match
-             (rx (* nonl) "<<'" (literal tramp-end-of-heredoc) "'" (* nonl))
+             (tramp-compat-rx
+              (* nonl) "<<'" (literal tramp-end-of-heredoc) "'" (* nonl))
              command))
        (setq cmd (match-string 0 command)
              data (substring command (match-end 0)))
@@ -5396,7 +5486,7 @@ raises an error."
                     (unless noerror signal-hook-function)))
                (read (current-buffer)))
            ;; Error handling.
-           (when (re-search-forward (rx (not space)) (line-end-position) t)
+           (when (re-search-forward (rx (not blank)) (line-end-position) t)
              (error nil)))
        (error (unless noerror
                 (tramp-error
@@ -5504,7 +5594,7 @@ Nonexistent directories are removed from spec."
                    (tramp-get-method-parameter vec 'tramp-remote-shell-args)
                    " ")
                   (tramp-shell-quote-argument tramp-end-of-heredoc))
-                 'noerror (rx (literal tramp-end-of-heredoc)))
+                 'noerror (tramp-compat-rx (literal tramp-end-of-heredoc)))
                 (progn
                   (tramp-message
                    vec 2 "Could not retrieve `tramp-own-remote-path'")
@@ -5554,7 +5644,7 @@ Nonexistent directories are removed from spec."
        (while candidates
          (goto-char (point-min))
          (if (string-match-p
-              (rx bol (literal (car candidates))"%s" (? "\r") eol)
+              (tramp-compat-rx bol (literal (car candidates)) (? "\r") eol)
               (buffer-string))
              (setq locale (car candidates)
                    candidates nil)
@@ -5633,7 +5723,7 @@ Nonexistent directories are removed from spec."
        vec (format "( %s / -nt / )" (tramp-get-test-command vec)))
        (with-current-buffer (tramp-get-buffer vec)
         (goto-char (point-min))
-        (when (looking-at-p (rx (literal tramp-end-of-output)))
+        (when (looking-at-p (tramp-compat-rx (literal tramp-end-of-output)))
           (format "%s %%s -nt %%s" (tramp-get-test-command vec)))))
      (progn
        (tramp-send-command
@@ -5792,36 +5882,9 @@ This command is returned only if 
`delete-by-moving-to-trash' is non-nil."
          (while (and dl (setq result (tramp-find-executable vec cmd dl t t)))
            ;; Check POSIX parameter.
            (when (tramp-send-command-and-check vec (format "%s -u" result))
-             (tramp-set-connection-property
-              vec "uid-integer"
-              (with-current-buffer (tramp-get-connection-buffer vec)
-                (goto-char (point-min))
-                (read (current-buffer))))
              (throw 'id-found result))
            (setq dl (cdr dl))))))))
 
-(defun tramp-get-remote-uid-with-id (vec id-format)
-  "Implement `tramp-get-remote-uid' for Tramp files using `id'."
-  ;; `tramp-get-remote-id' sets already connection property "uid-integer".
-  (with-tramp-connection-property vec (format "uid-%s" id-format)
-    (tramp-send-command-and-read
-     vec
-     (format "%s -u%s %s"
-            (tramp-get-remote-id vec)
-            (if (equal id-format 'integer) "" "n")
-            (if (equal id-format 'integer)
-                "" "| sed -e s/^/\\\"/ -e s/\\$/\\\"/")))))
-
-(defun tramp-get-remote-uid-with-perl (vec id-format)
-  "Implement `tramp-get-remote-uid' for Tramp files using a Perl script."
-  (tramp-send-command-and-read
-   vec
-   (format "%s -le '%s'"
-          (tramp-get-remote-perl vec)
-          (if (equal id-format 'integer)
-              "print $>"
-            "print \"\\\"\", scalar getpwuid($>), \"\\\"\""))))
-
 (defun tramp-get-remote-python (vec)
   "Determine remote `python' command."
   (with-tramp-connection-property vec "python"
@@ -5829,46 +5892,6 @@ This command is returned only if 
`delete-by-moving-to-trash' is non-nil."
     (or (tramp-find-executable vec "python" (tramp-get-remote-path vec))
         (tramp-find-executable vec "python3" (tramp-get-remote-path vec)))))
 
-(defun tramp-get-remote-uid-with-python (vec id-format)
-  "Implement `tramp-get-remote-uid' for Tramp files using `python'."
-  (tramp-send-command-and-read
-   vec
-   (format "%s -c \"%s\""
-          (tramp-get-remote-python vec)
-          (if (equal id-format 'integer)
-              "import os; print (os.getuid())"
-    "import os, pwd; print ('\\\"' + pwd.getpwuid(os.getuid())[0] + 
'\\\"')"))))
-
-(defun tramp-get-remote-gid-with-id (vec id-format)
-  "Implement `tramp-get-remote-gid' for Tramp files using `id'."
-  (tramp-send-command-and-read
-   vec
-   (format "%s -g%s %s"
-          (tramp-get-remote-id vec)
-          (if (equal id-format 'integer) "" "n")
-          (if (equal id-format 'integer)
-              "" "| sed -e s/^/\\\"/ -e s/\\$/\\\"/"))))
-
-(defun tramp-get-remote-gid-with-perl (vec id-format)
-  "Implement `tramp-get-remote-gid' for Tramp files using a Perl script."
-  (tramp-send-command-and-read
-   vec
-   (format "%s -le '%s'"
-          (tramp-get-remote-perl vec)
-          (if (equal id-format 'integer)
-              "print ($)=~/(\\d+)/)"
-            "print \"\\\"\", scalar getgrgid($)), \"\\\"\""))))
-
-(defun tramp-get-remote-gid-with-python (vec id-format)
-  "Implement `tramp-get-remote-gid' for Tramp files using `python'."
-  (tramp-send-command-and-read
-   vec
-   (format "%s -c \"%s\""
-          (tramp-get-remote-python vec)
-          (if (equal id-format 'integer)
-              "import os; print (os.getgid())"
-    "import os, grp; print ('\\\"' + grp.getgrgid(os.getgid())[0] + 
'\\\"')"))))
-
 (defun tramp-get-remote-busybox (vec)
   "Determine remote `busybox' command."
   (with-tramp-connection-property vec "busybox"
diff --git a/lisp/net/tramp-smb.el b/lisp/net/tramp-smb.el
index 9e63d53262..e55f6bb6ee 100644
--- a/lisp/net/tramp-smb.el
+++ b/lisp/net/tramp-smb.el
@@ -53,7 +53,7 @@
 ;;;###tramp-autoload
 (tramp--with-startup
  (add-to-list 'tramp-default-user-alist
-             `(,(rx bos (literal tramp-smb-method) eos) nil nil))
+             `(,(tramp-compat-rx bos (literal tramp-smb-method) eos) nil nil))
 
  ;; Add completion function for SMB method.
  (tramp-set-completion-function
@@ -92,81 +92,79 @@ this variable \"client min protocol=NT1\"."
   "Version string of the SMB client.")
 
 (defconst tramp-smb-server-version
-  (rx "Domain=[" (* (not (any "]"))) "] "
-      "OS=[" (* (not (any "]"))) "] "
-      "Server=[" (* (not (any "]"))) "]")
+  (tramp-compat-rx "Domain=[" (* (not "]")) "] "
+                  "OS=[" (* (not "]")) "] "
+                  "Server=[" (* (not "]")) "]")
   "Regexp of SMB server identification.")
 
 (defconst tramp-smb-prompt
-  (rx bol (| (: (| "smb:" "PS") " " (+ nonl) "> ")
-            (: (+ space) "Server"
-               (+ space) "Comment" eol)))
+  (rx bol (| (: (| "smb:" "PS") blank (+ nonl) "> ")
+            (: (+ blank) "Server"
+               (+ blank) "Comment" eol)))
   "Regexp used as prompt in smbclient or powershell.")
 
 (defconst tramp-smb-wrong-passwd-regexp
-  (regexp-opt
-   '("NT_STATUS_LOGON_FAILURE"
-     "NT_STATUS_WRONG_PASSWORD"))
+  (rx (| "NT_STATUS_LOGON_FAILURE"
+        "NT_STATUS_WRONG_PASSWORD"))
   "Regexp for login error strings of SMB servers.")
 
 (defconst tramp-smb-errors
   (rx (| ;; Connection error / timeout / unknown command.
-       (: "Connection" (? " to " (+ (not space))) " failed")
+       (: "Connection" (? " to " (+ (not blank))) " failed")
        "Read from server failed, maybe it closed the connection"
        "Call timed out: server did not respond"
-       (: (+ (not space)) ": command not found")
+       (: (+ (not blank)) ": command not found")
        "Server doesn't support UNIX CIFS calls"
-       (regexp (regexp-opt
-               '(;; Samba.
-                 "ERRDOS"
-                 "ERRHRD"
-                 "ERRSRV"
-                 "ERRbadfile"
-                 "ERRbadpw"
-                 "ERRfilexists"
-                 "ERRnoaccess"
-                 "ERRnomem"
-                 "ERRnosuchshare"
-                 ;; See /usr/include/samba-4.0/core/ntstatus.h.
-                 ;; Windows 4.0 (Windows NT), Windows 5.0 (Windows 2000),
-                 ;; Windows 5.1 (Windows XP), Windows 5.2 (Windows Server 
2003),
-                 ;; Windows 6.0 (Windows Vista), Windows 6.1 (Windows 7),
-                 ;; Windows 6.3 (Windows Server 2012, Windows 10).
-                 "NT_STATUS_ACCESS_DENIED"
-                 "NT_STATUS_ACCOUNT_LOCKED_OUT"
-                 "NT_STATUS_BAD_NETWORK_NAME"
-                 "NT_STATUS_CANNOT_DELETE"
-                 "NT_STATUS_CONNECTION_DISCONNECTED"
-                 "NT_STATUS_CONNECTION_REFUSED"
-                 "NT_STATUS_CONNECTION_RESET"
-                 "NT_STATUS_DIRECTORY_NOT_EMPTY"
-                 "NT_STATUS_DUPLICATE_NAME"
-                 "NT_STATUS_FILE_IS_A_DIRECTORY"
-                 "NT_STATUS_HOST_UNREACHABLE"
-                 "NT_STATUS_IMAGE_ALREADY_LOADED"
-                 "NT_STATUS_INVALID_LEVEL"
-                 "NT_STATUS_INVALID_PARAMETER"
-                 "NT_STATUS_INVALID_PARAMETER_MIX"
-                 "NT_STATUS_IO_TIMEOUT"
-                 "NT_STATUS_LOGON_FAILURE"
-                 "NT_STATUS_NETWORK_ACCESS_DENIED"
-                 "NT_STATUS_NOT_IMPLEMENTED"
-                 "NT_STATUS_NO_LOGON_SERVERS"
-                 "NT_STATUS_NO_SUCH_FILE"
-                 "NT_STATUS_NO_SUCH_USER"
-                 "NT_STATUS_NOT_A_DIRECTORY"
-                 "NT_STATUS_NOT_SUPPORTED"
-                 "NT_STATUS_OBJECT_NAME_COLLISION"
-                 "NT_STATUS_OBJECT_NAME_INVALID"
-                 "NT_STATUS_OBJECT_NAME_NOT_FOUND"
-                 "NT_STATUS_OBJECT_PATH_SYNTAX_BAD"
-                 "NT_STATUS_PASSWORD_MUST_CHANGE"
-                 "NT_STATUS_RESOURCE_NAME_NOT_FOUND"
-                 "NT_STATUS_REVISION_MISMATCH"
-                 "NT_STATUS_SHARING_VIOLATION"
-                 "NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE"
-                 "NT_STATUS_UNSUCCESSFUL"
-                 "NT_STATUS_WRONG_PASSWORD")))))
+       (| ;; Samba.
+       "ERRDOS"
+       "ERRHRD"
+       "ERRSRV"
+       "ERRbadfile"
+       "ERRbadpw"
+       "ERRfilexists"
+       "ERRnoaccess"
+       "ERRnomem"
+       "ERRnosuchshare"
+       ;; See /usr/include/samba-4.0/core/ntstatus.h.
+       ;; Windows 4.0 (Windows NT), Windows 5.0 (Windows 2000),
+       ;; Windows 5.1 (Windows XP), Windows 5.2 (Windows Server 2003),
+       ;; Windows 6.0 (Windows Vista), Windows 6.1 (Windows 7),
+       ;; Windows 6.3 (Windows Server 2012, Windows 10).
+       "NT_STATUS_ACCESS_DENIED"
+       "NT_STATUS_ACCOUNT_LOCKED_OUT"
+       "NT_STATUS_BAD_NETWORK_NAME"
+       "NT_STATUS_CANNOT_DELETE"
+       "NT_STATUS_CONNECTION_DISCONNECTED"
+       "NT_STATUS_CONNECTION_REFUSED"
+       "NT_STATUS_CONNECTION_RESET"
+       "NT_STATUS_DIRECTORY_NOT_EMPTY"
+       "NT_STATUS_DUPLICATE_NAME"
+       "NT_STATUS_FILE_IS_A_DIRECTORY"
+       "NT_STATUS_HOST_UNREACHABLE"
+       "NT_STATUS_IMAGE_ALREADY_LOADED"
+       "NT_STATUS_INVALID_LEVEL"
+       "NT_STATUS_INVALID_PARAMETER"
+       "NT_STATUS_INVALID_PARAMETER_MIX"
+       "NT_STATUS_IO_TIMEOUT"
+       "NT_STATUS_LOGON_FAILURE"
+       "NT_STATUS_NETWORK_ACCESS_DENIED"
+       "NT_STATUS_NOT_IMPLEMENTED"
+       "NT_STATUS_NO_LOGON_SERVERS"
+       "NT_STATUS_NO_SUCH_FILE"
+       "NT_STATUS_NO_SUCH_USER"
+       "NT_STATUS_NOT_A_DIRECTORY"
+       "NT_STATUS_NOT_SUPPORTED"
+       "NT_STATUS_OBJECT_NAME_COLLISION"
+       "NT_STATUS_OBJECT_NAME_INVALID"
+       "NT_STATUS_OBJECT_NAME_NOT_FOUND"
+       "NT_STATUS_OBJECT_PATH_SYNTAX_BAD"
+       "NT_STATUS_PASSWORD_MUST_CHANGE"
+       "NT_STATUS_RESOURCE_NAME_NOT_FOUND"
+       "NT_STATUS_REVISION_MISMATCH"
+       "NT_STATUS_SHARING_VIOLATION"
+       "NT_STATUS_TRUSTED_RELATIONSHIP_FAILURE"
+       "NT_STATUS_UNSUCCESSFUL"
+       "NT_STATUS_WRONG_PASSWORD")))
   "Regexp for possible error strings of SMB servers.
 Used instead of analyzing error codes of commands.")
 
@@ -300,6 +298,7 @@ See `tramp-actions-before-shell' for more info.")
     (temporary-file-directory . tramp-handle-temporary-file-directory)
     (tramp-get-home-directory . tramp-smb-handle-get-home-directory)
     (tramp-get-remote-gid . ignore)
+    (tramp-get-remote-groups . ignore)
     (tramp-get-remote-uid . ignore)
     (tramp-set-file-uid-gid . ignore)
     (unhandled-file-name-directory . ignore)
@@ -730,7 +729,7 @@ PRESERVE-UID-GID and PRESERVE-EXTENDED-ATTRIBUTES are 
completely ignored."
     (with-parsed-tramp-file-name name nil
       ;; Tilde expansion if necessary.
       (when (string-match
-            (rx bos "~" (group (* (not (any "/")))) (group (* nonl)) eos)
+            (tramp-compat-rx bos "~" (group (* (not "/"))) (group (* nonl)) 
eos)
             localname)
        (let ((uname (match-string 1 localname))
              (fname (match-string 2 localname))
@@ -886,28 +885,28 @@ PRESERVE-UID-GID and PRESERVE-EXTENDED-ATTRIBUTES are 
completely ignored."
          (while (not (eobp))
            (cond
             ((looking-at
-              (rx "Size:" (+ space) (group (+ digit)) (+ space)
-                  "Blocks:" (+ space) (+ digit) (+ space) (group (+ 
wordchar))))
+              (rx "Size:" (+ blank) (group (+ digit)) (+ blank)
+                  "Blocks:" (+ blank) (+ digit) (+ blank) (group (+ 
wordchar))))
              (setq size (string-to-number (match-string 1))
                    id (if (string-equal "directory" (match-string 2)) t
                         (if (string-equal "symbolic" (match-string 2)) ""))))
             ((looking-at
-              (rx "Inode:" (+ space) (group (+ digit)) (+ space)
-                  "Links:" (+ space) (group (+ digit))))
+              (rx "Inode:" (+ blank) (group (+ digit)) (+ blank)
+                  "Links:" (+ blank) (group (+ digit))))
              (setq inode (string-to-number (match-string 1))
                    link (string-to-number (match-string 2))))
             ((looking-at
-              (rx "Access:" (+ space)
-                  "(" (+ digit) "/" (group (+ (not space))) ")" (+ space)
-                  "Uid:" (+ space) (group (+ digit)) (+ whitespace)
-                  "Gid:" (+ space) (group (+ digit))))
+              (rx "Access:" (+ blank)
+                  "(" (+ digit) "/" (group (+ (not blank))) ")" (+ blank)
+                  "Uid:" (+ blank) (group (+ digit)) (+ blank)
+                  "Gid:" (+ blank) (group (+ digit))))
              (setq mode (match-string 1)
                    uid (match-string 2)
                    gid (match-string 3)))
             ((looking-at
-              (rx "Access:" (+ space)
+              (rx "Access:" (+ blank)
                   (group (+ digit)) "-" (group (+ digit)) "-"
-                  (group (+ digit)) (+ space)
+                  (group (+ digit)) (+ blank)
                   (group (+ digit)) ":" (group (+ digit)) ":"
                   (group (+ digit))))
              (setq atime
@@ -919,9 +918,9 @@ PRESERVE-UID-GID and PRESERVE-EXTENDED-ATTRIBUTES are 
completely ignored."
                     (string-to-number (match-string 2)) ;; month
                     (string-to-number (match-string 1))))) ;; year
             ((looking-at
-              (rx "Modify:" (+ space)
+              (rx "Modify:" (+ blank)
                   (group (+ digit)) "-" (group (+ digit)) "-"
-                  (group (+ digit)) (+ space)
+                  (group (+ digit)) (+ blank)
                   (group (+ digit)) ":" (group (+ digit)) ":"
                   (group (+ digit))))
              (setq mtime
@@ -933,9 +932,9 @@ PRESERVE-UID-GID and PRESERVE-EXTENDED-ATTRIBUTES are 
completely ignored."
                     (string-to-number (match-string 2)) ;; month
                     (string-to-number (match-string 1))))) ;; year
             ((looking-at
-              (rx "Change:" (+ space)
+              (rx "Change:" (+ blank)
                   (group (+ digit)) "-" (group (+ digit)) "-"
-                  (group (+ digit)) (+ space)
+                  (group (+ digit)) (+ blank)
                   (group (+ digit)) ":" (group (+ digit)) ":"
                   (group (+ digit))))
              (setq ctime
@@ -1010,7 +1009,7 @@ PRESERVE-UID-GID and PRESERVE-EXTENDED-ATTRIBUTES are 
completely ignored."
            (goto-char (point-min))
            (forward-line)
            (when (looking-at
-                  (rx (* space) (group (+ digit))
+                  (rx (* blank) (group (+ digit))
                       " blocks of size " (group (+ digit))
                       ". " (group (+ digit)) " blocks available"))
              (setq blocksize (string-to-number (match-string 2))
@@ -1083,7 +1082,8 @@ PRESERVE-UID-GID and PRESERVE-EXTENDED-ATTRIBUTES are 
completely ignored."
                     ;; Check for matching entries.
                     (mapcar
                      (lambda (x)
-                       (when (string-match-p (rx bol (literal base)) (nth 0 x))
+                       (when (string-match-p
+                              (tramp-compat-rx bol (literal base)) (nth 0 x))
                          x))
                      entries)
                   ;; We just need the only and only entry FILENAME.
@@ -1213,50 +1213,47 @@ PRESERVE-UID-GID and PRESERVE-EXTENDED-ATTRIBUTES are 
completely ignored."
 If TARGET is a non-Tramp file, it is used verbatim as the target
 of the symlink.  If TARGET is a Tramp file, only the localname
 component is used as the target of the symlink."
-  (if (not (tramp-tramp-file-p (expand-file-name linkname)))
-      (tramp-run-real-handler
-       #'make-symbolic-link (list target linkname ok-if-already-exists))
-
-    (with-parsed-tramp-file-name linkname nil
-      ;; If TARGET is a Tramp name, use just the localname component.
-      ;; Don't check for a proper method.
-      (let ((non-essential t))
-       (when (and (tramp-tramp-file-p target)
-                  (tramp-file-name-equal-p v (tramp-dissect-file-name target)))
-         (setq target (tramp-file-local-name (expand-file-name target)))))
-
-      ;; If TARGET is still remote, quote it.
-      (if (tramp-tramp-file-p target)
-         (make-symbolic-link (tramp-compat-file-name-quote target 'top)
-          linkname ok-if-already-exists)
-
-       ;; Do the 'confirm if exists' thing.
-       (when (file-exists-p linkname)
-         ;; What to do?
-         (if (or (null ok-if-already-exists) ; not allowed to exist
-                 (and (numberp ok-if-already-exists)
-                      (not (yes-or-no-p
-                            (format
-                             "File %s already exists; make it a link anyway?"
-                             localname)))))
-             (tramp-error v 'file-already-exists localname)
-           (delete-file linkname)))
-
-       (unless (tramp-smb-get-cifs-capabilities v)
-         (tramp-error v 'file-error "make-symbolic-link not supported"))
-
-       ;; We must also flush the cache of the directory, because
-       ;; `file-attributes' reads the values from there.
-       (tramp-flush-file-properties v localname)
+  (with-parsed-tramp-file-name linkname nil
+    ;; If TARGET is a Tramp name, use just the localname component.
+    ;; Don't check for a proper method.
+    (let ((non-essential t))
+      (when (and (tramp-tramp-file-p target)
+                (tramp-file-name-equal-p v (tramp-dissect-file-name target)))
+       (setq target (tramp-file-local-name (expand-file-name target)))))
+
+    ;; If TARGET is still remote, quote it.
+    (if (tramp-tramp-file-p target)
+       (make-symbolic-link
+        (tramp-compat-file-name-quote target 'top)
+        linkname ok-if-already-exists)
 
-       (unless (tramp-smb-send-command
-                v (format "symlink %s %s"
-                          (tramp-smb-shell-quote-argument target)
-                          (tramp-smb-shell-quote-localname v)))
-         (tramp-error
-          v 'file-error
-          "error with make-symbolic-link, see buffer `%s' for details"
-          (tramp-get-connection-buffer v)))))))
+      ;; Do the 'confirm if exists' thing.
+      (when (file-exists-p linkname)
+       ;; What to do?
+       (if (or (null ok-if-already-exists) ; not allowed to exist
+               (and (numberp ok-if-already-exists)
+                    (not (yes-or-no-p
+                          (format
+                           "File %s already exists; make it a link anyway?"
+                           localname)))))
+           (tramp-error v 'file-already-exists localname)
+         (delete-file linkname)))
+
+      (unless (tramp-smb-get-cifs-capabilities v)
+       (tramp-error v 'file-error "make-symbolic-link not supported"))
+
+      ;; We must also flush the cache of the directory, because
+      ;; `file-attributes' reads the values from there.
+      (tramp-flush-file-properties v localname)
+
+      (unless (tramp-smb-send-command
+              v (format "symlink %s %s"
+                        (tramp-smb-shell-quote-argument target)
+                        (tramp-smb-shell-quote-localname v)))
+       (tramp-error
+        v 'file-error
+        "error with make-symbolic-link, see buffer `%s' for details"
+        (tramp-get-connection-buffer v))))))
 
 (defun tramp-smb-handle-process-file
   (program &optional infile destination display &rest args)
@@ -1633,7 +1630,7 @@ VEC or USER, or if there is no home directory, return 
nil."
   (save-match-data
     (let ((localname (tramp-file-name-unquote-localname vec)))
       (when (string-match
-            (rx bol (? "/") (group (+ (not (any "/")))) "/") localname)
+            (tramp-compat-rx bol (? "/") (group (+ (not "/"))) "/") localname)
        (match-string 1 localname)))))
 
 (defun tramp-smb-get-localname (vec)
@@ -1644,7 +1641,8 @@ If VEC has no cifs capabilities, exchange \"/\" by 
\"\\\\\"."
       (setq
        localname
        (if (string-match
-           (rx bol (? "/") (+ (not (any "/"))) (group "/" (* nonl))) localname)
+           (tramp-compat-rx bol (? "/") (+ (not "/")) (group "/" (* nonl)))
+           localname)
           ;; There is a share, separated by "/".
           (if (not (tramp-smb-get-cifs-capabilities vec))
               (mapconcat
@@ -1653,16 +1651,16 @@ If VEC has no cifs capabilities, exchange \"/\" by 
\"\\\\\"."
             (match-string 1 localname))
         ;; There is just a share.
         (if (string-match
-             (rx bol (? "/") (group (+ (not (any "/")))) eol) localname)
+             (tramp-compat-rx bol (? "/") (group (+ (not "/"))) eol) localname)
             (match-string 1 localname)
           "")))
 
       ;; Sometimes we have discarded `substitute-in-file-name'.
-      (when (string-match (rx (group "$$") (group (| "/" eol))) localname)
+      (when (string-match (rx (group "$$") (| "/" eol)) localname)
        (setq localname (replace-match "$" nil nil localname 1)))
 
       ;; A trailing space is not supported.
-      (when (string-match-p (rx " " eol) localname)
+      (when (string-match-p (rx blank eol) localname)
        (tramp-error
         vec 'file-error
         "Invalid file name %s" (tramp-make-tramp-file-name vec localname)))
@@ -1763,7 +1761,7 @@ Result is a list of (LOCALNAME MODE SIZE MONTH DAY TIME 
YEAR)."
 ;;
 ;; Problems:
 ;; * Modern regexp constructs, like spy groups and counted repetitions, aren't
-;;   available in older Emacsen.
+;;   available in older versions of Emacs.
 ;; * The length of constructs (file name, size) might exceed the default.
 ;; * File names might contain spaces.
 ;; * Permissions might be empty.
@@ -1782,7 +1780,7 @@ are listed.  Result is the list (LOCALNAME MODE SIZE 
MTIME)."
 
        ;; Read share entries.
        (when (string-match
-              (rx bol "Disk|" (group (+ (not (any "|")))) "|") line)
+              (tramp-compat-rx bol "Disk|" (group (+ (not "|"))) "|") line)
          (setq localname (match-string 1 line)
                mode "dr-xr-xr-x"
                size 0))
@@ -1821,7 +1819,7 @@ are listed.  Result is the list (LOCALNAME MODE SIZE 
MTIME)."
          (cl-return))
 
        ;; weekday.
-       (if (string-match-p (rx (group (+ wordchar)) eol) line)
+       (if (string-match-p (rx (+ wordchar) eol) line)
            (setq line (substring line 0 -5))
          (cl-return))
 
@@ -1855,9 +1853,9 @@ are listed.  Result is the list (LOCALNAME MODE SIZE 
MTIME)."
 
        ;; localname.
        (if (string-match
-            (rx bol (+ space)
-                (group (not space) (? (group (* nonl) (not space))))
-                (* space) eol)
+            (rx bol (+ blank)
+                (group (not blank) (? (* nonl) (not blank)))
+                (* blank) eol)
             line)
            (setq localname (match-string 1 line))
          (cl-return))))
diff --git a/lisp/net/tramp-sshfs.el b/lisp/net/tramp-sshfs.el
index 31720a605e..3c67fa6ea2 100644
--- a/lisp/net/tramp-sshfs.el
+++ b/lisp/net/tramp-sshfs.el
@@ -46,6 +46,9 @@
   :version "28.1"
   :type 'string)
 
+;;;###tramp-autoload
+(defvar tramp-default-remote-shell) ;; Silence byte compiler.
+
 ;;;###tramp-autoload
 (tramp--with-startup
  (add-to-list 'tramp-methods
@@ -150,6 +153,7 @@
     (temporary-file-directory . tramp-handle-temporary-file-directory)
     (tramp-get-home-directory . ignore)
     (tramp-get-remote-gid . ignore)
+    (tramp-get-remote-groups . ignore)
     (tramp-get-remote-uid . ignore)
     (tramp-set-file-uid-gid . ignore)
     (unhandled-file-name-directory . ignore)
diff --git a/lisp/net/tramp-sudoedit.el b/lisp/net/tramp-sudoedit.el
index 893afcdbbe..dc87c590b3 100644
--- a/lisp/net/tramp-sudoedit.el
+++ b/lisp/net/tramp-sudoedit.el
@@ -49,7 +49,7 @@
                (tramp-password-previous-hop t)))
 
  (add-to-list 'tramp-default-user-alist
-             `(,(rx bos (literal tramp-sudoedit-method) eos)
+             `(,(tramp-compat-rx bos (literal tramp-sudoedit-method) eos)
                nil ,tramp-root-id-string))
 
  (tramp-set-completion-function
@@ -143,6 +143,7 @@ See `tramp-actions-before-shell' for more info.")
     (temporary-file-directory . tramp-handle-temporary-file-directory)
     (tramp-get-home-directory . tramp-sudoedit-handle-get-home-directory)
     (tramp-get-remote-gid . tramp-sudoedit-handle-get-remote-gid)
+    (tramp-get-remote-groups . tramp-sudoedit-handle-get-remote-groups)
     (tramp-get-remote-uid . tramp-sudoedit-handle-get-remote-uid)
     (tramp-set-file-uid-gid . tramp-sudoedit-handle-set-file-uid-gid)
     (unhandled-file-name-directory . ignore)
@@ -193,8 +194,8 @@ arguments to pass to the OPERATION."
        v 'file-error
        "add-name-to-file: %s"
        "only implemented for same method, same user, same host")))
-  (with-parsed-tramp-file-name filename v1
-    (with-parsed-tramp-file-name newname v2
+  (with-parsed-tramp-file-name (expand-file-name filename) v1
+    (with-parsed-tramp-file-name (expand-file-name newname) v2
        ;; Do the 'confirm if exists' thing.
        (when (file-exists-p newname)
          ;; What to do?
@@ -234,6 +235,7 @@ This function is invoked by 
`tramp-sudoedit-handle-copy-file' and
 `tramp-sudoedit-handle-rename-file'.  It is an error if OP is
 neither of `copy' and `rename'.  FILENAME and NEWNAME must be
 absolute file names."
+  ;; FILENAME and NEWNAME are already expanded.
   (unless (memq op '(copy rename))
     (error "Unknown operation `%s', must be `copy' or `rename'" op))
 
@@ -344,7 +346,7 @@ absolute file names."
 
 (defun tramp-sudoedit-handle-delete-file (filename &optional trash)
   "Like `delete-file' for Tramp files."
-  (with-parsed-tramp-file-name filename nil
+  (with-parsed-tramp-file-name (expand-file-name filename) nil
     (tramp-flush-file-properties v localname)
     (if (and delete-by-moving-to-trash trash)
        (move-file-to-trash filename)
@@ -376,7 +378,7 @@ the result will be a local, non-Tramp, file name."
     (unless (file-name-absolute-p localname)
       (setq localname (format "~%s/%s" user localname)))
     (when (string-match
-          (rx bos "~" (group (* (not (any "/")))) (group (* nonl)) eos)
+          (tramp-compat-rx bos "~" (group (* (not "/"))) (group (* nonl)) eos)
           localname)
       (let ((uname (match-string 1 localname))
            (fname (match-string 2 localname))
@@ -402,7 +404,7 @@ the result will be a local, non-Tramp, file name."
 
 (defun tramp-sudoedit-handle-file-acl (filename)
   "Like `file-acl' for Tramp files."
-  (with-parsed-tramp-file-name filename nil
+  (with-parsed-tramp-file-name (expand-file-name filename) nil
     (with-tramp-file-property v localname "file-acl"
       (let ((result (and (tramp-sudoedit-remote-acl-p v)
                         (tramp-sudoedit-send-command-string
@@ -439,10 +441,15 @@ the result will be a local, non-Tramp, file name."
 
 (defun tramp-sudoedit-handle-file-executable-p (filename)
   "Like `file-executable-p' for Tramp files."
-  (with-parsed-tramp-file-name filename nil
+  (with-parsed-tramp-file-name (expand-file-name filename) nil
     (with-tramp-file-property v localname "file-executable-p"
-      (tramp-sudoedit-send-command
-       v "test" "-x" (tramp-compat-file-name-unquote localname)))))
+      ;; Examine `file-attributes' cache to see if request can be
+      ;; satisfied without remote operation.
+      (if (tramp-file-property-p v localname "file-attributes")
+         (or (tramp-check-cached-permissions v ?x)
+             (tramp-check-cached-permissions v ?s))
+       (tramp-sudoedit-send-command
+        v "test" "-x" (tramp-compat-file-name-unquote localname))))))
 
 (defun tramp-sudoedit-handle-file-exists-p (filename)
   "Like `file-exists-p' for Tramp files."
@@ -450,10 +457,12 @@ the result will be a local, non-Tramp, file name."
   ;; We don't want to run it when `non-essential' is t, or there is
   ;; no connection process yet.
   (when (tramp-connectable-p filename)
-    (with-parsed-tramp-file-name filename nil
+    (with-parsed-tramp-file-name (expand-file-name filename) nil
       (with-tramp-file-property v localname "file-exists-p"
-       (tramp-sudoedit-send-command
-        v "test" "-e" (tramp-compat-file-name-unquote localname))))))
+       (if (tramp-file-property-p v localname "file-attributes")
+           (not (null (tramp-get-file-property v localname "file-attributes")))
+         (tramp-sudoedit-send-command
+          v "test" "-e" (tramp-compat-file-name-unquote localname)))))))
 
 (defun tramp-sudoedit-handle-file-name-all-completions (filename directory)
   "Like `file-name-all-completions' for Tramp files."
@@ -473,18 +482,21 @@ the result will be a local, non-Tramp, file name."
        (delq
         nil
         (mapcar
-         (lambda (l) (and (not (string-match-p (rx bol (* space) eol) l)) l))
+         (lambda (l) (and (not (string-match-p (rx bol (* blank) eol) l)) l))
          (split-string
           (tramp-get-buffer-string (tramp-get-connection-buffer v))
           "\n" 'omit))))))))
 
 (defun tramp-sudoedit-handle-file-readable-p (filename)
   "Like `file-readable-p' for Tramp files."
-  (with-parsed-tramp-file-name filename nil
+  (with-parsed-tramp-file-name (expand-file-name filename) nil
     (with-tramp-file-property v localname "file-readable-p"
-      (or (tramp-handle-file-readable-p filename)
-         (tramp-sudoedit-send-command
-          v "test" "-r" (tramp-compat-file-name-unquote localname))))))
+      ;; Examine `file-attributes' cache to see if request can be
+      ;; satisfied without remote operation.
+      (if (tramp-file-property-p v localname "file-attributes")
+         (tramp-handle-file-readable-p filename)
+       (tramp-sudoedit-send-command
+        v "test" "-r" (tramp-compat-file-name-unquote localname))))))
 
 (defun tramp-sudoedit-handle-set-file-modes (filename mode &optional flag)
   "Like `set-file-modes' for Tramp files."
@@ -504,13 +516,14 @@ the result will be a local, non-Tramp, file name."
 
 (defun tramp-sudoedit-handle-file-selinux-context (filename)
   "Like `file-selinux-context' for Tramp files."
-  (with-parsed-tramp-file-name filename nil
+  (with-parsed-tramp-file-name (expand-file-name filename) nil
     (with-tramp-file-property v localname "file-selinux-context"
       (let ((context '(nil nil nil nil))
-           (regexp (rx (group (+ (any "_" alnum))) ":"
-                       (group (+ (any "_" alnum))) ":"
-                       (group (+ (any "_" alnum))) ":"
-                       (group (+ (any "_" alnum))))))
+           (regexp (tramp-compat-rx
+                    (group (+ (any "_" alnum))) ":"
+                    (group (+ (any "_" alnum))) ":"
+                    (group (+ (any "_" alnum))) ":"
+                    (group (+ (any "_" alnum))))))
        (when (and (tramp-sudoedit-remote-selinux-p v)
                   (tramp-sudoedit-send-command
                    v "ls" "-d" "-Z"
@@ -535,9 +548,9 @@ the result will be a local, non-Tramp, file name."
          (goto-char (point-min))
          (forward-line)
          (when (looking-at
-                (rx (* space) (group (+ digit))
-                    (+ space) (group (+ digit))
-                    (+ space) (group (+ digit))))
+                (rx (* blank) (group (+ digit))
+                    (+ blank) (group (+ digit))
+                    (+ blank) (group (+ digit))))
            (list (string-to-number (match-string 1))
                  ;; The second value is the used size.  We need the
                  ;; free size.
@@ -555,7 +568,7 @@ the result will be a local, non-Tramp, file name."
               nil
             time)))
       (tramp-sudoedit-send-command
-       v "env" "TZ=UTC" "touch" "-t"
+       v "env" "TZ=UTC0" "touch" "-t"
        (format-time-string "%Y%m%d%H%M.%S" time t)
        (if (eq flag 'nofollow) "-h" "")
        (tramp-compat-file-name-unquote localname)))))
@@ -593,14 +606,19 @@ the result will be a local, non-Tramp, file name."
 
 (defun tramp-sudoedit-handle-file-writable-p (filename)
   "Like `file-writable-p' for Tramp files."
-  (with-parsed-tramp-file-name filename nil
+  (with-parsed-tramp-file-name (expand-file-name filename) nil
     (with-tramp-file-property v localname "file-writable-p"
       (if (file-exists-p filename)
-         (tramp-sudoedit-send-command
-          v "test" "-w" (tramp-compat-file-name-unquote localname))
-       (let ((dir (file-name-directory filename)))
-         (and (file-exists-p dir)
-              (file-writable-p dir)))))))
+         (if (tramp-file-property-p v localname "file-attributes")
+             ;; Examine `file-attributes' cache to see if request can
+             ;; be satisfied without remote operation.
+             (tramp-check-cached-permissions v ?w)
+           (tramp-sudoedit-send-command
+            v "test" "-w" (tramp-compat-file-name-unquote localname)))
+       ;; If file doesn't exist, check if directory is writable.
+       (and
+        (file-directory-p (file-name-directory filename))
+        (file-writable-p (file-name-directory filename)))))))
 
 (defun tramp-sudoedit-handle-make-directory (dir &optional parents)
   "Like `make-directory' for Tramp files."
@@ -625,41 +643,38 @@ the result will be a local, non-Tramp, file name."
 If TARGET is a non-Tramp file, it is used verbatim as the target
 of the symlink.  If TARGET is a Tramp file, only the localname
 component is used as the target of the symlink."
-  (if (not (tramp-tramp-file-p (expand-file-name linkname)))
-      (tramp-run-real-handler
-       #'make-symbolic-link (list target linkname ok-if-already-exists))
-
-    (with-parsed-tramp-file-name linkname nil
-      ;; If TARGET is a Tramp name, use just the localname component.
-      ;; Don't check for a proper method.
-      (let ((non-essential t))
-       (when (and (tramp-tramp-file-p target)
-                  (tramp-file-name-equal-p v (tramp-dissect-file-name target)))
-         (setq target (tramp-file-local-name (expand-file-name target)))))
-
-      ;; If TARGET is still remote, quote it.
-      (if (tramp-tramp-file-p target)
-         (make-symbolic-link (tramp-compat-file-name-quote target 'top)
-          linkname ok-if-already-exists)
-
-       ;; Do the 'confirm if exists' thing.
-       (when (file-exists-p linkname)
-         ;; What to do?
-         (if (or (null ok-if-already-exists) ; not allowed to exist
-                 (and (numberp ok-if-already-exists)
-                      (not
-                       (yes-or-no-p
-                        (format
-                         "File %s already exists; make it a link anyway?"
-                         localname)))))
-             (tramp-error v 'file-already-exists localname)
-           (delete-file linkname)))
-
-       (tramp-flush-file-properties v localname)
-        (tramp-sudoedit-send-command
-        v "ln" "-sf"
-        (tramp-compat-file-name-unquote target)
-        (tramp-compat-file-name-unquote localname))))))
+  (with-parsed-tramp-file-name (expand-file-name linkname) nil
+    ;; If TARGET is a Tramp name, use just the localname component.
+    ;; Don't check for a proper method.
+    (let ((non-essential t))
+      (when (and (tramp-tramp-file-p target)
+                (tramp-file-name-equal-p v (tramp-dissect-file-name target)))
+       (setq target (tramp-file-local-name (expand-file-name target)))))
+
+    ;; If TARGET is still remote, quote it.
+    (if (tramp-tramp-file-p target)
+       (make-symbolic-link
+        (tramp-compat-file-name-quote target 'top)
+        linkname ok-if-already-exists)
+
+      ;; Do the 'confirm if exists' thing.
+      (when (file-exists-p linkname)
+       ;; What to do?
+       (if (or (null ok-if-already-exists) ; not allowed to exist
+               (and (numberp ok-if-already-exists)
+                    (not
+                     (yes-or-no-p
+                      (format
+                       "File %s already exists; make it a link anyway?"
+                       localname)))))
+           (tramp-error v 'file-already-exists localname)
+         (delete-file linkname)))
+
+      (tramp-flush-file-properties v localname)
+      (tramp-sudoedit-send-command
+       v "ln" "-sf"
+       (tramp-compat-file-name-unquote target)
+       (tramp-compat-file-name-unquote localname)))))
 
 (defun tramp-sudoedit-handle-rename-file
   (filename newname &optional ok-if-already-exists)
@@ -689,7 +704,7 @@ component is used as the target of the symlink."
 
 (defun tramp-sudoedit-handle-set-file-selinux-context (filename context)
   "Like `set-file-selinux-context' for Tramp files."
-  (with-parsed-tramp-file-name filename nil
+  (with-parsed-tramp-file-name (expand-file-name filename) nil
     (when (and (consp context)
               (tramp-sudoedit-remote-selinux-p v))
       (let ((user (and (stringp (nth 0 context)) (nth 0 context)))
@@ -719,18 +734,23 @@ VEC or USER, or if there is no home directory, return 
nil."
 (defun tramp-sudoedit-handle-get-remote-uid (vec id-format)
   "The uid of the remote connection VEC, in ID-FORMAT.
 ID-FORMAT valid values are `string' and `integer'."
-  ;; The result is cached in `tramp-get-remote-uid'.
-  (if (equal id-format 'integer)
-      (tramp-sudoedit-send-command-and-read vec "id" "-u")
-    (tramp-sudoedit-send-command-string vec "id" "-un")))
+  (tramp-sudoedit-send-command vec "id")
+  (tramp-read-id-output vec)
+  (tramp-get-connection-property vec (format "uid-%s" id-format)))
 
 (defun tramp-sudoedit-handle-get-remote-gid (vec id-format)
   "The gid of the remote connection VEC, in ID-FORMAT.
 ID-FORMAT valid values are `string' and `integer'."
-  ;; The result is cached in `tramp-get-remote-gid'.
-  (if (equal id-format 'integer)
-      (tramp-sudoedit-send-command-and-read vec "id" "-g")
-    (tramp-sudoedit-send-command-string vec "id" "-gn")))
+  (tramp-sudoedit-send-command vec "id")
+  (tramp-read-id-output vec)
+  (tramp-get-connection-property vec (format "gid-%s" id-format)))
+
+(defun tramp-sudoedit-handle-get-remote-groups (vec id-format)
+  "Like `tramp-get-remote-groups' for Tramp files.
+ID-FORMAT valid values are `string' and `integer'."
+  (tramp-sudoedit-send-command vec "id")
+  (tramp-read-id-output vec)
+  (tramp-get-connection-property vec (format "groups-%s" id-format)))
 
 (defun tramp-sudoedit-handle-set-file-uid-gid (filename &optional uid gid)
   "Like `tramp-set-file-uid-gid' for Tramp files."
@@ -846,7 +866,7 @@ In case there is no valid Lisp expression, it raises an 
error."
       (condition-case nil
          (prog1 (read (current-buffer))
            ;; Error handling.
-           (when (re-search-forward (rx (not space)) (line-end-position) t)
+           (when (re-search-forward (rx (not blank)) (line-end-position) t)
              (error nil)))
        (error (tramp-error
                vec 'file-error
diff --git a/lisp/net/tramp-uu.el b/lisp/net/tramp-uu.el
index e849c36d13..2bbdb299a6 100644
--- a/lisp/net/tramp-uu.el
+++ b/lisp/net/tramp-uu.el
@@ -25,7 +25,7 @@
 ;;; Commentary:
 
 ;; An implementation of "uuencode" in Lisp.  Uses the function
-;; base64-encode-region which is built-in to modern Emacsen.
+;; base64-encode-region which is built-in to modern Emacs.
 
 ;;; Code:
 
diff --git a/lisp/net/tramp.el b/lisp/net/tramp.el
index bb6eeaa741..03dc47a053 100644
--- a/lisp/net/tramp.el
+++ b/lisp/net/tramp.el
@@ -205,9 +205,9 @@ pair of the form (KEY VALUE).  The following KEYs are 
defined:
     MUST be a Bourne-like shell.  It is normally not necessary to
     set this to any value other than \"/bin/sh\": Tramp wants to
     use a shell which groks tilde expansion, but it can search
-    for it.  Also note that \"/bin/sh\" exists on all Unixen,
-    this might not be true for the value that you decide to use.
-    You Have Been Warned.
+    for it.  Also note that \"/bin/sh\" exists on all Unixen
+    except Andtoid, this might not be true for the value that you
+    decide to use.  You Have Been Warned.
 
   * `tramp-remote-shell-login'
     This specifies the arguments to let `tramp-remote-shell' run
@@ -278,7 +278,8 @@ pair of the form (KEY VALUE).  The following KEYs are 
defined:
 
   * `tramp-direct-async'
     Whether the method supports direct asynchronous processes.
-    Until now, just \"ssh\"-based and \"adb\"-based methods do.
+    Until now, just \"ssh\"-based, \"sshfs\"-based and
+    \"adb\"-based methods do.
 
   * `tramp-copy-program'
     This specifies the name of the program to use for remotely copying
@@ -516,9 +517,10 @@ interpreted as a regular expression which always matches."
 (defcustom tramp-restricted-shell-hosts-alist
   (when (and (eq system-type 'windows-nt)
              (not (string-match-p (rx "sh" eol) tramp-encoding-shell)))
-    (list (rx bos (group (| (literal (downcase tramp-system-name))
-                           (literal (upcase tramp-system-name))))
-             eos)))
+    (list (tramp-compat-rx
+          bos (| (literal (downcase tramp-system-name))
+                 (literal (upcase tramp-system-name)))
+          eos)))
   "List of hosts, which run a restricted shell.
 This is a list of regular expressions, which denote hosts running
 a restricted shell like \"rbash\".  Those hosts can be used as
@@ -529,11 +531,11 @@ host runs a restricted shell, it shall be added to this 
list, too."
 
 ;;;###tramp-autoload
 (defcustom tramp-local-host-regexp
-  (rx bos
-      (regexp (regexp-opt
-              `("localhost" "localhost4" "localhost6"
-                ,tramp-system-name "127.0.0.1" "::1")))
-      eos)
+  (tramp-compat-rx
+   bos
+   (| (literal tramp-system-name)
+      (| "localhost" "localhost4" "localhost6" "127.0.0.1" "::1"))
+   eos)
   "Host names which are regarded as local host.
 If the local host runs a chrooted environment, set this to nil."
   :version "29.1"
@@ -582,7 +584,7 @@ usually suffice.")
 (defconst tramp-echoed-echo-mark-regexp
   (rx-to-string
    `(: ,tramp-echo-mark-marker
-       (= ,tramp-echo-mark-marker-length (group "\b" (? " \b")))))
+       (= ,tramp-echo-mark-marker-length "\b" (? " \b"))))
   "Regexp which matches `tramp-echo-mark' as it gets echoed by \
 the remote shell.")
 
@@ -599,7 +601,7 @@ if you need to change this."
   :type 'string)
 
 (defcustom tramp-login-prompt-regexp
-  (rx (* nonl) (group (| "user" "login")) (? (group " " (* nonl))) ":" (* " "))
+  (rx (* nonl) (| "user" "login") (? blank (* nonl)) ":" (* blank))
   "Regexp matching login-like prompts.
 The regexp should match at end of buffer.
 
@@ -613,9 +615,9 @@ Sometimes the prompt is reported to look like \"login 
as:\"."
   ;; connection initialization; Tramp redefines the prompt afterwards.
   (rx (| bol "\r")
       (* (not (any "\n#$%>]")))
-      (? "#") (any "#$%>]") (* space)
+      (? "#") (any "#$%>]") (* blank)
       ;; Escape characters.
-      (* "[" (* (any ";" digit)) alpha (* space)))
+      (* "[" (* (any ";" digit)) alpha (* blank)))
   "Regexp to match prompts from remote shell.
 Normally, Tramp expects you to configure `shell-prompt-pattern'
 correctly, but sometimes it happens that you are connecting to a
@@ -630,9 +632,10 @@ This regexp must match both `tramp-initial-end-of-output' 
and
   :type 'regexp)
 
 (defcustom tramp-password-prompt-regexp
-  (rx bol (* nonl)
-      (group (regexp (regexp-opt password-word-equivalents)))
-      (* nonl) ":" (? "\^@") (* space))
+  (tramp-compat-rx
+   bol (* nonl)
+   (group (regexp (regexp-opt password-word-equivalents)))
+   (* nonl) ":" (? "\^@") (* blank))
   "Regexp matching password-like prompts.
 The regexp should match at end of buffer.
 
@@ -665,7 +668,7 @@ The regexp should match at end of buffer."
 (defcustom tramp-yesno-prompt-regexp
   (rx "Are you sure you want to continue connecting (yes/no"
       (? "/[fingerprint]") ")?"
-      (* space))
+      (* blank))
   "Regular expression matching all yes/no queries which need to be confirmed.
 The confirmation should be done with yes or no.
 The regexp should match at end of buffer.
@@ -675,7 +678,7 @@ See also `tramp-yn-prompt-regexp'."
 (defcustom tramp-yn-prompt-regexp
   (rx (| "Store key in cache? (y/n)"
         "Update cached key? (y/n, Return cancels connection)")
-      (* space))
+      (* blank))
   "Regular expression matching all y/n queries which need to be confirmed.
 The confirmation should be done with y or n.
 The regexp should match at end of buffer.
@@ -692,10 +695,9 @@ files conditionalize this setup based on the TERM 
environment variable."
   :type 'string)
 
 (defcustom tramp-terminal-prompt-regexp
-  (rx (group
-       (| (: "TERM = (" (* nonl) ")")
-         (: "Terminal type? [" (* nonl) "]")))
-      (* space))
+  (rx (| (: "TERM = (" (* nonl) ")")
+        (: "Terminal type? [" (* nonl) "]"))
+      (* blank))
   "Regular expression matching all terminal setting prompts.
 The regexp should match at end of buffer.
 The answer will be provided by `tramp-action-terminal', which see."
@@ -706,7 +708,7 @@ The answer will be provided by `tramp-action-terminal', 
which see."
 ;; "-no-antispoof".  However, since we don't know which PuTTY
 ;; version is installed, we must react interactively.
 (defcustom tramp-antispoof-regexp
-  (rx (literal "Access granted. Press Return to begin session. "))
+  (rx "Access granted. Press Return to begin session. ")
   "Regular expression matching plink's anti-spoofing message.
 The regexp should match at end of buffer."
   :version "27.1"
@@ -738,7 +740,7 @@ The regexp should match at end of buffer."
   :type 'regexp)
 
 (defcustom tramp-operation-not-permitted-regexp
-  (rx (| (: "preserving times" (* nonl)) "set mode") ":" (* space)
+  (rx (| (: "preserving times" (* nonl)) "set mode") ":" (* blank)
       "Operation not permitted")
   "Regular expression matching keep-date problems in (s)cp operations.
 Copying has been performed successfully already, so this message can
@@ -751,7 +753,7 @@ be ignored safely."
         "Permission denied"
         "is a directory"
         "not a regular file")
-      (* space))
+      (* blank))
   "Regular expression matching copy problems in (s)cp operations."
   :type 'regexp)
 
@@ -889,17 +891,18 @@ Used in `tramp-make-tramp-file-name'.")
 
 (defun tramp-build-prefix-regexp ()
   "Return `tramp-prefix-regexp'."
-  (rx bol (literal (tramp-build-prefix-format))))
+  (tramp-compat-rx bol (literal (tramp-build-prefix-format))))
 
 (defvar tramp-prefix-regexp nil ; Initialized when defining `tramp-syntax'!
   "Regexp matching the very beginning of Tramp file names.
 Should always start with \"^\".  Derived from `tramp-prefix-format'.")
 
 (defconst tramp-method-regexp-alist
-  `((default . ,(rx (| (literal tramp-default-method-marker) (>= 2 alnum))))
+  `((default . ,(tramp-compat-rx
+                (| (literal tramp-default-method-marker) (>= 2 alnum))))
     (simplified . "")
-    (separate
-     . ,(rx (? (| (literal tramp-default-method-marker) (>= 2 alnum))))))
+    (separate . ,(tramp-compat-rx
+                 (? (| (literal tramp-default-method-marker) (>= 2 alnum))))))
   "Alist mapping Tramp syntax to regexps matching methods identifiers.")
 
 (defun tramp-build-method-regexp ()
@@ -927,19 +930,20 @@ Used in `tramp-make-tramp-file-name'.")
 
 (defun tramp-build-postfix-method-regexp ()
   "Return `tramp-postfix-method-regexp'."
-  (rx (literal (tramp-build-postfix-method-format))))
+  (tramp-compat-rx (literal (tramp-build-postfix-method-format))))
 
 (defvar tramp-postfix-method-regexp nil ; Init'd when defining `tramp-syntax'!
   "Regexp matching delimiter between method and user or host names.
 Derived from `tramp-postfix-method-format'.")
 
-(defconst tramp-user-regexp (rx (+ (not (any "/:|" space))))
+(defconst tramp-user-regexp (rx (+ (not (any "/:|" blank))))
   "Regexp matching user names.")
 
 (defconst tramp-prefix-domain-format "%"
   "String matching delimiter between user and domain names.")
 
-(defconst tramp-prefix-domain-regexp (rx (literal tramp-prefix-domain-format))
+(defconst tramp-prefix-domain-regexp
+  (tramp-compat-rx (literal tramp-prefix-domain-format))
   "Regexp matching delimiter between user and domain names.
 Derived from `tramp-prefix-domain-format'.")
 
@@ -947,16 +951,18 @@ Derived from `tramp-prefix-domain-format'.")
   "Regexp matching domain names.")
 
 (defconst tramp-user-with-domain-regexp
-  (rx (group (regexp tramp-user-regexp))
-             (regexp tramp-prefix-domain-regexp)
-      (group (regexp tramp-domain-regexp)))
+  (tramp-compat-rx
+   (group (regexp tramp-user-regexp))
+   (regexp tramp-prefix-domain-regexp)
+   (group (regexp tramp-domain-regexp)))
   "Regexp matching user names with domain names.")
 
 (defconst tramp-postfix-user-format "@"
   "String matching delimiter between user and host names.
 Used in `tramp-make-tramp-file-name'.")
 
-(defconst tramp-postfix-user-regexp (rx (literal tramp-postfix-user-format))
+(defconst tramp-postfix-user-regexp
+  (tramp-compat-rx (literal tramp-postfix-user-format))
   "Regexp matching delimiter between user and host names.
 Derived from `tramp-postfix-user-format'.")
 
@@ -979,7 +985,7 @@ Used in `tramp-make-tramp-file-name'.")
 
 (defun tramp-build-prefix-ipv6-regexp ()
   "Return `tramp-prefix-ipv6-regexp'."
-  (rx (literal tramp-prefix-ipv6-format)))
+  (tramp-compat-rx (literal tramp-prefix-ipv6-format)))
 
 (defvar tramp-prefix-ipv6-regexp nil ; Initialized when defining 
`tramp-syntax'!
   "Regexp matching left hand side of IPv6 addresses.
@@ -1007,7 +1013,7 @@ Used in `tramp-make-tramp-file-name'.")
 
 (defun tramp-build-postfix-ipv6-regexp ()
   "Return `tramp-postfix-ipv6-regexp'."
-  (rx (literal tramp-postfix-ipv6-format)))
+  (tramp-compat-rx (literal tramp-postfix-ipv6-format)))
 
 (defvar tramp-postfix-ipv6-regexp nil ; Initialized when defining 
`tramp-syntax'!
   "Regexp matching right hand side of IPv6 addresses.
@@ -1016,7 +1022,8 @@ Derived from `tramp-postfix-ipv6-format'.")
 (defconst tramp-prefix-port-format "#"
   "String matching delimiter between host names and port numbers.")
 
-(defconst tramp-prefix-port-regexp (rx (literal tramp-prefix-port-format))
+(defconst tramp-prefix-port-regexp
+  (tramp-compat-rx (literal tramp-prefix-port-format))
   "Regexp matching delimiter between host names and port numbers.
 Derived from `tramp-prefix-port-format'.")
 
@@ -1024,15 +1031,17 @@ Derived from `tramp-prefix-port-format'.")
   "Regexp matching port numbers.")
 
 (defconst tramp-host-with-port-regexp
-  (rx (group (regexp tramp-host-regexp))
-             (regexp tramp-prefix-port-regexp)
-      (group (regexp tramp-port-regexp)))
+  (tramp-compat-rx
+   (group (regexp tramp-host-regexp))
+   (regexp tramp-prefix-port-regexp)
+   (group (regexp tramp-port-regexp)))
   "Regexp matching host names with port numbers.")
 
 (defconst tramp-postfix-hop-format "|"
   "String matching delimiter after ad-hoc hop definitions.")
 
-(defconst tramp-postfix-hop-regexp (rx (literal tramp-postfix-hop-format))
+(defconst tramp-postfix-hop-regexp
+  (tramp-compat-rx (literal tramp-postfix-hop-format))
   "Regexp matching delimiter after ad-hoc hop definitions.
 Derived from `tramp-postfix-hop-format'.")
 
@@ -1052,7 +1061,7 @@ Used in `tramp-make-tramp-file-name'.")
 
 (defun tramp-build-postfix-host-regexp ()
   "Return `tramp-postfix-host-regexp'."
-  (rx (literal tramp-postfix-host-format)))
+  (tramp-compat-rx (literal tramp-postfix-host-format)))
 
 (defvar tramp-postfix-host-regexp nil ; Initialized when defining 
`tramp-syntax'!
   "Regexp matching delimiter between host names and localnames.
@@ -1079,17 +1088,34 @@ Derived from `tramp-postfix-host-format'.")
 (defun tramp-build-remote-file-name-spec-regexp ()
   "Construct a regexp matching a Tramp file name for a Tramp syntax.
 It is expected, that `tramp-syntax' has the proper value."
-  (rx ;; Method.
-      (group (regexp tramp-method-regexp)) (regexp tramp-postfix-method-regexp)
-      ;; Optional user.
-      (? (group (regexp tramp-user-regexp)) (regexp tramp-postfix-user-regexp))
-      ;; Optional host.
-      (? (group (| (regexp tramp-host-regexp)
-                   (: (regexp tramp-prefix-ipv6-regexp)
-                     (? (regexp tramp-ipv6-regexp))
-                     (regexp tramp-postfix-ipv6-regexp)))
-      ;; Optional port.
-      (? (regexp tramp-prefix-port-regexp) (regexp tramp-port-regexp))))))
+  ;; Starting with Emacs 27, we can use `rx-let'.
+  (let* ((user-regexp
+         (tramp-compat-rx
+          (group-n 6 (regexp tramp-user-regexp))
+          (regexp tramp-postfix-user-regexp)))
+        (host-regexp
+         (tramp-compat-rx
+          (group-n 7 (| (regexp tramp-host-regexp)
+                        (: (regexp tramp-prefix-ipv6-regexp)
+                           (? (regexp tramp-ipv6-regexp))
+                           (regexp tramp-postfix-ipv6-regexp)))
+                   ;; Optional port.
+                   (? (regexp tramp-prefix-port-regexp)
+                      (regexp tramp-port-regexp)))))
+        (user-host-regexp
+         (if (eq tramp-syntax 'simplified)
+             ;; There must be either user or host.
+             (tramp-compat-rx
+              (| (: (regexp user-regexp) (? (regexp host-regexp)))
+                 (: (? (regexp user-regexp)) (regexp host-regexp))))
+           (tramp-compat-rx
+            (? (regexp user-regexp)) (? (regexp host-regexp))))))
+    (tramp-compat-rx
+     ;; Method.
+     (group-n 5 (regexp tramp-method-regexp))
+     (regexp tramp-postfix-method-regexp)
+     ;; User and host.
+     (regexp user-host-regexp))))
 
 (defvar tramp-remote-file-name-spec-regexp
   nil ; Initialized when defining `tramp-syntax'!
@@ -1100,12 +1126,13 @@ It is expected, that `tramp-syntax' has the proper 
value."
 It is expected, that `tramp-syntax' has the proper value.
 See `tramp-file-name-structure'."
   (list
-   (rx (regexp tramp-prefix-regexp)
-       (? (group (+ (regexp tramp-remote-file-name-spec-regexp)
-                   (regexp tramp-postfix-hop-regexp))))
-       (regexp tramp-remote-file-name-spec-regexp)
-       (regexp tramp-postfix-host-regexp)
-       (group (regexp tramp-localname-regexp)))
+   (tramp-compat-rx
+    (regexp tramp-prefix-regexp)
+    (? (group (+ (regexp tramp-remote-file-name-spec-regexp)
+                (regexp tramp-postfix-hop-regexp))))
+    (regexp tramp-remote-file-name-spec-regexp)
+    (regexp tramp-postfix-host-regexp)
+    (group (regexp tramp-localname-regexp)))
    5 6 7 8 1))
 
 (defvar tramp-file-name-structure nil ; Initialized when defining 
`tramp-syntax'!
@@ -1159,9 +1186,11 @@ initial value is overwritten by the car of 
`tramp-file-name-structure'.")
 ;; `tramp-method-regexp' needs at least two characters, in order to
 ;; distinguish from volume letter.  This is in the way when completing.
 (defconst tramp-completion-method-regexp-alist
-  `((default    . ,(rx (| (literal tramp-default-method-marker) (+ alnum))))
+  `((default    . ,(tramp-compat-rx
+                   (| (literal tramp-default-method-marker) (+ alnum))))
     (simplified . "")
-    (separate   . ,(rx (| (literal tramp-default-method-marker) (* alnum)))))
+    (separate   . ,(tramp-compat-rx
+                   (| (literal tramp-default-method-marker) (* alnum)))))
   "Alist mapping Tramp syntax to regexps matching completion methods.")
 
 (defun tramp-build-completion-method-regexp ()
@@ -1177,27 +1206,28 @@ The `ftp' syntax does not support methods.")
   "Return `tramp-completion-file-name-regexp' according to `tramp-syntax'."
   (if (eq tramp-syntax 'separate)
       ;; FIXME: This shouldn't be necessary.
-      (rx bos "/" (? (group "[" (* (not (any "]"))))) eos)
-  (rx bos
-      ;; `file-name-completion' uses absolute paths for matching.
-      ;; This means that on W32 systems, something like
-      ;; "/ssh:host:~/path" becomes "c:/ssh:host:~/path".  See also
-      ;; `tramp-drop-volume-letter'.
-      (? (regexp tramp-volume-letter-regexp))
-      (regexp tramp-prefix-regexp)
-
-      ;; Optional multi hops.
-      (* (regexp tramp-remote-file-name-spec-regexp)
-         (regexp tramp-postfix-hop-regexp))
-
-      ;; Last hop.
-      (? (regexp tramp-completion-method-regexp)
-        ;; Method separator, user name and host name.
-        (? (regexp tramp-postfix-method-regexp)
-           ;; This is a little bit lax, but it serves.
-           (? (regexp tramp-host-regexp))))
-
-      eos)))
+      (tramp-compat-rx bos "/" (? "[" (* (not "]"))) eos)
+    (tramp-compat-rx
+     bos
+     ;; `file-name-completion' uses absolute paths for matching.
+     ;; This means that on W32 systems, something like
+     ;; "/ssh:host:~/path" becomes "c:/ssh:host:~/path".  See also
+     ;; `tramp-drop-volume-letter'.
+     (? (regexp tramp-volume-letter-regexp))
+     (regexp tramp-prefix-regexp)
+
+     ;; Optional multi hops.
+     (* (regexp tramp-remote-file-name-spec-regexp)
+        (regexp tramp-postfix-hop-regexp))
+
+     ;; Last hop.
+     (? (regexp tramp-completion-method-regexp)
+       ;; Method separator, user name and host name.
+       (? (regexp tramp-postfix-method-regexp)
+          ;; This is a little bit lax, but it serves.
+          (? (regexp tramp-host-regexp))))
+
+     eos)))
 
 (defvar tramp-completion-file-name-regexp
    nil ; Initialized when defining `tramp-syntax'!
@@ -1433,9 +1463,14 @@ calling HANDLER.")
 ;; internal data structure.  Convenience functions for internal
 ;; data structure.
 
-;; The basic structure for remote file names.  We must autoload it in
-;; tramp-loaddefs.el, because some functions, which need it, wouldn't
-;; work otherwise when unloading / reloading Tramp.  (Bug#50869)
+;; The basic structure for remote file names.
+
+;; Note: We started autoloading it in tramp-loaddefs.el, because some
+;; functions, which needed it, wouldn't work otherwise when unloading
+;; / reloading Tramp (Bug#50869).
+;; This bug is fixed in Emacs 29, but other parts of Tramp have grown
+;; dependencies on having this in tramp-loaddefs.el in the mean time,
+;; so .... here we are.
 ;;;###tramp-autoload(require 'cl-lib)
 ;;;###tramp-autoload
 (progn
@@ -1486,21 +1521,21 @@ If nil, return `tramp-default-port'."
 ;;;###tramp-autoload
 (defun tramp-file-name-unify (vec &optional localname)
   "Unify VEC by removing localname and hop from `tramp-file-name' structure.
-If LOCALNAME is a string, set it as localname.
+If LOCALNAME is an absolute file name, set it as localname.  If
+LOCALNAME is a relative file name, return `tramp-cache-undefined'.
 Objects returned by this function compare `equal' if they refer to the
 same connection.  Make a copy in order to avoid side effects."
-  (when (tramp-file-name-p vec)
-    (setq vec (copy-tramp-file-name vec))
-    (setf (tramp-file-name-localname vec)
-         (and (stringp localname)
-              ;; FIXME: This is a sanity check.  When this error
-              ;; doesn't happen for a while, it can be removed.
-              (or (file-name-absolute-p localname)
-                  (tramp-error
-                   vec 'file-error "File `%s' must be absolute" localname))
-              (tramp-compat-file-name-unquote (directory-file-name localname)))
-         (tramp-file-name-hop vec) nil))
-  vec)
+  (if (and (stringp localname)
+          (not (file-name-absolute-p localname)))
+      (setq vec tramp-cache-undefined)
+    (when (tramp-file-name-p vec)
+      (setq vec (copy-tramp-file-name vec))
+      (setf (tramp-file-name-localname vec)
+           (and (stringp localname)
+                (tramp-compat-file-name-unquote
+                 (directory-file-name localname)))
+           (tramp-file-name-hop vec) nil))
+    vec))
 
 (put #'tramp-file-name-unify 'tramp-suppress-trace t)
 
@@ -1730,7 +1765,7 @@ See `tramp-dissect-file-name' for details."
   (let ((v (tramp-dissect-file-name
            (concat tramp-prefix-format
                    (replace-regexp-in-string
-                    (rx (regexp tramp-postfix-hop-regexp) eos)
+                    (tramp-compat-rx (regexp tramp-postfix-hop-regexp) eos)
                     tramp-postfix-host-format name))
            nodefault)))
     ;; Only some methods from tramp-sh.el do support multi-hops.
@@ -1826,7 +1861,8 @@ the form (METHOD USER DOMAIN HOST PORT LOCALNAME 
&optional HOP)."
    (replace-regexp-in-string
     tramp-prefix-regexp ""
     (replace-regexp-in-string
-     (rx (regexp tramp-postfix-host-regexp) eos) tramp-postfix-hop-format
+     (tramp-compat-rx
+      (regexp tramp-postfix-host-regexp) eos) tramp-postfix-hop-format
      (tramp-make-tramp-file-name vec 'noloc)))))
 
 (defun tramp-completion-make-tramp-file-name (method user host localname)
@@ -1942,11 +1978,11 @@ of `current-buffer'."
 
 (defconst tramp-debug-outline-regexp
   (rx ;; Timestamp.
-      (+ digit) ":" (+ digit) ":" (+ digit) "." (+ digit) " "
+      (+ digit) ":" (+ digit) ":" (+ digit) "." (+ digit) blank
       ;; Thread.
-      (? (group "#<thread " (+ nonl) ">") " ")
+      (? (group "#<thread " (+ nonl) ">") blank)
        ;; Function name, verbosity.
-      (+ (any "-" alnum)) " (" (group (group (+ digit))) ") #")
+      (+ (any "-" alnum)) " (" (group (+ digit)) ") #")
   "Used for highlighting Tramp debug buffers in `outline-mode'.")
 
 (defconst tramp-debug-font-lock-keywords
@@ -1955,7 +1991,7 @@ of `current-buffer'."
   ;; Also, in `font-lock-defaults' you can specify a function name for
   ;; the "KEYWORDS" part, so font-lock calls it to get the actual keywords!
   '(list
-    (rx bol (regexp tramp-debug-outline-regexp) (+ nonl))
+    (tramp-compat-rx bol (regexp tramp-debug-outline-regexp) (+ nonl))
     '(1 font-lock-warning-face t t)
     '(0 (outline-font-lock-face) keep t))
   "Used for highlighting Tramp debug buffers in `outline-mode'.")
@@ -2410,9 +2446,9 @@ letter into the file name.  This function removes it."
     (let ((quoted (tramp-compat-file-name-quoted-p name 'top))
          (result (tramp-compat-file-name-unquote name 'top)))
       (setq result
-           (if (string-match
-                (rx (regexp tramp-volume-letter-regexp) "/") result)
-               (replace-match "/" nil t result) result))
+           (replace-regexp-in-string
+            (tramp-compat-rx (regexp tramp-volume-letter-regexp) "/")
+            "/" result))
       (if quoted (tramp-compat-file-name-quote result 'top) result))))
 
 ;;; Config Manipulation Functions:
@@ -2521,7 +2557,7 @@ coding system might not be determined.  This function 
repairs it."
        ;; We found a matching entry in `file-coding-system-alist'.
        ;; So we add a similar entry, but with the temporary file name
        ;; as regexp.
-       (push (cons (rx (literal tmpname)) (cdr elt)) result)))))
+       (push (cons (tramp-compat-rx (literal tmpname)) (cdr elt)) result)))))
 
 (defun tramp-run-real-handler (operation args)
   "Invoke normal file name handler for OPERATION.
@@ -2633,8 +2669,8 @@ Must be handled by the callers."
       (tramp-get-default-directory (process-buffer (nth 0 args)))))
    ;; VEC.
    ((member operation
-           '(tramp-get-home-directory
-             tramp-get-remote-gid tramp-get-remote-uid))
+           '(tramp-get-home-directory tramp-get-remote-gid
+             tramp-get-remote-groups tramp-get-remote-uid))
     (tramp-make-tramp-file-name (nth 0 args)))
    ;; Unknown file primitive.
    (t (error "Unknown file I/O primitive: %s" operation))))
@@ -2804,7 +2840,7 @@ remote file names."
                  #'file-name-sans-extension
                  (directory-files
                   dir nil (rx bos "tramp" (+ nonl) ".el" (? "c") eos)))))
-        (files-regexp (rx bol (: (regexp (regexp-opt files))) eol)))
+        (files-regexp (tramp-compat-rx bol (regexp (regexp-opt files)) eol)))
     (mapatoms
      (lambda (atom)
        (when (and (functionp atom)
@@ -2841,6 +2877,7 @@ remote file names."
   (put #'tramp-completion-file-name-handler 'operations
        (mapcar #'car tramp-completion-file-name-handler-alist))
 
+  ;; Integrated in Emacs 27.
   (when (bound-and-true-p tramp-archive-enabled)
     (add-to-list 'file-name-handler-alist
                 (cons tramp-archive-file-name-regexp
@@ -2949,9 +2986,10 @@ not in completion mode."
 
     ;; Suppress hop from completion.
     (when (string-match
-          (rx (regexp tramp-prefix-regexp)
-              (group (+ (regexp tramp-remote-file-name-spec-regexp)
-                        (regexp tramp-postfix-hop-regexp))))
+          (tramp-compat-rx
+           (regexp tramp-prefix-regexp)
+           (group (+ (regexp tramp-remote-file-name-spec-regexp)
+                     (regexp tramp-postfix-hop-regexp))))
           fullname)
       (setq hop (match-string 1 fullname)
            fullname (replace-match "" nil nil fullname 1)))
@@ -3038,58 +3076,64 @@ not in completion mode."
 (defun tramp-completion-dissect-file-name (name)
   "Return a list of `tramp-file-name' structures for NAME.
 They are collected by `tramp-completion-dissect-file-name1'."
-  (let* (;; "/method" "/[method"
-        (tramp-completion-file-name-structure1
-         (list
-          (rx (regexp tramp-prefix-regexp)
-              (group (? (regexp tramp-completion-method-regexp))) eol)
-          1 nil nil nil))
-        ;; "/method:user" "/[method/user"
-        (tramp-completion-file-name-structure2
-         (list
-          (rx (regexp tramp-prefix-regexp)
-              (group (regexp tramp-method-regexp))
-              (regexp tramp-postfix-method-regexp)
-              (group (? (regexp tramp-user-regexp))) eol)
-          1 2 nil nil))
-        ;; "/method:host" "/[method/host"
-        (tramp-completion-file-name-structure3
-         (list
-          (rx (regexp tramp-prefix-regexp)
-              (group (regexp tramp-method-regexp))
-              (regexp tramp-postfix-method-regexp)
-              (group (? (regexp tramp-host-regexp))) eol)
-          1 nil 2 nil))
-        ;; "/method:[ipv6" "/[method/ipv6"
-        (tramp-completion-file-name-structure4
-         (list
-          (rx (regexp tramp-prefix-regexp)
-              (group (regexp tramp-method-regexp))
-              (regexp tramp-postfix-method-regexp)
-              (regexp tramp-prefix-ipv6-regexp)
-              (group (? (regexp tramp-ipv6-regexp))) eol)
-          1 nil 2 nil))
-        ;; "/method:user@host" "/[method/user@host"
-        (tramp-completion-file-name-structure5
-         (list
-          (rx (regexp tramp-prefix-regexp)
-              (group (regexp tramp-method-regexp))
-              (regexp tramp-postfix-method-regexp)
-              (group (regexp tramp-user-regexp))
-              (regexp tramp-postfix-user-regexp)
-              (group (? (regexp tramp-host-regexp))) eol)
-          1 2 3 nil))
-        ;; "/method:user@[ipv6" "/[method/user@ipv6"
-        (tramp-completion-file-name-structure6
-         (list
-          (rx (regexp tramp-prefix-regexp)
-              (group (regexp tramp-method-regexp))
-              (regexp tramp-postfix-method-regexp)
-              (group (regexp tramp-user-regexp))
-              (regexp tramp-postfix-user-regexp)
-              (regexp tramp-prefix-ipv6-regexp)
-              (group (? (regexp tramp-ipv6-regexp))) eol)
-          1 2 3 nil)))
+  (let (;; "/method" "/[method"
+       (tramp-completion-file-name-structure1
+        (list
+         (tramp-compat-rx
+          (regexp tramp-prefix-regexp)
+          (group (? (regexp tramp-completion-method-regexp))) eol)
+         1 nil nil nil))
+       ;; "/method:user" "/[method/user"
+       (tramp-completion-file-name-structure2
+        (list
+         (tramp-compat-rx
+          (regexp tramp-prefix-regexp)
+          (group (regexp tramp-method-regexp))
+          (regexp tramp-postfix-method-regexp)
+          (group (? (regexp tramp-user-regexp))) eol)
+         1 2 nil nil))
+       ;; "/method:host" "/[method/host"
+       (tramp-completion-file-name-structure3
+        (list
+         (tramp-compat-rx
+          (regexp tramp-prefix-regexp)
+          (group (regexp tramp-method-regexp))
+          (regexp tramp-postfix-method-regexp)
+          (group (? (regexp tramp-host-regexp))) eol)
+         1 nil 2 nil))
+       ;; "/method:[ipv6" "/[method/ipv6"
+       (tramp-completion-file-name-structure4
+        (list
+         (tramp-compat-rx
+          (regexp tramp-prefix-regexp)
+          (group (regexp tramp-method-regexp))
+          (regexp tramp-postfix-method-regexp)
+          (regexp tramp-prefix-ipv6-regexp)
+          (group (? (regexp tramp-ipv6-regexp))) eol)
+         1 nil 2 nil))
+       ;; "/method:user@host" "/[method/user@host"
+       (tramp-completion-file-name-structure5
+        (list
+         (tramp-compat-rx
+          (regexp tramp-prefix-regexp)
+          (group (regexp tramp-method-regexp))
+          (regexp tramp-postfix-method-regexp)
+          (group (regexp tramp-user-regexp))
+          (regexp tramp-postfix-user-regexp)
+          (group (? (regexp tramp-host-regexp))) eol)
+         1 2 3 nil))
+       ;; "/method:user@[ipv6" "/[method/user@ipv6"
+       (tramp-completion-file-name-structure6
+        (list
+         (tramp-compat-rx
+          (regexp tramp-prefix-regexp)
+          (group (regexp tramp-method-regexp))
+          (regexp tramp-postfix-method-regexp)
+          (group (regexp tramp-user-regexp))
+          (regexp tramp-postfix-user-regexp)
+          (regexp tramp-prefix-ipv6-regexp)
+          (group (? (regexp tramp-ipv6-regexp))) eol)
+         1 2 3 nil)))
     (delq
      nil
      (mapcar
@@ -3214,8 +3258,9 @@ Either user or host may be nil."
 Either user or host may be nil."
    (let (result
         (regexp
-         (rx bol (group (regexp tramp-host-regexp))
-             (? (+ space) (group (regexp tramp-user-regexp))))))
+         (tramp-compat-rx
+          bol (group (regexp tramp-host-regexp))
+          (? (+ blank) (group (regexp tramp-user-regexp))))))
      (when (re-search-forward regexp (line-end-position) t)
        (setq result (append (list (match-string 2) (match-string 1)))))
      (forward-line 1)
@@ -3229,7 +3274,8 @@ User is always nil."
 (defun tramp-parse-shosts-group ()
    "Return a (user host) tuple allowed to access.
 User is always nil."
-   (tramp-parse-group (rx bol (group (regexp tramp-host-regexp))) 1 ","))
+   (tramp-parse-group
+    (tramp-compat-rx bol (group (regexp tramp-host-regexp))) 1 ","))
 
 (defun tramp-parse-sconfig (filename)
   "Return a list of (user host) tuples allowed to access.
@@ -3240,10 +3286,11 @@ User is always nil."
    "Return a (user host) tuple allowed to access.
 User is always nil."
    (tramp-parse-group
-    (rx (| (: bol (* space) "Host")
-          (: bol (+ nonl)) ;; ???
-          (group (regexp tramp-host-regexp))))
-    1 (rx space)))
+    (tramp-compat-rx
+     (| (: bol (* blank) "Host")
+       (: bol (+ nonl)) ;; ???
+       (group (regexp tramp-host-regexp))))
+    1 (rx blank)))
 
 ;; Generic function.
 (defun tramp-parse-shostkeys-sknownhosts (dirname regexp)
@@ -3264,15 +3311,16 @@ User is always nil."
 User is always nil."
   (tramp-parse-shostkeys-sknownhosts
    dirname
-   (rx bol "key_" (+ digit) "_" (group (regexp tramp-host-regexp)) ".pub" 
eol)))
+   (tramp-compat-rx
+    bol "key_" (+ digit) "_" (group (regexp tramp-host-regexp)) ".pub" eol)))
 
 (defun tramp-parse-sknownhosts (dirname)
   "Return a list of (user host) tuples allowed to access.
 User is always nil."
   (tramp-parse-shostkeys-sknownhosts
    dirname
-   (rx bol (group (regexp tramp-host-regexp))
-       ".ssh-" (| "dss" "rsa") ".pub" eol)))
+   (tramp-compat-rx
+    bol (group (regexp tramp-host-regexp)) ".ssh-" (| "dss" "rsa") ".pub" 
eol)))
 
 (defun tramp-parse-hosts (filename)
   "Return a list of (user host) tuples allowed to access.
@@ -3283,8 +3331,9 @@ User is always nil."
    "Return a (user host) tuple allowed to access.
 User is always nil."
    (tramp-parse-group
-    (rx bol (group (| (regexp tramp-ipv6-regexp) (regexp tramp-host-regexp))))
-    1 (rx space)))
+    (tramp-compat-rx
+     bol (group (| (regexp tramp-ipv6-regexp) (regexp tramp-host-regexp))))
+    1 (rx blank)))
 
 (defun tramp-parse-passwd (filename)
   "Return a list of (user host) tuples allowed to access.
@@ -3302,7 +3351,7 @@ Host is always \"localhost\"."
    "Return a (user host) tuple allowed to access.
 Host is always \"localhost\"."
    (let (result
-        (regexp (rx bol (group (regexp tramp-user-regexp)) ":")))
+        (regexp (tramp-compat-rx bol (group (regexp tramp-user-regexp)) ":")))
      (when (re-search-forward regexp (line-end-position) t)
        (setq result (list (match-string 1) "localhost")))
      (forward-line 1)
@@ -3353,17 +3402,18 @@ User is always nil."
                     (tramp-parse-putty-group registry-or-dirname)))))
     ;; UNIX case.
     (tramp-parse-shostkeys-sknownhosts
-     registry-or-dirname (rx bol (group (regexp tramp-host-regexp)) eol))))
+     registry-or-dirname
+     (tramp-compat-rx bol (group (regexp tramp-host-regexp)) eol))))
 
 (defun tramp-parse-putty-group (registry)
-   "Return a (user host) tuple allowed to access.
+  "Return a (user host) tuple allowed to access.
 User is always nil."
-   (let (result
-        (regexp (rx (literal registry) "\\" (group (+ nonl)))))
-     (when (re-search-forward regexp (line-end-position) t)
-       (setq result (list nil (match-string 1))))
-     (forward-line 1)
-     result))
+  (let (result
+       (regexp (tramp-compat-rx (literal registry) "\\" (group (+ nonl)))))
+    (when (re-search-forward regexp (line-end-position) t)
+      (setq result (list nil (match-string 1))))
+    (forward-line 1)
+    result))
 
 ;;; Skeleton macros for file name handler functions.
 
@@ -3402,7 +3452,7 @@ BODY is the backend specific code."
 BODY is the backend specific code."
   (declare (indent 5) (debug t))
   `(or
-    (with-parsed-tramp-file-name ,directory nil
+    (with-parsed-tramp-file-name (expand-file-name ,directory) nil
       (tramp-barf-if-file-missing v ,directory
        (when (file-directory-p ,directory)
          (setq ,directory
@@ -3433,7 +3483,7 @@ BODY is the backend specific code."
 BODY is the backend specific code."
   (declare (indent 6) (debug t))
   `(or
-    (with-parsed-tramp-file-name ,directory nil
+    (with-parsed-tramp-file-name (expand-file-name ,directory) nil
       (tramp-barf-if-file-missing v ,directory
        (when (file-directory-p ,directory)
          (let ((temp
@@ -3495,7 +3545,7 @@ BODY is the backend specific code."
   "Skeleton for `tramp-*-set-file-{modes,times,uid-gid}'.
 BODY is the backend specific code."
   (declare (indent 1) (debug t))
-  `(with-parsed-tramp-file-name ,filename nil
+  `(with-parsed-tramp-file-name (expand-file-name ,filename) nil
      (when (not (file-exists-p ,filename))
        (tramp-error v 'file-missing ,filename))
      (with-tramp-saved-file-properties
@@ -3626,7 +3676,7 @@ Let-bind it when necessary.")
 
 ;; `directory-abbrev-apply' and `directory-abbrev-make-regexp' exists
 ;; since Emacs 29.1.  Since this handler isn't called for older
-;; Emacsen, it is save to invoke them via `tramp-compat-funcall'.
+;; Emacs, it is save to invoke them via `tramp-compat-funcall'.
 (defun tramp-handle-abbreviate-file-name (filename)
   "Like `abbreviate-file-name' for Tramp files."
   (let* ((case-fold-search (file-name-case-insensitive-p filename))
@@ -3676,7 +3726,7 @@ Let-bind it when necessary.")
   (filename newname &optional ok-if-already-exists)
   "Like `add-name-to-file' for Tramp files."
   (with-parsed-tramp-file-name
-      (if (tramp-tramp-file-p newname) newname filename) nil
+      (expand-file-name (if (tramp-tramp-file-p newname) newname filename)) nil
     (unless (tramp-equal-remote filename newname)
       (tramp-error
        v 'file-error
@@ -3760,7 +3810,7 @@ Let-bind it when necessary.")
       ;; not support tilde expansion.  But users could declare a
       ;; respective connection property.  (Bug#53847)
       (when (string-match
-            (rx bos "~" (group (* (not (any "/")))) (group (* nonl)) eos)
+            (tramp-compat-rx bos "~" (group (* (not "/"))) (group (* nonl)) 
eos)
             localname)
        (let ((uname (match-string 1 localname))
              (fname (match-string 2 localname))
@@ -3813,7 +3863,7 @@ Let-bind it when necessary.")
   ;; We don't want to run it when `non-essential' is t, or there is
   ;; no connection process yet.
   (when (tramp-connectable-p filename)
-    (with-parsed-tramp-file-name filename nil
+    (with-parsed-tramp-file-name (expand-file-name filename) nil
       (with-tramp-file-property v localname "file-exists-p"
        (not (null (file-attributes filename)))))))
 
@@ -3872,9 +3922,7 @@ Let-bind it when necessary.")
              (with-tramp-progress-reporter v 5 "Checking case-insensitive"
                ;; The idea is to compare a file with lower case
                ;; letters with the same file with upper case letters.
-               (let ((candidate
-                      (tramp-compat-file-name-unquote
-                       (directory-file-name filename)))
+               (let ((candidate (directory-file-name filename))
                      case-fold-search
                      tmpfile)
                  ;; Check, whether we find an existing file with
@@ -3924,7 +3972,8 @@ Let-bind it when necessary.")
           (and
            completion-ignored-extensions
            (string-match-p
-            (rx (regexp (regexp-opt completion-ignored-extensions)) eos)
+            (tramp-compat-rx
+             (regexp (regexp-opt completion-ignored-extensions)) eos)
             x)
            ;; We remember the hit.
            (push x hits-ignored-extensions))))))
@@ -3960,7 +4009,7 @@ Let-bind it when necessary.")
 
 (defun tramp-handle-file-readable-p (filename)
   "Like `file-readable-p' for Tramp files."
-  (with-parsed-tramp-file-name filename nil
+  (with-parsed-tramp-file-name (expand-file-name filename) nil
     (with-tramp-file-property v localname "file-readable-p"
       (or (tramp-check-cached-permissions v ?r)
          ;; `tramp-check-cached-permissions' doesn't handle symbolic
@@ -4059,7 +4108,7 @@ Let-bind it when necessary.")
 
 (defun tramp-handle-file-writable-p (filename)
   "Like `file-writable-p' for Tramp files."
-  (with-parsed-tramp-file-name filename nil
+  (with-parsed-tramp-file-name (expand-file-name filename) nil
     (with-tramp-file-property v localname "file-writable-p"
       (if (file-exists-p filename)
          (tramp-check-cached-permissions v ?w)
@@ -4104,10 +4153,9 @@ Let-bind it when necessary.")
                   (not (with-tramp-connection-property
                            (tramp-get-process v) "unsafe-temporary-file"
                          (yes-or-no-p
-                          (eval-when-compile
-                            (concat
-                             "Backup file on local temporary directory, "
-                             "do you want to continue?"))))))
+                          (concat
+                           "Backup file on local temporary directory, "
+                           "do you want to continue?")))))
          (tramp-error v 'file-error "Unsafe backup file name"))))))
 
 (defun tramp-handle-insert-directory
@@ -4264,7 +4312,7 @@ Let-bind it when necessary.")
 (defun tramp-ps-time ()
   "Read printed time oif \"ps\" in format \"[[DD-]hh:]mm:ss\".
 Return it as number of seconds.  Used in `tramp-process-attributes-ps-format'."
-  (search-forward-regexp (rx (+ space)))
+  (search-forward-regexp (rx (+ blank)))
   (search-forward-regexp (rx (? (? (group (+ digit)) "-")
                                   (group (+ digit)) ":")
                                   (group (+ digit)) ":"
@@ -4384,17 +4432,17 @@ It is not guaranteed, that all process attributes as 
described in
                       (cond
                        ((eq (cdr elt) 'number) (read (current-buffer)))
                        ((eq (cdr elt) 'string)
-                        (search-forward-regexp (rx (+ (not space))))
+                        (search-forward-regexp (rx (+ (not blank))))
                         (match-string 0))
                        ((numberp (cdr elt))
-                        (search-forward-regexp (rx (+ space)))
+                        (search-forward-regexp (rx (+ blank)))
                         (search-forward-regexp
                         (rx (+ nonl)) (+ (point) (cdr elt)))
                         (string-trim (match-string 0)))
                        ((fboundp (cdr elt))
                         (funcall (cdr elt)))
                        ((null (cdr elt))
-                        (search-forward-regexp (rx (+ whitespace)))
+                        (search-forward-regexp (rx (+ blank)))
                         (buffer-substring (point) (line-end-position)))))
                      res))
                   ;; `nice' could be `-'.
@@ -4439,7 +4487,7 @@ Do not set it manually, it is used buffer-local in 
`tramp-get-lock-pid'.")
   (rx bos (group (+ nonl))
       "@" (group (+ nonl))
       "." (group (+ digit))
-      (? ":" (group (+ digit))) eos)
+      (? ":" (+ digit)) eos)
   "The format of a lock file.")
 
 (defun tramp-handle-file-locked-p (file)
@@ -4494,10 +4542,9 @@ Do not set it manually, it is used buffer-local in 
`tramp-get-lock-pid'.")
                     (not (with-tramp-connection-property
                              (tramp-get-process v) "unsafe-temporary-file"
                            (yes-or-no-p
-                            (eval-when-compile
-                              (concat
-                               "Lock file on local temporary directory, "
-                               "do you want to continue?"))))))
+                            (concat
+                             "Lock file on local temporary directory, "
+                             "do you want to continue?")))))
            (tramp-error v 'file-error "Unsafe lock file name")))
 
        ;; Do the lock.
@@ -4569,9 +4616,9 @@ Do not set it manually, it is used buffer-local in 
`tramp-get-lock-pid'.")
                     tramp-prefix-format proxy tramp-postfix-host-format))
             (entry
              (list (and (stringp host-port)
-                        (rx bol (literal host-port) eol))
+                        (tramp-compat-rx bol (literal host-port) eol))
                    (and (stringp user-domain)
-                        (rx bol (literal user-domain) eol))
+                        (tramp-compat-rx bol (literal user-domain) eol))
                    (propertize proxy 'tramp-ad-hoc t))))
        (tramp-message vec 5 "Add %S to `tramp-default-proxies-alist'" entry)
        ;; Add the hop.
@@ -4650,7 +4697,7 @@ Do not set it manually, it is used buffer-local in 
`tramp-get-lock-pid'.")
            (setq tramp-default-proxies-alist saved-tdpa)
            (tramp-user-error
             vec "Host name `%s' does not match `%s'" host previous-host))
-         (setq previous-host (rx bol (literal host) eol)))))
+         (setq previous-host (tramp-compat-rx bol (literal host) eol)))))
 
     ;; Result.
     target-alist))
@@ -4839,7 +4886,7 @@ support symbolic links."
 
 (defun tramp-handle-shell-command (command &optional output-buffer 
error-buffer)
   "Like `shell-command' for Tramp files."
-  (let* ((asynchronous (string-match-p (rx (* space) "&" (* space) eos) 
command))
+  (let* ((asynchronous (string-match-p (rx (* blank) "&" (* blank) eos) 
command))
         (command (substring command 0 asynchronous))
         current-buffer-p
         (output-buffer-p output-buffer)
@@ -5142,8 +5189,10 @@ of."
   ;; The descriptor must be a process object.
   (unless (processp proc)
     (tramp-error proc 'file-notify-error "Not a valid descriptor %S" proc))
-  ;; There might be pending output.
-  (while (tramp-accept-process-output proc 0))
+  ;; There might be pending output.  Avoid problems with reentrant
+  ;; call of Tramp.
+  (ignore-errors
+    (while (tramp-accept-process-output proc 0)))
   (tramp-message proc 6 "Kill %S" proc)
   (delete-process proc))
 
@@ -5438,7 +5487,7 @@ performed successfully.  Any other value means an error."
 Mostly useful to protect BODY from being interrupted by timers."
   (declare (indent 1) (debug t))
   `(if (tramp-get-connection-property ,proc "locked")
-       ;; Be kind for older Emacsen.
+       ;; Be kind for old versions of Emacs.
        (if (member 'remote-file-error debug-ignored-errors)
           (throw 'non-essential 'non-essential)
         (tramp-error
@@ -5613,7 +5662,8 @@ the remote host use line-endings as defined in the 
variable
         (tramp-flush-directory-properties vec "/"))
       (when (buffer-live-p buf)
        (with-current-buffer buf
-          (when (and prompt (tramp-search-regexp (rx (literal prompt))))
+          (when (and prompt
+                    (tramp-search-regexp (tramp-compat-rx (literal prompt))))
            (delete-region (point) (point-max))))))))
 
 (defun tramp-get-inode (vec)
@@ -5817,7 +5867,7 @@ VEC is used for tracing."
          (while candidates
            (goto-char (point-min))
            (if (string-match-p
-                (rx bol (literal (car candidates)) (? "\r") eol)
+                (tramp-compat-rx bol (literal (car candidates)) (? "\r") eol)
                 (buffer-string))
                (setq locale (car candidates)
                      candidates nil)
@@ -5860,7 +5910,13 @@ be granted."
          (equal remote-gid tramp-unknown-id-integer)
          (equal remote-gid (file-attribute-group-id file-attr))
          (equal tramp-unknown-id-integer
-                (file-attribute-group-id file-attr)))))))
+                (file-attribute-group-id file-attr))))
+     ;; Group accessible and owned by user's secondary group.
+     (and
+      (eq access
+         (aref (file-attribute-modes file-attr) (+ offset 3)))
+      (member (file-attribute-group-id file-attr)
+             (tramp-get-remote-groups vec 'integer))))))
 
 (defmacro tramp-convert-file-attributes (vec localname id-format attr)
   "Convert `file-attributes' ATTR generated Tramp backend functions.
@@ -5998,6 +6054,52 @@ ID-FORMAT valid values are `string' and `integer'."
       (and (equal id-format 'integer) tramp-unknown-id-integer)
       (and (equal id-format 'string) tramp-unknown-id-string)))
 
+(defun tramp-get-remote-groups (vec id-format)
+  "The list of groups of the remote connection VEC, in ID-FORMAT.
+ID-FORMAT valid values are `string' and `integer'."
+  (and (tramp-file-name-p vec)
+       (with-tramp-connection-property vec (format "groups-%s" id-format)
+        (tramp-file-name-handler #'tramp-get-remote-groups vec id-format))))
+
+(defun tramp-read-id-output (vec)
+  "Read in connection buffer the output of the `id' command.
+Set connection properties \"{uid,gid.groups}-{integer,string}\"."
+  (with-current-buffer (tramp-get-connection-buffer vec)
+    (let (uid-integer uid-string
+         gid-integer gid-string
+         groups-integer groups-string)
+      (goto-char (point-min))
+      ;; Read uid.
+      (when (re-search-forward
+            (rx "uid=" (group (+ digit)) "(" (group (+ (any "_" word))) ")")
+            nil 'noerror)
+       (setq uid-integer (string-to-number (match-string 1))
+             uid-string (match-string 2)))
+      ;; Read gid.
+      (when (re-search-forward
+            (rx "gid=" (group (+ digit)) "(" (group (+ (any "_" word))) ")")
+            nil 'noerror)
+       (setq gid-integer (string-to-number (match-string 1))
+             gid-string (match-string 2)))
+      ;; Read groups.
+      (when (re-search-forward (rx "groups=") nil 'noerror)
+       (while (looking-at
+               (rx (group (+ digit)) "(" (group (+ (any "_" word))) ")"))
+         (setq groups-integer (cons (string-to-number (match-string 1))
+                                    groups-integer)
+               groups-string (cons (match-string 2) groups-string))
+         (goto-char (match-end 0))
+         (skip-chars-forward ",")))
+      ;; Set connection properties.
+      (tramp-set-connection-property vec "uid-integer" uid-integer)
+      (tramp-set-connection-property vec "uid-string" uid-string)
+      (tramp-set-connection-property vec "gid-integer" gid-integer)
+      (tramp-set-connection-property vec "gid-string" gid-string)
+      (tramp-set-connection-property
+       vec "groups-integer" (nreverse groups-integer))
+      (tramp-set-connection-property
+       vec "groups-string" (nreverse groups-string)))))
+
 (defun tramp-local-host-p (vec)
   "Return t if this points to the local host, nil otherwise.
 This handles also chrooted environments, which are not regarded as local."
@@ -6112,10 +6214,9 @@ this file, if that variable is non-nil."
                   (not (with-tramp-connection-property
                            (tramp-get-process v) "unsafe-temporary-file"
                          (yes-or-no-p
-                          (eval-when-compile
-                            (concat
-                             "Autosave file on local temporary directory, "
-                             "do you want to continue?"))))))
+                          (concat
+                           "Autosave file on local temporary directory, "
+                           "do you want to continue?")))))
          (tramp-error v 'file-error "Unsafe autosave file name"))))))
 
 (defun tramp-subst-strs-in-string (alist string)
@@ -6126,7 +6227,7 @@ ALIST is of the form ((FROM . TO) ...)."
       (let* ((pr (car alist))
              (from (car pr))
              (to (cdr pr)))
-        (while (string-match (rx (literal from)) string)
+        (while (string-match (tramp-compat-rx (literal from)) string)
           (setq string (replace-match to t t string)))
         (setq alist (cdr alist))))
     string))
@@ -6389,15 +6490,13 @@ would use a wrong quoting for local file names.  See 
`w32-shell-name'."
 Only works for Bourne-like shells."
   (let ((system-type 'not-windows))
     (save-match-data
-      (let ((result (tramp-unquote-shell-quote-argument s))
-           (nl (regexp-quote (format "\\%s" tramp-rsh-end-of-line))))
+      (let ((result (tramp-unquote-shell-quote-argument s)))
        (when (and (>= (length result) 2)
                   (string= (substring result 0 2) "\\~"))
          (setq result (substring result 1)))
-       (while (string-match nl result)
-         (setq result (replace-match (format "'%s'" tramp-rsh-end-of-line)
-                                     t t result)))
-       result))))
+       (replace-regexp-in-string
+        (tramp-compat-rx "\\" (literal tramp-rsh-end-of-line))
+        (format "'%s'" tramp-rsh-end-of-line) result)))))
 
 ;;; Signal handling.  This works for remote processes, which have set
 ;;; the process property `remote-pid'.
diff --git a/lisp/obsolete/crisp.el b/lisp/obsolete/crisp.el
index 8424c42b69..5e1a278a2c 100644
--- a/lisp/obsolete/crisp.el
+++ b/lisp/obsolete/crisp.el
@@ -174,9 +174,6 @@
 All the bindings are done here instead of globally to try and be
 nice to the world.")
 
-(define-obsolete-variable-alias 'crisp-mode-modeline-string
-  'crisp-mode-mode-line-string "24.3")
-
 (defcustom crisp-mode-mode-line-string " *CRiSP*"
   "String to display in the mode line when CRiSP emulation mode is enabled."
   :type 'string)
diff --git a/lisp/linum.el b/lisp/obsolete/linum.el
similarity index 95%
rename from lisp/linum.el
rename to lisp/obsolete/linum.el
index 1b897a2bd2..c6ce3d6d11 100644
--- a/lisp/linum.el
+++ b/lisp/obsolete/linum.el
@@ -6,6 +6,7 @@
 ;; Maintainer: emacs-devel@gnu.org
 ;; Keywords: convenience
 ;; Old-Version: 0.9x
+;; Obsolete-since: 29.1
 
 ;; This file is part of GNU Emacs.
 
@@ -24,6 +25,13 @@
 
 ;;; Commentary:
 
+;; NOTE: This library was made obsolete in Emacs 29.1.  We recommend
+;; using either the built-in `display-line-numbers-mode', or the
+;; `nlinum' package from GNU ELPA instead.  The former has better
+;; performance, but the latter is closer to a drop-in replacement.
+;;
+;; --------------------
+;;
 ;; Display line numbers for the current buffer.
 ;;
 ;; Toggle display of line numbers with M-x linum-mode.  To enable
@@ -240,6 +248,9 @@ Linum mode is a buffer-local minor mode."
 (defconst linum-version "0.9x")
 (make-obsolete-variable 'linum-version 'emacs-version "28.1")
 
+(make-obsolete 'linum-mode #'display-line-numbers-mode "29.1")
+(make-obsolete 'global-linum-mode #'global-display-line-numbers-mode "29.1")
+
 (provide 'linum)
 
 ;;; linum.el ends here
diff --git a/lisp/thumbs.el b/lisp/obsolete/thumbs.el
similarity index 97%
rename from lisp/thumbs.el
rename to lisp/obsolete/thumbs.el
index 0b3d36d6e3..a98b339b47 100644
--- a/lisp/thumbs.el
+++ b/lisp/obsolete/thumbs.el
@@ -5,6 +5,7 @@
 ;; Author: Jean-Philippe Theberge <jphiltheberge@videotron.ca>
 ;; Maintainer: emacs-devel@gnu.org
 ;; Keywords: Multimedia
+;; Obsolete-since: 29.1
 
 ;; This file is part of GNU Emacs.
 
@@ -23,6 +24,11 @@
 
 ;;; Commentary:
 
+;; NOTE: This library was made obsolete in Emacs 29.1.
+;;       We recommend using `M-x image-dired' instead.
+;;
+;; --------------------
+;;
 ;; This package create two new modes: `thumbs-mode' and 
`thumbs-view-image-mode'.
 ;; It is used for basic browsing and viewing of images from within Emacs.
 ;; Minimal image manipulation functions are also available via external
@@ -99,6 +105,8 @@ This must be the ImageMagick \"convert\" utility."
   :type 'string
   :version "28.1")
 
+(make-obsolete-variable 'thumbs-setroot-command
+                        'wallpaper-commands-alist "29.1")
 (defcustom thumbs-setroot-command
   "xloadimage -onroot -fullscreen *"
   "Command to set the root window."
@@ -385,6 +393,7 @@ and SAME-WINDOW to show thumbs in the same window."
 
 ;;;###autoload
 (defalias 'thumbs 'thumbs-show-from-dir)
+(make-obsolete 'thumbs 'image-dired "29.1")
 
 (defun thumbs-find-image (img &optional num otherwin)
   (let ((buffer (current-buffer)))
@@ -425,6 +434,7 @@ Open another window."
 
 (defun thumbs-call-setroot-command (img)
   "Call the setroot program for IMG."
+  (declare (obsolete wallpaper-set "29.1"))
   (run-hooks 'thumbs-before-setroot-hook)
   (shell-command (string-replace
                  "*"
@@ -435,15 +445,13 @@ Open another window."
 (defun thumbs-set-image-at-point-to-root-window ()
   "Set the image at point as the desktop wallpaper."
   (interactive)
-  (thumbs-call-setroot-command
-   (thumbs-current-image)))
+  (wallpaper-set (thumbs-current-image)))
 
 (defun thumbs-set-root ()
   "Set the current image as root."
   (interactive)
-  (thumbs-call-setroot-command
-   (or thumbs-current-tmp-filename
-       thumbs-current-image-filename)))
+  (wallpaper-set (or thumbs-current-tmp-filename
+                     thumbs-current-image-filename)))
 
 (defun thumbs-file-alist ()
   "Make an alist of elements (POS . FILENAME) for all images in thumb buffer."
@@ -756,13 +764,16 @@ ACTION and ARG should be a valid convert command."
 (defun thumbs-dired-setroot ()
   "In dired, call the setroot program on the image at point."
   (interactive)
-  (thumbs-call-setroot-command (dired-get-filename)))
+  (wallpaper-set (dired-get-filename)))
 
 ;; Modif to dired mode map
 (define-key dired-mode-map "\C-ta" 'thumbs-dired-show)
 (define-key dired-mode-map "\C-tm" 'thumbs-dired-show-marked)
 (define-key dired-mode-map "\C-tw" 'thumbs-dired-setroot)
 
+(make-obsolete-variable 'thumbs-before-setroot-hook nil "29.1")
+(make-obsolete-variable 'thumbs-after-setroot-hook nil "29.1")
+
 (define-obsolete-function-alias 'thumbs-image-type
   #'image-supported-file-p "29.1")
 
diff --git a/lisp/obsolete/url-about.el b/lisp/obsolete/url-about.el
index 608df3f2a5..b9f8732b28 100644
--- a/lisp/obsolete/url-about.el
+++ b/lisp/obsolete/url-about.el
@@ -32,7 +32,7 @@
   (or (get 'url-extension-protocols 'probed)
       (mapc (lambda (s) (url-scheme-get-property s 'name))
            (or (get 'url-extension-protocols 'schemes)
-               (let ((schemes '("info" "man" "rlogin" "telnet"
+                (let ((schemes '("info" "man" "telnet"
                                 "tn3270" "data" "snews")))
                  (mapc (lambda (d)
                          (mapc (lambda (f)
diff --git a/lisp/obsolete/vc-arch.el b/lisp/obsolete/vc-arch.el
index 537d65c658..20835a09d0 100644
--- a/lisp/obsolete/vc-arch.el
+++ b/lisp/obsolete/vc-arch.el
@@ -311,7 +311,7 @@ Only the value `maybe' can be trusted :-(."
 
 ;; dir-status-files called from vc-dir, which loads vc,
 ;; which loads vc-dispatcher.
-(declare-function vc-exec-after "vc-dispatcher" (code))
+(declare-function vc-exec-after "vc-dispatcher" (code &optional success))
 
 (defun vc-arch-dir-status-files (dir _files callback)
   "Run `tla inventory' for DIR and pass results to CALLBACK.
diff --git a/lisp/obsolete/vc-mtn.el b/lisp/obsolete/vc-mtn.el
index cd56b29007..4fc496d509 100644
--- a/lisp/obsolete/vc-mtn.el
+++ b/lisp/obsolete/vc-mtn.el
@@ -141,7 +141,7 @@ switches."
 
 ;; dir-status-files called from vc-dir, which loads vc,
 ;; which loads vc-dispatcher.
-(declare-function vc-exec-after "vc-dispatcher" (code))
+(declare-function vc-exec-after "vc-dispatcher" (code &optional success))
 
 (defun vc-mtn-dir-status-files (dir _files update-function)
   (vc-mtn-command (current-buffer) 'async dir "status")
diff --git a/lisp/org/org-macro.el b/lisp/org/org-macro.el
index 0921f3aa27..b58c51f3fb 100644
--- a/lisp/org/org-macro.el
+++ b/lisp/org/org-macro.el
@@ -66,7 +66,7 @@
 (declare-function org-mode "org" ())
 (declare-function vc-backend "vc-hooks" (f))
 (declare-function vc-call "vc-hooks" (fun file &rest args) t)
-(declare-function vc-exec-after "vc-dispatcher" (code))
+(declare-function vc-exec-after "vc-dispatcher" (code &optional success))
 
 (defvar org-link-search-must-match-exact-headline)
 
diff --git a/lisp/org/org-version.el b/lisp/org/org-version.el
index 353d533c06..3273d8707d 100644
--- a/lisp/org/org-version.el
+++ b/lisp/org/org-version.el
@@ -5,13 +5,13 @@
 (defun org-release ()
   "The release version of Org.
 Inserted by installing Org mode or when a release is made."
-   (let ((org-release "9.5.4"))
+   (let ((org-release "9.5.5"))
      org-release))
 ;;;###autoload
 (defun org-git-version ()
   "The Git version of Org mode.
 Inserted by installing Org or when a release is made."
-   (let ((org-git-version "release_9.5.4-19-g4dff42"))
+   (let ((org-git-version "release_9.5.5"))
      org-git-version))
 
 (provide 'org-version)
diff --git a/lisp/org/org.el b/lisp/org/org.el
index 9facbed04d..6f92cdeab5 100644
--- a/lisp/org/org.el
+++ b/lisp/org/org.el
@@ -9,7 +9,7 @@
 ;; Homepage: https://orgmode.org
 ;; Package-Requires: ((emacs "25.1"))
 
-;; Version: 9.5.4
+;; Version: 9.5.5
 
 ;; This file is part of GNU Emacs.
 ;;
diff --git a/lisp/org/ox.el b/lisp/org/ox.el
index 9a2a69b2c1..56bb4b74df 100644
--- a/lisp/org/ox.el
+++ b/lisp/org/ox.el
@@ -4642,7 +4642,7 @@ from the export back-end."
 ;; a given element, excluded.  Note: "-n" switches reset that count.
 ;;
 ;; `org-export-unravel-code' extracts source code (along with a code
-;; references alist) from an `element-block' or `src-block' type
+;; references alist) from an `example-block' or `src-block' type
 ;; element.
 ;;
 ;; `org-export-format-code' applies a formatting function to each line
diff --git a/lisp/outline.el b/lisp/outline.el
index 3250b62f1e..93a9247f61 100644
--- a/lisp/outline.el
+++ b/lisp/outline.el
@@ -288,26 +288,70 @@ The value should be a `buffer-match-p' condition.
 These buttons can be used to hide and show the body under the heading.
 Note that this feature is not meant to be used in editing
 buffers (yet) -- that will be amended in a future version."
-  ;; FIXME -- is there a `buffer-match-p' defcustom type somewhere?
-  :type 'sexp
+  :type 'buffer-predicate
   :safe #'booleanp
   :version "29.1")
 
-(define-icon outline-open button
-  '((emoji "🔽")
+(defvar-local outline--use-buttons nil
+  "Non-nil when buffer displays clickable buttons on the headings.")
+
+(defvar-local outline--use-rtl nil
+  "Non-nil when direction of clickable buttons is right-to-left.")
+
+(defcustom outline-minor-mode-use-margins '(and (derived-mode . special-mode)
+                                                (not (derived-mode . 
help-mode)))
+  "Whether to display clickable buttons in the margins.
+The value should be a `buffer-match-p' condition.
+
+These buttons can be used to hide and show the body under the heading.
+Note that this feature is meant to be used in editing buffers."
+  :type 'buffer-predicate
+  :safe #'booleanp
+  :version "29.1")
+
+(defvar-local outline--use-margins nil
+  "Non-nil when buffer displays clickable buttons in the margins.")
+
+(define-icon outline-open nil
+  '((image "outline-open.svg" "outline-open.pbm" :height (0.8 . em))
+    (emoji "🔽")
     (symbol " ▼ ")
     (text " open "))
-  "Icon used for buttons for opening a section in outline buffers."
+  "Icon used for buttons for opened sections in outline buffers."
   :version "29.1"
-  :help-echo "Open this section")
+  :help-echo "Close this section")
 
-(define-icon outline-close button
-  '((emoji "▶️")
+(define-icon outline-close nil
+  '((image "outline-close.svg" "outline-close.pbm" :height (0.8 . em))
+    (emoji "▶️")
     (symbol " ▶ ")
     (text " close "))
-  "Icon used for buttons for closing a section in outline buffers."
+  "Icon used for buttons for closed sections in outline buffers."
   :version "29.1"
-  :help-echo "Close this section")
+  :help-echo "Open this section")
+
+(define-icon outline-close-rtl outline-close
+  '((image "outline-close.svg" "outline-close.pbm" :height (0.8 . em)
+           :rotation 180)
+    (emoji "◀️")
+    (symbol " ◀ "))
+  "Right-to-left icon used for buttons in closed outline sections."
+  :version "29.1")
+
+(define-icon outline-open-in-margins outline-open
+  '((image "outline-open.svg" "outline-open.pbm" :height 10))
+  "Icon used for buttons for opened sections in margins."
+  :version "29.1")
+
+(define-icon outline-close-in-margins outline-close
+  '((image "outline-open.svg" "outline-open.pbm" :height 10 :rotation -90))
+  "Icon used for buttons for closed sections in margins."
+  :version "29.1")
+
+(define-icon outline-close-rtl-in-margins outline-close-rtl
+  '((image "outline-open.svg" "outline-open.pbm" :height 10 :rotation 90))
+  "Right-to-left icon used for closed sections in margins."
+  :version "29.1")
 
 
 (defvar outline-level #'outline-level
@@ -338,6 +382,10 @@ data reflects the `outline-regexp'.")
   :safe #'booleanp
   :version "22.1")
 
+(defvar outline-imenu-generic-expression
+  (list (list nil (concat "^\\(?:" outline-regexp "\\).*$") 0))
+  "Value for `imenu-generic-expression' in Outline mode.")
+
 ;;;###autoload
 (define-derived-mode outline-mode text-mode "Outline"
   "Set major mode for editing outlines with selective display.
@@ -372,8 +420,7 @@ Turning on outline mode calls the value of `text-mode-hook' 
and then of
               (concat paragraph-separate "\\|\\(?:" outline-regexp "\\)"))
   (setq-local font-lock-defaults
               '(outline-font-lock-keywords t nil nil backward-paragraph))
-  (setq-local imenu-generic-expression
-             (list (list nil (concat "^\\(?:" outline-regexp "\\).*$") 0)))
+  (setq-local imenu-generic-expression outline-imenu-generic-expression)
   (add-hook 'change-major-mode-hook #'outline-show-all nil t)
   (add-hook 'hack-local-variables-hook #'outline-apply-default-state nil t))
 
@@ -427,16 +474,13 @@ outline font-lock faces to those of major mode."
     (goto-char (point-min))
     (let ((regexp (concat "^\\(?:" outline-regexp "\\).*$")))
       (while (re-search-forward regexp nil t)
-        (let ((overlay (make-overlay (match-beginning 0)
-                                     (match-end 0))))
+        (let ((overlay (make-overlay (match-beginning 0) (match-end 0))))
           (overlay-put overlay 'outline-overlay t)
-          (when (or (eq outline-minor-mode-highlight 'override)
+          ;; FIXME: Is it possible to override all underlying face attributes?
+          (when (or (memq outline-minor-mode-highlight '(append override))
                     (and (eq outline-minor-mode-highlight t)
-                         (goto-char (match-beginning 0))
-                         (not (get-text-property (point) 'face))))
-            (overlay-put overlay 'face (outline-font-lock-face)))
-          (when (and (outline--use-buttons-p) (outline-on-heading-p))
-            (outline--insert-open-button)))
+                         (not (get-text-property (match-beginning 0) 'face))))
+            (overlay-put overlay 'face (outline-font-lock-face))))
         (goto-char (match-end 0))))))
 
 ;;;###autoload
@@ -445,17 +489,47 @@ outline font-lock faces to those of major mode."
 
 See the command `outline-mode' for more information on this mode."
   :lighter " Outl"
-  :keymap (easy-mmode-define-keymap
-           `(([menu-bar] . ,outline-minor-mode-menu-bar-map)
-             (,outline-minor-mode-prefix . ,outline-mode-prefix-map))
-           :inherit outline-minor-mode-cycle-map)
+  :keymap (define-keymap
+            :parent outline-minor-mode-cycle-map
+            "<menu-bar>" outline-minor-mode-menu-bar-map
+            "<left-margin> <mouse-1>" 'outline-cycle
+            "<right-margin> <mouse-1>" 'outline-cycle
+            "<left-margin> S-<mouse-1>" 'outline-cycle-buffer
+            "<right-margin> S-<mouse-1>" 'outline-cycle-buffer
+            (key-description outline-minor-mode-prefix) 
outline-mode-prefix-map)
   (if outline-minor-mode
       (progn
+        (cond
+         ((buffer-match-p outline-minor-mode-use-margins (current-buffer))
+          (setq-local outline--use-margins t))
+         ((buffer-match-p outline-minor-mode-use-buttons (current-buffer))
+          (setq-local outline--use-buttons t)))
+        (when (and (or outline--use-buttons outline--use-margins)
+                   (eq (current-bidi-paragraph-direction) 'right-to-left))
+          (setq-local outline--use-rtl t))
+        (when outline--use-margins
+          (if outline--use-rtl
+              (setq-local right-margin-width (1+ right-margin-width))
+            (setq-local left-margin-width (1+ left-margin-width)))
+          (setq-local fringes-outside-margins t)
+          ;; Force display of margins
+          (set-window-buffer nil (window-buffer)))
+        (when (or outline--use-buttons outline--use-margins)
+          (add-hook 'after-change-functions
+                    (lambda (beg end _len)
+                      (when outline--use-buttons
+                        (remove-overlays beg end 'outline-button t))
+                      (when outline--use-margins
+                        (remove-overlays beg end 'outline-margin t))
+                      (outline--fix-up-all-buttons beg end))
+                    nil t))
         (when outline-minor-mode-highlight
-          (when (and global-font-lock-mode (font-lock-specified-p major-mode))
-            (font-lock-add-keywords nil outline-font-lock-keywords t)
-            (font-lock-flush))
-          (outline-minor-mode-highlight-buffer))
+          (if (and global-font-lock-mode (font-lock-specified-p major-mode))
+              (progn
+                (font-lock-add-keywords nil outline-font-lock-keywords t)
+                (font-lock-flush))
+            (outline-minor-mode-highlight-buffer)))
+        (outline--fix-up-all-buttons)
        ;; Turn off this mode if we change major modes.
        (add-hook 'change-major-mode-hook
                  (lambda () (outline-minor-mode -1))
@@ -464,20 +538,26 @@ See the command `outline-mode' for more information on 
this mode."
        ;; Cause use of ellipses for invisible text.
        (add-to-invisibility-spec '(outline . t))
        (outline-apply-default-state))
-    (when outline-minor-mode-highlight
-      (if font-lock-fontified
-          (font-lock-remove-keywords nil outline-font-lock-keywords))
-      (remove-overlays nil nil 'outline-overlay t)
-      (font-lock-flush))
     (setq line-move-ignore-invisible nil)
     ;; Cause use of ellipses for invisible text.
     (remove-from-invisibility-spec '(outline . t))
     ;; When turning off outline mode, get rid of any outline hiding.
-    (outline-show-all)))
-
-(defun outline--use-buttons-p ()
-  (and outline-minor-mode
-       (buffer-match-p outline-minor-mode-use-buttons (current-buffer))))
+    (outline-show-all)
+    (when outline-minor-mode-highlight
+      (if font-lock-fontified
+          (font-lock-remove-keywords nil outline-font-lock-keywords))
+      (font-lock-flush)
+      (remove-overlays nil nil 'outline-overlay t))
+    (when outline--use-buttons
+      (remove-overlays nil nil 'outline-button t))
+    (when outline--use-margins
+      (remove-overlays nil nil 'outline-margin t)
+      (if outline--use-rtl
+          (setq-local right-margin-width (1- right-margin-width))
+        (setq-local left-margin-width (1- left-margin-width)))
+      (setq-local fringes-outside-margins nil)
+      ;; Force removal of margins
+      (set-window-buffer nil (window-buffer)))))
 
 (defvar-local outline-heading-alist ()
   "Alist associating a heading for every possible level.
@@ -980,11 +1060,10 @@ Note that this does not hide the lines preceding the 
first heading line."
   "Hide everything after this heading at deeper levels.
 If non-nil, EVENT should be a mouse event."
   (interactive (list last-nonmenu-event))
-  (when (mouse-event-p event)
-    (mouse-set-point event))
-  (when (outline--use-buttons-p)
-    (outline--insert-close-button))
-  (outline-flag-subtree t))
+  (save-excursion
+    (when (mouse-event-p event)
+      (mouse-set-point event))
+    (outline-flag-subtree t)))
 
 (defun outline--make-button-overlay (type)
   (let ((o (seq-find (lambda (o)
@@ -992,27 +1071,57 @@ If non-nil, EVENT should be a mouse event."
                      (overlays-at (point)))))
     (unless o
       (setq o (make-overlay (point) (1+ (point))))
+      (overlay-put o 'evaporate t)
       (overlay-put o 'follow-link 'mouse-face)
       (overlay-put o 'mouse-face 'highlight)
       (overlay-put o 'outline-button t))
-    (let ((icon
-           (icon-elements (if (eq type 'close) 'outline-close 'outline-open)))
+    (let ((icon (icon-elements (if (eq type 'close)
+                                   (if outline--use-rtl
+                                       'outline-close-rtl
+                                     'outline-close)
+                                 'outline-open)))
           (inhibit-read-only t))
       ;; In editing buffers we use overlays only, but in other buffers
       ;; we use a mix of text properties, text and overlays to make
       ;; movement commands work more logically.
       (when (derived-mode-p 'special-mode)
         (put-text-property (point) (1+ (point)) 'face (plist-get icon 'face)))
-      (when-let ((image (plist-get icon 'image)))
-        (overlay-put o 'display image))
-      (overlay-put o 'display (plist-get icon 'string))
-      (overlay-put o 'face (plist-get icon 'face)))
+      (if-let ((image (plist-get icon 'image)))
+          (overlay-put o 'display image)
+        (overlay-put o 'display (concat (plist-get icon 'string)
+                                        (string (char-after (point)))))
+        (overlay-put o 'face (plist-get icon 'face))))
     o))
 
-(defun outline--insert-open-button ()
+(defun outline--make-margin-overlay (type)
+  (let ((o (seq-find (lambda (o)
+                       (overlay-get o 'outline-margin))
+                     (overlays-at (point)))))
+    (unless o
+      (setq o (make-overlay (point) (1+ (point))))
+      (overlay-put o 'evaporate t)
+      (overlay-put o 'outline-margin t))
+    (let ((icon (icon-elements (if (eq type 'close)
+                                   (if outline--use-rtl
+                                       'outline-close-rtl-in-margins
+                                     'outline-close-in-margins)
+                                 'outline-open-in-margins)))
+          (inhibit-read-only t))
+      (overlay-put
+       o 'before-string
+       (propertize " " 'display
+                   `((margin ,(if outline--use-rtl
+                                  'right-margin 'left-margin))
+                     ,(or (plist-get icon 'image)
+                          (plist-get icon 'string))))))
+    o))
+
+(defun outline--insert-open-button (&optional use-margins)
   (with-silent-modifications
     (save-excursion
-        (beginning-of-line)
+      (beginning-of-line)
+      (if use-margins
+          (outline--make-margin-overlay 'open)
         (when (derived-mode-p 'special-mode)
           (let ((inhibit-read-only t))
             (insert "  ")
@@ -1022,12 +1131,14 @@ If non-nil, EVENT should be a mouse event."
           (overlay-put o 'keymap
                        (define-keymap
                          "RET" #'outline-hide-subtree
-                         "<mouse-2>" #'outline-hide-subtree))))))
+                         "<mouse-2>" #'outline-hide-subtree)))))))
 
-(defun outline--insert-close-button ()
+(defun outline--insert-close-button (&optional use-margins)
   (with-silent-modifications
     (save-excursion
-        (beginning-of-line)
+      (beginning-of-line)
+      (if use-margins
+          (outline--make-margin-overlay 'close)
         (when (derived-mode-p 'special-mode)
           (let ((inhibit-read-only t))
             (insert "  ")
@@ -1037,22 +1148,22 @@ If non-nil, EVENT should be a mouse event."
           (overlay-put o 'keymap
                        (define-keymap
                          "RET" #'outline-show-subtree
-                         "<mouse-2>" #'outline-show-subtree))))))
+                         "<mouse-2>" #'outline-show-subtree)))))))
 
 (defun outline--fix-up-all-buttons (&optional from to)
-  (when from
-    (save-excursion
-      (goto-char from)
-      (setq from (line-beginning-position))))
-  (when (outline--use-buttons-p)
+  (when (or outline--use-buttons outline--use-margins)
+    (when from
+      (save-excursion
+        (goto-char from)
+        (setq from (line-beginning-position))))
     (outline-map-region
      (lambda ()
-       ;; `outline--cycle-state' will fail if we're in a totally
-       ;; collapsed buffer -- but in that case, we're not in a
-       ;; `show-all' situation.
-       (if (eq (ignore-errors (outline--cycle-state)) 'show-all)
-           (outline--insert-open-button)
-         (outline--insert-close-button)))
+       (if (save-excursion
+             (outline-end-of-heading)
+             (seq-some (lambda (o) (eq (overlay-get o 'invisible) 'outline))
+                       (overlays-at (point))))
+           (outline--insert-close-button outline--use-margins)
+         (outline--insert-open-button outline--use-margins)))
      (or from (point-min)) (or to (point-max)))))
 
 (define-obsolete-function-alias 'hide-subtree #'outline-hide-subtree "25.1")
@@ -1071,13 +1182,13 @@ If non-nil, EVENT should be a mouse event."
 (define-obsolete-function-alias 'hide-leaves #'outline-hide-leaves "25.1")
 
 (defun outline-show-subtree (&optional event)
-  "Show everything after this heading at deeper levels."
+  "Show everything after this heading at deeper levels.
+If non-nil, EVENT should be a mouse event."
   (interactive (list last-nonmenu-event))
-  (when (mouse-event-p event)
-    (mouse-set-point event))
-  (when (outline--use-buttons-p)
-    (outline--insert-open-button))
-  (outline-flag-subtree nil))
+  (save-excursion
+    (when (mouse-event-p event)
+      (mouse-set-point event))
+    (outline-flag-subtree nil)))
 
 (define-obsolete-function-alias 'show-subtree #'outline-show-subtree "25.1")
 
@@ -1552,7 +1663,7 @@ Return either `hide-all', `headings-only', or `show-all'."
     (< (save-excursion (outline-next-heading) (point))
        (save-excursion (outline-end-of-subtree) (point)))))
 
-(defun outline-cycle ()
+(defun outline-cycle (&optional event)
   "Cycle visibility state of the current heading line's body.
 
 This cycles the visibility of the current heading line's subheadings
@@ -1560,28 +1671,33 @@ and body between `hide all', `headings only' and `show 
all'.
 
 `Hide all' means hide all the subheadings and their bodies.
 `Headings only' means show the subheadings, but not their bodies.
-`Show all' means show all the subheadings and their bodies."
-  (interactive)
-  (condition-case nil
-      (pcase (outline--cycle-state)
-        ('hide-all
-         (if (outline-has-subheading-p)
-             (progn (outline-show-children)
-                    (message "Only headings"))
+`Show all' means show all the subheadings and their bodies.
+
+If non-nil, EVENT should be a mouse event."
+  (interactive (list last-nonmenu-event))
+  (save-excursion
+    (when (mouse-event-p event)
+      (mouse-set-point event))
+    (condition-case nil
+        (pcase (outline--cycle-state)
+          ('hide-all
+           (if (outline-has-subheading-p)
+               (progn (outline-show-children)
+                      (message "Only headings"))
+             (outline-show-subtree)
+             (message "Show all")))
+          ('headings-only
            (outline-show-subtree)
-           (message "Show all")))
-        ('headings-only
-         (outline-show-subtree)
-         (message "Show all"))
-        ('show-all
-         (outline-hide-subtree)
-         (message "Hide all")))
-    (outline-before-first-heading nil)))
+           (message "Show all"))
+          ('show-all
+           (outline-hide-subtree)
+           (message "Hide all")))
+      (outline-before-first-heading nil))))
 
 (defvar-local outline--cycle-buffer-state 'show-all
   "Internal variable used for tracking buffer cycle state.")
 
-(defun outline-cycle-buffer ()
+(defun outline-cycle-buffer (&optional level)
   "Cycle visibility state of the body lines of the whole buffer.
 
 This cycles the visibility of all the subheadings and bodies of all
@@ -1590,20 +1706,28 @@ the heading lines in the buffer.  It cycles them 
between `hide all',
 
 `Hide all' means hide all the buffer's subheadings and their bodies.
 `Headings only' means show all the subheadings, but not their bodies.
-`Show all' means show all the buffer's subheadings and their bodies."
-  (interactive)
-  (let (has-top-level)
+`Show all' means show all the buffer's subheadings and their bodies.
+
+With a prefix argument, show headings up to that LEVEL."
+  (interactive (list (when current-prefix-arg
+                       (prefix-numeric-value current-prefix-arg))))
+  (let (top-level)
     (save-excursion
       (goto-char (point-min))
-      (while (not (or has-top-level (eobp)))
-        (when (outline-on-heading-p t)
-          (when (= (funcall outline-level) 1)
-            (setq has-top-level t)))
+      (while (not (or (eq top-level 1) (eobp)))
+        (when-let ((level (and (outline-on-heading-p t)
+                               (funcall outline-level))))
+          (when (< level (or top-level most-positive-fixnum))
+            (setq top-level (max level 1))))
         (outline-next-heading)))
     (cond
+     (level
+      (outline-hide-sublevels level)
+      (setq outline--cycle-buffer-state 'all-heading)
+      (message "All headings up to level %s" level))
      ((and (eq outline--cycle-buffer-state 'show-all)
-           has-top-level)
-      (outline-hide-sublevels 1)
+           top-level)
+      (outline-hide-sublevels top-level)
       (setq outline--cycle-buffer-state 'top-level)
       (message "Top level headings"))
      ((or (eq outline--cycle-buffer-state 'show-all)
@@ -1615,22 +1739,20 @@ the heading lines in the buffer.  It cycles them 
between `hide all',
      (t
       (outline-show-all)
       (setq outline--cycle-buffer-state 'show-all)
-      (message "Show all")))
-    (outline--fix-up-all-buttons)))
+      (message "Show all")))))
 
-(defvar outline-navigation-repeat-map
-  (let ((map (make-sparse-keymap)))
-    (define-key map (kbd "C-b") #'outline-backward-same-level)
-    (define-key map (kbd "b") #'outline-backward-same-level)
-    (define-key map (kbd "C-f") #'outline-forward-same-level)
-    (define-key map (kbd "f") #'outline-forward-same-level)
-    (define-key map (kbd "C-n") #'outline-next-visible-heading)
-    (define-key map (kbd "n") #'outline-next-visible-heading)
-    (define-key map (kbd "C-p") #'outline-previous-visible-heading)
-    (define-key map (kbd "p") #'outline-previous-visible-heading)
-    (define-key map (kbd "C-u") #'outline-up-heading)
-    (define-key map (kbd "u") #'outline-up-heading)
-    map))
+
+(defvar-keymap outline-navigation-repeat-map
+  "C-b" #'outline-backward-same-level
+  "b"   #'outline-backward-same-level
+  "C-f" #'outline-forward-same-level
+  "f"   #'outline-forward-same-level
+  "C-n" #'outline-next-visible-heading
+  "n"   #'outline-next-visible-heading
+  "C-p" #'outline-previous-visible-heading
+  "p"   #'outline-previous-visible-heading
+  "C-u" #'outline-up-heading
+  "u"   #'outline-up-heading)
 
 (dolist (command '(outline-backward-same-level
                    outline-forward-same-level
@@ -1639,17 +1761,15 @@ the heading lines in the buffer.  It cycles them 
between `hide all',
                    outline-up-heading))
   (put command 'repeat-map 'outline-navigation-repeat-map))
 
-(defvar outline-editing-repeat-map
-  (let ((map (make-sparse-keymap)))
-    (define-key map (kbd "C-v") #'outline-move-subtree-down)
-    (define-key map (kbd "v") #'outline-move-subtree-down)
-    (define-key map (kbd "C-^") #'outline-move-subtree-up)
-    (define-key map (kbd "^") #'outline-move-subtree-up)
-    (define-key map (kbd "C->") #'outline-demote)
-    (define-key map (kbd ">") #'outline-demote)
-    (define-key map (kbd "C-<") #'outline-promote)
-    (define-key map (kbd "<") #'outline-promote)
-    map))
+(defvar-keymap outline-editing-repeat-map
+  "C-v" #'outline-move-subtree-down
+  "v"   #'outline-move-subtree-down
+  "C-^" #'outline-move-subtree-up
+  "^"   #'outline-move-subtree-up
+  "C->" #'outline-demote
+  ">"   #'outline-demote
+  "C-<" #'outline-promote
+  "<"   #'outline-promote)
 
 (dolist (command '(outline-move-subtree-down
                    outline-move-subtree-up
@@ -1657,6 +1777,7 @@ the heading lines in the buffer.  It cycles them between 
`hide all',
                    outline-promote))
   (put command 'repeat-map 'outline-editing-repeat-map))
 
+
 (provide 'outline)
 (provide 'noutline)
 
diff --git a/lisp/paren.el b/lisp/paren.el
index d7580de9a9..e2c060ceb9 100644
--- a/lisp/paren.el
+++ b/lisp/paren.el
@@ -118,6 +118,14 @@ On non-graphical frames, the context is shown in the echo 
area."
   (let ((ol (make-overlay (point) (point) nil t))) (delete-overlay ol) ol)
   "Overlay used to highlight the paren at point.")
 
+(defcustom show-paren-predicate '(not (derived-mode . special-mode))
+  "Whether to use `show-paren-mode' in a buffer.
+The default is to enable the mode in all buffers that have don't
+derive from `special-mode', which means that it's on (by default)
+in all editing buffers."
+  :type 'buffer-predicate
+  :safe #'booleanp
+  :version "29.1")
 
 ;;;###autoload
 (define-minor-mode show-paren-mode
@@ -126,6 +134,9 @@ On non-graphical frames, the context is shown in the echo 
area."
 When enabled, any matching parenthesis is highlighted in `show-paren-style'
 after `show-paren-delay' seconds of Emacs idle time.
 
+Also see `show-paren-predicate', which controls which buffers
+this mode is enabled in.
+
 This is a global minor mode.  To toggle the mode in a single buffer,
 use `show-paren-local-mode'."
   :global t :group 'paren-showing
@@ -414,7 +425,13 @@ It is the default value of `show-paren-data-function'."
 
 (defun show-paren-function ()
   "Highlight the parentheses until the next input arrives."
-  (let ((data (and show-paren-mode (funcall show-paren-data-function))))
+  (let ((data (and show-paren-mode
+                   ;; If we're using `show-paren-local-mode', then
+                   ;; always heed the value.
+                   (or (local-variable-p 'show-paren-mode)
+                       ;; If not, check that the predicate matches.
+                       (buffer-match-p show-paren-predicate (current-buffer)))
+                   (funcall show-paren-data-function))))
     (if (not data)
         (progn
           ;; If show-paren-mode is nil in this buffer or if not at a paren that
diff --git a/lisp/pcmpl-git.el b/lisp/pcmpl-git.el
new file mode 100644
index 0000000000..3584fa0673
--- /dev/null
+++ b/lisp/pcmpl-git.el
@@ -0,0 +1,110 @@
+;;; pcmpl-git.el --- Completions for Git -*- lexical-binding: t -*-
+
+;; Copyright (C) 2022 Free Software Foundation, Inc.
+
+;; Package: pcomplete
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; This library provides completion rules for the Git program.
+
+;;; Code:
+
+(require 'pcomplete)
+(require 'vc-git)
+
+(defun pcmpl-git--expand-flags (args)
+  "In the list of ARGS, expand arguments of the form --[no-]flag."
+  (mapcan (lambda (arg) (if (string-search "[no-]" arg)
+                            (list (string-replace "[no-]" "" arg)
+                                  (string-replace "[no-]" "no-" arg))
+                          (list arg)))
+          args))
+
+(defun pcmpl-git--tracked-file-predicate (&rest args)
+  "Return a predicate function determining the Git status of a file.
+Files listed by `git ls-files ARGS' satisfy the predicate."
+  (when-let ((files (mapcar #'expand-file-name
+                            (ignore-errors
+                              (apply #'process-lines
+                                     vc-git-program "ls-files" args)))))
+    (lambda (file)
+      (setq file (expand-file-name file))
+      (if (string-suffix-p "/" file)
+          (seq-some (lambda (f) (string-prefix-p file f))
+                    files)
+        (member file files)))))
+
+(defun pcmpl-git--remote-refs (remote)
+  "List the locally known Git revisions from REMOTE."
+  (delq nil
+        (mapcar
+         (let ((re (concat "\\`" (regexp-quote remote) "/\\(.*\\)")))
+           (lambda (s) (when (string-match re s) (match-string 1 s))))
+         (vc-git-revision-table nil))))
+
+;;;###autoload
+(defun pcomplete/git ()
+  "Completion for the `git' command."
+  (let ((subcommands (pcomplete-from-help `(,vc-git-program "help" "-a")
+                                          :margin "^\\( +\\)[a-z]"
+                                          :argument "[[:alnum:]-]+")))
+    (while (not (member (pcomplete-arg 1) subcommands))
+      (if (string-prefix-p "-" (pcomplete-arg))
+          (pcomplete-here (pcomplete-from-help `(,vc-git-program "help")
+                                               :margin "\\(\\[\\)-"
+                                               :separator " | "
+                                               :description "\\`"))
+        (pcomplete-here (completion-table-merge
+                         subcommands
+                         (when (string-prefix-p "-" (pcomplete-arg 1))
+                           (pcomplete-entries))))))
+    (let ((subcmd (pcomplete-arg 1)))
+      (while (pcase subcmd
+               ((guard (string-prefix-p "-" (pcomplete-arg)))
+                (pcomplete-here
+                 (pcmpl-git--expand-flags
+                  (pcomplete-from-help `(,vc-git-program "help" ,subcmd)
+                                       :argument
+                                       "-+\\(?:\\[no-\\]\\)?[a-z-]+=?"))))
+               ;; Complete modified tracked files
+               ((or "add" "commit" "restore")
+                (pcomplete-here
+                 (pcomplete-entries
+                  nil (pcmpl-git--tracked-file-predicate "-m"))))
+               ;; Complete all tracked files
+               ((or "mv" "rm" "grep" "status")
+                (pcomplete-here
+                 (pcomplete-entries nil (pcmpl-git--tracked-file-predicate))))
+               ;; Complete revisions
+               ((or "branch" "merge" "rebase" "switch")
+                (pcomplete-here (vc-git-revision-table nil)))
+               ;; Complete revisions and tracked files
+               ;; TODO: diff and log accept revision ranges
+               ((or "checkout" "reset" "show" "diff" "log")
+                (pcomplete-here
+                 (completion-table-in-turn
+                  (vc-git-revision-table nil)
+                  (pcomplete-entries nil 
(pcmpl-git--tracked-file-predicate)))))
+               ;; Complete remotes and their revisions
+               ((or "fetch" "pull" "push")
+                (pcomplete-here (process-lines vc-git-program "remote"))
+                (pcomplete-here (pcmpl-git--remote-refs (pcomplete-arg 
1)))))))))
+
+(provide 'pcmpl-git)
+;;; pcmpl-git.el ends here
diff --git a/lisp/pcmpl-gnu.el b/lisp/pcmpl-gnu.el
index 3c9bf1ec9d..cdfde5640a 100644
--- a/lisp/pcmpl-gnu.el
+++ b/lisp/pcmpl-gnu.el
@@ -394,6 +394,40 @@ Return the new list."
     (while (pcomplete-here (pcomplete-dirs) nil #'identity))))
 
 ;;;###autoload
-(defalias 'pcomplete/gdb 'pcomplete/xargs)
+(defun pcomplete/awk ()
+  "Completion for the `awk' command."
+  (pcomplete-here-using-help "awk --help"
+                             :margin "\t"
+                             :separator "  +"
+                             :description "\0"
+                             :metavar "[=a-z]+"))
+
+;;;###autoload
+(defun pcomplete/gpg ()
+  "Completion for the `gpg` command."
+  (pcomplete-here-using-help "gpg --help" :narrow-end "^ -se"))
+
+;;;###autoload
+(defun pcomplete/gdb ()
+  "Completion for the `gdb' command."
+  (while
+      (cond
+       ((string= "--args" (pcomplete-arg 1))
+        (funcall pcomplete-command-completion-function)
+        (funcall (or (pcomplete-find-completion-function (pcomplete-arg 1))
+                    pcomplete-default-completion-function)))
+       ((string-prefix-p "-" (pcomplete-arg 0))
+        (pcomplete-here (pcomplete-from-help "gdb --help")))
+       (t (pcomplete-here (pcomplete-entries))))))
+
+;;;###autoload
+(defun pcomplete/emacs ()
+  "Completion for the `emacs' command."
+  (pcomplete-here-using-help "emacs --help" :margin "^\\(\\)-"))
+
+;;;###autoload
+(defun pcomplete/emacsclient ()
+  "Completion for the `emacsclient' command."
+  (pcomplete-here-using-help "emacsclient --help" :margin "^\\(\\)-"))
 
 ;;; pcmpl-gnu.el ends here
diff --git a/lisp/pcmpl-linux.el b/lisp/pcmpl-linux.el
index 7c072f3d40..023c655a2a 100644
--- a/lisp/pcmpl-linux.el
+++ b/lisp/pcmpl-linux.el
@@ -30,6 +30,7 @@
 (provide 'pcmpl-linux)
 
 (require 'pcomplete)
+(eval-when-compile (require 'rx))
 
 ;; Functions:
 
@@ -111,4 +112,71 @@ Test is done using `equal'."
        (pcomplete-uniquify-list points)
        (cons "swap" (pcmpl-linux-mounted-directories))))))
 
+;;; systemd
+
+(defun pcmpl-linux--systemd-units (&rest args)
+  "Run `systemd list-units ARGS' and return the output as a list."
+  (with-temp-buffer
+    (apply #'call-process
+           "systemctl" nil '(t nil) nil
+           "list-units" "--full" "--legend=no" "--plain" args)
+    (goto-char (point-min))
+    (let (result)
+      (while (re-search-forward (rx bol (group (+ (not space)))
+                                    (+ space) (+ (not space))
+                                    (+ space) (group (+ (not space)))
+                                    (+ space) (+ (not space))
+                                    (+ space) (group (* nonl)))
+                                nil t)
+        (push (match-string 1) result)
+        (put-text-property 0 1 'pcomplete-annotation
+                           (concat " " (match-string 2))
+                           (car result))
+        (put-text-property 0 1 'pcomplete-description
+                           (match-string 3)
+                           (car result)))
+      (nreverse result))))
+
+;;;###autoload
+(defun pcomplete/systemctl ()
+  "Completion for the `systemctl' command."
+  (let ((subcmds (pcomplete-from-help
+                  "systemctl --help"
+                  :margin (rx bol "  " (group) alpha)
+                  :argument (rx (+ (any alpha ?-)))
+                  :metavar (rx (group (+ " " (>= 2 (any upper "[]|."))))))))
+    (while (not (member (pcomplete-arg 1) subcmds))
+      (if (string-prefix-p "-" (pcomplete-arg 0))
+          (pcomplete-here (pcomplete-from-help "systemctl --help"
+                                               :metavar "[^ ]+"
+                                               :separator " \\(\\)-"))
+        (pcomplete-here subcmds)))
+    (let ((subcmd (pcomplete-arg 1))
+          (context (if (member "--user" pcomplete-args) "--user" "--system")))
+      (while (pcase subcmd
+               ((guard (string-prefix-p "-" (pcomplete-arg 0)))
+                (pcomplete-here
+                 (pcomplete-from-help "systemctl --help")))
+               ;; TODO: suggest only relevant units to each subcommand
+               ("start"
+                (pcomplete-here
+                 (pcmpl-linux--systemd-units context "--state" 
"inactive,failed")))
+               ((or "restart" "stop")
+                (pcomplete-here
+                 (pcmpl-linux--systemd-units context "--state" "active")))
+               (_ (pcomplete-here
+                   (completion-table-in-turn
+                    (pcmpl-linux--systemd-units context "--all")
+                    (pcomplete-entries)))))))))
+
+;;;###autoload
+(defun pcomplete/journalctl ()
+  "Completion for the `journalctl' command."
+  (while (if (string-prefix-p "-" (pcomplete-arg 0))
+             (pcomplete-here (pcomplete-from-help "journalctl --help"
+                                                  :metavar "[^ ]+"
+                                                  :separator " \\(\\)-"))
+           (pcomplete-here (mapcar (lambda (s) (concat s "="))
+                                   (process-lines "journalctl" "--fields"))))))
+
 ;;; pcmpl-linux.el ends here
diff --git a/lisp/pcmpl-rpm.el b/lisp/pcmpl-rpm.el
index f7925d9d9e..ebb6b72600 100644
--- a/lisp/pcmpl-rpm.el
+++ b/lisp/pcmpl-rpm.el
@@ -21,7 +21,8 @@
 
 ;;; Commentary:
 
-;; These functions provide completion rules for the `rpm' command.
+;; These functions provide completion rules for the `rpm' command and
+;; related tools.
 
 ;;; Code:
 
@@ -378,6 +379,46 @@
        (t
        (error "You must select a mode: -q, -i, -U, --verify, etc"))))))
 
+;;; DNF
+
+(defvar pcmpl-rpm-dnf-cache-file "/var/cache/dnf/packages.db"
+  "Location of the DNF cache.")
+
+(defun pcmpl-rpm--dnf-packages (status)
+  (when (and (file-exists-p pcmpl-rpm-dnf-cache-file)
+             (executable-find "sqlite3"))
+    (with-temp-message
+        "Getting list of packages..."
+      (process-lines "sqlite3" "-batch" "-init" "/dev/null"
+                     pcmpl-rpm-dnf-cache-file
+                     (pcase-exhaustive status
+                       ('available "select pkg from available")
+                       ('installed "select pkg from installed")
+                       ('not-installed "\
+select pkg from available where pkg not in (select pkg from installed)"))))))
+
+;;;###autoload
+(defun pcomplete/dnf ()
+  "Completion for the `dnf' command."
+  (let ((subcmds (pcomplete-from-help "dnf help"
+                                      :margin "^\\(\\)[a-z-]+  "
+                                      :argument "[a-z-]+")))
+    (while (not (member (pcomplete-arg 1) subcmds))
+      (pcomplete-here (completion-table-merge
+                       subcmds
+                       (pcomplete-from-help "dnf help"))))
+    (let ((subcmd (pcomplete-arg 1)))
+      (while (pcase subcmd
+               ((guard (pcomplete-match "\\`-" 0))
+                (pcomplete-here
+                 (pcomplete-from-help `("dnf" "help" ,subcmd))))
+               ((or "downgrade" "reinstall" "remove")
+                (pcomplete-here (pcmpl-rpm--dnf-packages 'installed)))
+               ((or "install" "mark" "reinstall" "upgrade")
+                (pcomplete-here (pcmpl-rpm--dnf-packages 'not-installed)))
+               ((or "builddep" "changelog" "info" "list" "repoquery" 
"updateinfo")
+                (pcomplete-here (pcmpl-rpm--dnf-packages 'available))))))))
+
 (provide 'pcmpl-rpm)
 
 ;;; pcmpl-rpm.el ends here
diff --git a/lisp/pcmpl-unix.el b/lisp/pcmpl-unix.el
index 8774f091c8..0c32f814d0 100644
--- a/lisp/pcmpl-unix.el
+++ b/lisp/pcmpl-unix.el
@@ -25,7 +25,7 @@
 
 (require 'pcomplete)
 
-;; User Variables:
+;;; User Variables
 
 (defcustom pcmpl-unix-group-file "/etc/group"
   "If non-nil, a string naming the group file on your system."
@@ -56,7 +56,7 @@ being via `pcmpl-ssh-known-hosts-file'."
   :group 'pcmpl-unix
   :version "24.1")
 
-;; Functions:
+;;; Shell builtins and core utilities
 
 ;;;###autoload
 (defun pcomplete/cd ()
@@ -69,34 +69,38 @@ being via `pcmpl-ssh-known-hosts-file'."
 ;;;###autoload
 (defun pcomplete/rmdir ()
   "Completion for `rmdir'."
-  (while (pcomplete-here (pcomplete-dirs))))
+  (while (if (string-prefix-p "-" (pcomplete-arg))
+             (pcomplete-here (pcomplete-from-help "rmdir --help"))
+           (pcomplete-here (pcomplete-dirs)))))
 
 ;;;###autoload
 (defun pcomplete/rm ()
-  "Completion for `rm'."
-  (let ((pcomplete-help "(fileutils)rm invocation"))
-    (pcomplete-opt "dfirRv")
-    (while (pcomplete-here (pcomplete-all-entries) nil
-                          #'expand-file-name))))
+  "Completion for the `rm' command."
+  (pcomplete-here-using-help "rm --help"))
 
 ;;;###autoload
 (defun pcomplete/xargs ()
   "Completion for `xargs'."
   (while (string-prefix-p "-" (pcomplete-arg 0))
-    (pcomplete-here (funcall pcomplete-default-completion-function)))
+    (pcomplete-here (pcomplete-from-help "xargs --help"))
+    (when (pcomplete-match "\\`-[adEIiLnPs]\\'") (pcomplete-here)))
   (funcall pcomplete-command-completion-function)
   (funcall (or (pcomplete-find-completion-function (pcomplete-arg 1))
               pcomplete-default-completion-function)))
 
-;; FIXME: Add completion of sudo-specific arguments.
-(defalias 'pcomplete/sudo #'pcomplete/xargs)
-
 ;;;###autoload
-(defalias 'pcomplete/time 'pcomplete/xargs)
+(defun pcomplete/time ()
+  "Completion for the `time' command."
+  (pcomplete-opt "p")
+  (funcall pcomplete-command-completion-function)
+  (funcall (or (pcomplete-find-completion-function (pcomplete-arg 1))
+              pcomplete-default-completion-function)))
 
 ;;;###autoload
 (defun pcomplete/which ()
   "Completion for `which'."
+  (while (string-prefix-p "-" (pcomplete-arg 0))
+    (pcomplete-here (pcomplete-from-help "which --help")))
   (while (pcomplete-here (funcall pcomplete-command-completion-function))))
 
 (defun pcmpl-unix-read-passwd-file (file)
@@ -128,25 +132,455 @@ documentation), this function returns nil."
   (if pcmpl-unix-passwd-file
       (pcmpl-unix-read-passwd-file pcmpl-unix-passwd-file)))
 
+;;;###autoload
+(defun pcomplete/cat ()
+  "Completion for the `cat' command."
+  (pcomplete-here-using-help "cat --help"))
+
+;;;###autoload
+(defun pcomplete/tac ()
+  "Completion for the `tac' command."
+  (pcomplete-here-using-help "tac --help"))
+
+;;;###autoload
+(defun pcomplete/nl ()
+  "Completion for the `nl' command."
+  (pcomplete-here-using-help "nl --help"))
+
+;;;###autoload
+(defun pcomplete/od ()
+  "Completion for the `od' command."
+  (pcomplete-here-using-help "od --help"))
+
+;;;###autoload
+(defun pcomplete/base32 ()
+  "Completion for the `base32' and `base64' commands."
+  (pcomplete-here-using-help "base32 --help"))
+;;;###autoload
+(defalias 'pcomplete/base64 'pcomplete/base32)
+
+;;;###autoload
+(defun pcomplete/basenc ()
+  "Completion for the `basenc' command."
+  (pcomplete-here-using-help "basenc --help"))
+
+;;;###autoload
+(defun pcomplete/fmt ()
+  "Completion for the `fmt' command."
+  (pcomplete-here-using-help "fmt --help"))
+
+;;;###autoload
+(defun pcomplete/pr ()
+  "Completion for the `pr' command."
+  (pcomplete-here-using-help "pr --help"))
+
+;;;###autoload
+(defun pcomplete/fold ()
+  "Completion for the `fold' command."
+  (pcomplete-here-using-help "fold --help"))
+
+;;;###autoload
+(defun pcomplete/head ()
+  "Completion for the `head' command."
+  (pcomplete-here-using-help "head --help"))
+
+;;;###autoload
+(defun pcomplete/tail ()
+  "Completion for the `tail' command."
+  (pcomplete-here-using-help "tail --help"))
+
+;;;###autoload
+(defun pcomplete/split ()
+  "Completion for the `split' command."
+  (pcomplete-here-using-help "split --help"))
+
+;;;###autoload
+(defun pcomplete/csplit ()
+  "Completion for the `csplit' command."
+  (pcomplete-here-using-help "csplit --help"))
+
+;;;###autoload
+(defun pcomplete/wc ()
+  "Completion for the `wc' command."
+  (pcomplete-here-using-help "wc --help"))
+
+;;;###autoload
+(defun pcomplete/sum ()
+  "Completion for the `sum' command."
+  (pcomplete-here-using-help "sum --help"))
+
+;;;###autoload
+(defun pcomplete/cksum ()
+  "Completion for the `cksum' command."
+  (pcomplete-here-using-help "cksum --help"))
+
+;;;###autoload
+(defun pcomplete/b2sum ()
+  "Completion for the `b2sum' command."
+  (pcomplete-here-using-help "b2sum --help"))
+
+;;;###autoload
+(defun pcomplete/md5sum ()
+  "Completion for checksum commands."
+  (pcomplete-here-using-help "md5sum --help"))
+;;;###autoload(defalias 'pcomplete/sha1sum 'pcomplete/md5sum)
+;;;###autoload(defalias 'pcomplete/sha224sum 'pcomplete/md5sum)
+;;;###autoload(defalias 'pcomplete/sha256sum 'pcomplete/md5sum)
+;;;###autoload(defalias 'pcomplete/sha384sum 'pcomplete/md5sum)
+;;;###autoload(defalias 'pcomplete/sha521sum 'pcomplete/md5sum)
+
+;;;###autoload
+(defun pcomplete/sort ()
+  "Completion for the `sort' command."
+  (pcomplete-here-using-help "sort --help"))
+
+;;;###autoload
+(defun pcomplete/shuf ()
+  "Completion for the `shuf' command."
+  (pcomplete-here-using-help "shuf --help"))
+
+;;;###autoload
+(defun pcomplete/uniq ()
+  "Completion for the `uniq' command."
+  (pcomplete-here-using-help "uniq --help"))
+
+;;;###autoload
+(defun pcomplete/comm ()
+  "Completion for the `comm' command."
+  (pcomplete-here-using-help "comm --help"))
+
+;;;###autoload
+(defun pcomplete/ptx ()
+  "Completion for the `ptx' command."
+  (pcomplete-here-using-help "ptx --help"))
+
+;;;###autoload
+(defun pcomplete/tsort ()
+  "Completion for the `tsort' command."
+  (pcomplete-here-using-help "tsort --help"))
+
+;;;###autoload
+(defun pcomplete/cut ()
+  "Completion for the `cut' command."
+  (pcomplete-here-using-help "cut --help"))
+
+;;;###autoload
+(defun pcomplete/paste ()
+  "Completion for the `paste' command."
+  (pcomplete-here-using-help "paste --help"))
+
+;;;###autoload
+(defun pcomplete/join ()
+  "Completion for the `join' command."
+  (pcomplete-here-using-help "join --help"))
+
+;;;###autoload
+(defun pcomplete/tr ()
+  "Completion for the `tr' command."
+  (pcomplete-here-using-help "tr --help"))
+
+;;;###autoload
+(defun pcomplete/expand ()
+  "Completion for the `expand' command."
+  (pcomplete-here-using-help "expand --help"))
+
+;;;###autoload
+(defun pcomplete/unexpand ()
+  "Completion for the `unexpand' command."
+  (pcomplete-here-using-help "unexpand --help"))
+
+;;;###autoload
+(defun pcomplete/ls ()
+  "Completion for the `ls' command."
+  (pcomplete-here-using-help "ls --help"))
+;;;###autoload(defalias 'pcomplete/dir 'pcomplete/ls)
+;;;###autoload(defalias 'pcomplete/vdir 'pcomplete/ls)
+
+;;;###autoload
+(defun pcomplete/cp ()
+  "Completion for the `cp' command."
+  (pcomplete-here-using-help "cp --help"))
+
+;;;###autoload
+(defun pcomplete/dd ()
+  "Completion for the `dd' command."
+  (let ((operands (pcomplete-from-help "dd --help"
+                                       :argument "[a-z]+="
+                                       :narrow-start "\n\n"
+                                       :narrow-end "\n\n")))
+    (while
+        (cond ((pcomplete-match "\\`[io]f=\\(.*\\)" 0)
+               (pcomplete-here (pcomplete-entries)
+                               (pcomplete-match-string 1 0)))
+              (t (pcomplete-here operands))))))
+
+;;;###autoload
+(defun pcomplete/install ()
+  "Completion for the `install' command."
+  (pcomplete-here-using-help "install --help"))
+
+;;;###autoload
+(defun pcomplete/mv ()
+  "Completion for the `mv' command."
+  (pcomplete-here-using-help "mv --help"))
+
+;;;###autoload
+(defun pcomplete/shred ()
+  "Completion for the `shred' command."
+  (pcomplete-here-using-help "shred --help"))
+
+;;;###autoload
+(defun pcomplete/ln ()
+  "Completion for the `ln' command."
+  (pcomplete-here-using-help "ln --help"))
+
+;;;###autoload
+(defun pcomplete/mkdir ()
+  "Completion for the `mkdir' command."
+  (pcomplete-here-using-help "mkdir --help"))
+
+;;;###autoload
+(defun pcomplete/mkfifo ()
+  "Completion for the `mkfifo' command."
+  (pcomplete-here-using-help "mkfifo --help"))
+
+;;;###autoload
+(defun pcomplete/mknod ()
+  "Completion for the `mknod' command."
+  (pcomplete-here-using-help "mknod --help"))
+
+;;;###autoload
+(defun pcomplete/readlink ()
+  "Completion for the `readlink' command."
+  (pcomplete-here-using-help "readlink --help"))
+
 ;;;###autoload
 (defun pcomplete/chown ()
   "Completion for the `chown' command."
-  (unless (pcomplete-match "\\`-")
-    (if (pcomplete-match "\\`[^.]*\\'" 0)
-       (pcomplete-here* (pcmpl-unix-user-names))
-      (if (pcomplete-match "\\.\\([^.]*\\)\\'" 0)
-         (pcomplete-here* (pcmpl-unix-group-names)
-                          (pcomplete-match-string 1 0))
-       (pcomplete-here*))))
+  (while (pcomplete-match "\\`-" 0)
+    (pcomplete-here (pcomplete-from-help "chown --help")))
+  (if (pcomplete-match "\\`[^.]*\\'" 0)
+      (pcomplete-here* (pcmpl-unix-user-names))
+    (if (pcomplete-match "\\.\\([^.]*\\)\\'" 0)
+       (pcomplete-here* (pcmpl-unix-group-names)
+                        (pcomplete-match-string 1 0))
+      (pcomplete-here*)))
   (while (pcomplete-here (pcomplete-entries))))
 
 ;;;###autoload
 (defun pcomplete/chgrp ()
   "Completion for the `chgrp' command."
-  (unless (pcomplete-match "\\`-")
-    (pcomplete-here* (pcmpl-unix-group-names)))
+  (while (pcomplete-match "\\`-" 0)
+    (pcomplete-here (pcomplete-from-help "chgrp --help")))
+  (pcomplete-here* (pcmpl-unix-group-names))
   (while (pcomplete-here (pcomplete-entries))))
 
+;;;###autoload
+(defun pcomplete/chmod ()
+  "Completion for the `chmod' command."
+  (pcomplete-here-using-help "chmod --help"))
+
+;;;###autoload
+(defun pcomplete/touch ()
+  "Completion for the `touch' command."
+  (pcomplete-here-using-help "touch --help"))
+
+;;;###autoload
+(defun pcomplete/df ()
+  "Completion for the `df' command."
+  (pcomplete-here-using-help "df --help"))
+
+;;;###autoload
+(defun pcomplete/du ()
+  "Completion for the `du' command."
+  (pcomplete-here-using-help "du --help"))
+
+;;;###autoload
+(defun pcomplete/stat ()
+  "Completion for the `stat' command."
+  (pcomplete-here-using-help "stat --help"))
+
+;;;###autoload
+(defun pcomplete/sync ()
+  "Completion for the `sync' command."
+  (pcomplete-here-using-help "sync --help"))
+
+;;;###autoload
+(defun pcomplete/truncate ()
+  "Completion for the `truncate' command."
+  (pcomplete-here-using-help "truncate --help"))
+
+;;;###autoload
+(defun pcomplete/echo ()
+  "Completion for the `echo' command."
+  (pcomplete-here-using-help '("echo" "--help")))
+
+;;;###autoload
+(defun pcomplete/test ()
+  "Completion for the `test' command."
+  (pcomplete-here-using-help '("[" "--help")
+                             :margin "^ +\\([A-Z]+1 \\)?"))
+;;;###autoload(defalias (intern "pcomplete/[") 'pcomplete/test)
+
+;;;###autoload
+(defun pcomplete/tee ()
+  "Completion for the `tee' command."
+  (pcomplete-here-using-help "tee --help"))
+
+;;;###autoload
+(defun pcomplete/basename ()
+  "Completion for the `basename' command."
+  (pcomplete-here-using-help "basename --help"))
+
+;;;###autoload
+(defun pcomplete/dirname ()
+  "Completion for the `dirname' command."
+  (pcomplete-here-using-help "dirname --help"))
+
+;;;###autoload
+(defun pcomplete/pathchk ()
+  "Completion for the `pathchk' command."
+  (pcomplete-here-using-help "pathchk --help"))
+
+;;;###autoload
+(defun pcomplete/mktemp ()
+  "Completion for the `mktemp' command."
+  (pcomplete-here-using-help "mktemp --help"))
+
+;;;###autoload
+(defun pcomplete/realpath ()
+  "Completion for the `realpath' command."
+  (pcomplete-here-using-help "realpath --help"))
+
+;;;###autoload
+(defun pcomplete/id ()
+  "Completion for the `id' command."
+  (while (string-prefix-p "-" (pcomplete-arg 0))
+    (pcomplete-here (pcomplete-from-help "id --help")))
+  (while (pcomplete-here (pcmpl-unix-user-names))))
+
+;;;###autoload
+(defun pcomplete/groups ()
+  "Completion for the `groups' command."
+  (while (pcomplete-here (pcmpl-unix-user-names))))
+
+;;;###autoload
+(defun pcomplete/who ()
+  "Completion for the `who' command."
+  (pcomplete-here-using-help "who --help"))
+
+;;;###autoload
+(defun pcomplete/date ()
+  "Completion for the `date' command."
+  (pcomplete-here-using-help "date --help"))
+
+;;;###autoload
+(defun pcomplete/nproc ()
+  "Completion for the `nproc' command."
+  (pcomplete-here-using-help "nproc --help"))
+
+;;;###autoload
+(defun pcomplete/uname ()
+  "Completion for the `uname' command."
+  (pcomplete-here-using-help "uname --help"))
+
+;;;###autoload
+(defun pcomplete/hostname ()
+  "Completion for the `hostname' command."
+  (pcomplete-here-using-help "hostname --help"))
+
+;;;###autoload
+(defun pcomplete/uptime ()
+  "Completion for the `uptime' command."
+  (pcomplete-here-using-help "uptime --help"))
+
+;;;###autoload
+(defun pcomplete/chcon ()
+  "Completion for the `chcon' command."
+  (pcomplete-here-using-help "chcon --help"))
+
+;;;###autoload
+(defun pcomplete/runcon ()
+  "Completion for the `runcon' command."
+  (while (string-prefix-p "-" (pcomplete-arg 0))
+    (pcomplete-here (pcomplete-from-help "runcon --help"))
+    (when (pcomplete-match "\\`-[turl]\\'" 0) (pcomplete-here)))
+  (funcall pcomplete-command-completion-function)
+  (funcall (or (pcomplete-find-completion-function (pcomplete-arg 1))
+              pcomplete-default-completion-function)))
+
+;;;###autoload
+(defun pcomplete/chroot ()
+  "Completion for the `chroot' command."
+  (while (string-prefix-p "-" (pcomplete-arg 0))
+    (pcomplete-here (pcomplete-from-help "chroot --help")))
+  (pcomplete-here (pcomplete-dirs))
+  (funcall pcomplete-command-completion-function)
+  (funcall (or (pcomplete-find-completion-function (pcomplete-arg 1))
+              pcomplete-default-completion-function)))
+
+;;;###autoload
+(defun pcomplete/env ()
+  "Completion for the `env' command."
+  (while (string-prefix-p "-" (pcomplete-arg 0))
+    (pcomplete-here (pcomplete-from-help "env --help"))
+    (when (pcomplete-match "\\`-[uCS]\\'") (pcomplete-here)))
+  (while (pcomplete-match "=" 0) (pcomplete-here)) ; FIXME: Complete env vars
+  (funcall pcomplete-command-completion-function)
+  (funcall (or (pcomplete-find-completion-function (pcomplete-arg 1))
+              pcomplete-default-completion-function)))
+
+;;;###autoload
+(defun pcomplete/nice ()
+  "Completion for the `nice' command."
+  (while (string-prefix-p "-" (pcomplete-arg 0))
+    (pcomplete-here (pcomplete-from-help "nice --help"))
+    (pcomplete-here))
+  (funcall pcomplete-command-completion-function)
+  (funcall (or (pcomplete-find-completion-function (pcomplete-arg 1))
+              pcomplete-default-completion-function)))
+
+;;;###autoload
+(defun pcomplete/nohup ()
+  "Completion for the `nohup' command."
+  (while (string-prefix-p "-" (pcomplete-arg 0))
+    (pcomplete-here (pcomplete-from-help "nohup --help")))
+  (funcall pcomplete-command-completion-function)
+  (funcall (or (pcomplete-find-completion-function (pcomplete-arg 1))
+              pcomplete-default-completion-function)))
+
+;;;###autoload
+(defun pcomplete/stdbuf ()
+  "Completion for the `stdbuf' command."
+  (while (string-prefix-p "-" (pcomplete-arg 0))
+    (pcomplete-here (pcomplete-from-help "stdbuf --help"))
+    (when (pcomplete-match "\\`-[ioe]\\'") (pcomplete-here)))
+  (funcall pcomplete-command-completion-function)
+  (funcall (or (pcomplete-find-completion-function (pcomplete-arg 1))
+              pcomplete-default-completion-function)))
+
+;;;###autoload
+(defun pcomplete/timeout ()
+  "Completion for the `timeout' command."
+  (while (string-prefix-p "-" (pcomplete-arg 0))
+    (pcomplete-here (pcomplete-from-help "timeout --help"))
+    (when (pcomplete-match "\\`-[ks]\\'") (pcomplete-here)))
+  (pcomplete-here)                      ; eat DURATION argument
+  (funcall pcomplete-command-completion-function)
+  (funcall (or (pcomplete-find-completion-function (pcomplete-arg 1))
+              pcomplete-default-completion-function)))
+
+;;;###autoload
+(defun pcomplete/numfmt ()
+  "Completion for the `numfmt' command."
+  (pcomplete-here-using-help "numfmt --help"))
+
+;;;###autoload
+(defun pcomplete/seq ()
+  "Completion for the `seq' command."
+  (pcomplete-here-using-help "seq --help"))
+
+;;; Network commands
 
 ;; ssh support by Phil Hagelberg.
 ;; https://www.emacswiki.org/cgi-bin/wiki/pcmpl-ssh.el
@@ -239,6 +673,18 @@ Includes files as well as host names followed by a colon."
   (pcomplete-opt "xl(pcmpl-unix-user-names)")
   (pcmpl-unix-complete-hostname))
 
+;;; Miscellaneous
+
+;;;###autoload
+(defun pcomplete/sudo ()
+  "Completion for the `sudo' command."
+  (while (string-prefix-p "-" (pcomplete-arg 0))
+    (pcomplete-here (pcomplete-from-help "sudo --help"))
+    (when (pcomplete-match "\\`-[CDghpRtTUu]\\'") (pcomplete-here)))
+  (funcall pcomplete-command-completion-function)
+  (funcall (or (pcomplete-find-completion-function (pcomplete-arg 1))
+              pcomplete-default-completion-function)))
+
 (provide 'pcmpl-unix)
 
 ;;; pcmpl-unix.el ends here
diff --git a/lisp/pcmpl-x.el b/lisp/pcmpl-x.el
index 261a3d4e27..1ede867c5f 100644
--- a/lisp/pcmpl-x.el
+++ b/lisp/pcmpl-x.el
@@ -28,6 +28,22 @@
 (eval-when-compile (require 'cl-lib))
 (require 'pcomplete)
 
+;;; TeX
+
+;;;###autoload
+(defun pcomplete/tex ()
+  "Completion for the `tex' command."
+  (pcomplete-here-using-help "tex --help"
+                             :margin "^\\(?:\\[-no\\]\\)?\\(\\)-"))
+;;;###autoload(defalias 'pcomplete/pdftex 'pcomplete/tex)
+;;;###autoload(defalias 'pcomplete/latex 'pcomplete/tex)
+;;;###autoload(defalias 'pcomplete/pdflatex 'pcomplete/tex)
+
+;;;###autoload
+(defun pcomplete/luatex ()
+  "Completion for the `luatex' command."
+  (pcomplete-here-using-help "luatex --help"))
+;;;###autoload(defalias 'pcomplete/lualatex 'pcomplete/luatex)
 
 ;;;; tlmgr - https://www.tug.org/texlive/tlmgr.html
 
@@ -142,6 +158,12 @@
         (unless (pcomplete-match "^--" 0)
           (pcomplete-here* (pcomplete-dirs-or-entries)))))))
 
+;;; Grep-like tools
+
+;;;###autoload
+(defun pcomplete/rg ()
+  "Completion for the `rg' command."
+  (pcomplete-here-using-help "rg --help"))
 
 ;;;; ack - https://betterthangrep.com
 
@@ -288,6 +310,8 @@ long options."
                                     (pcmpl-x-ag-options))))
       (pcomplete-here* (pcomplete-dirs-or-entries)))))
 
+;;; Borland
+
 ;;;###autoload
 (defun pcomplete/bcc32 ()
   "Completion function for Borland's C++ compiler."
@@ -321,5 +345,24 @@ long options."
 ;;;###autoload
 (defalias 'pcomplete/bcc 'pcomplete/bcc32)
 
+;;; Network tools
+
+;;;###autoload
+(defun pcomplete/rclone ()
+  "Completion for the `rclone' command."
+  (let ((subcmds (pcomplete-from-help "rclone help"
+                                      :margin "^  "
+                                      :argument "[a-z]+"
+                                      :narrow-start "\n\n")))
+    (while (not (member (pcomplete-arg 1) subcmds))
+      (pcomplete-here (completion-table-merge
+                       subcmds
+                       (pcomplete-from-help "rclone help flags"))))
+    (let ((subcmd (pcomplete-arg 1)))
+      (while (if (pcomplete-match "\\`-" 0)
+                 (pcomplete-here (pcomplete-from-help
+                                  `("rclone" ,subcmd "--help")))
+               (pcomplete-here (pcomplete-entries)))))))
+
 (provide 'pcmpl-x)
 ;;; pcmpl-x.el ends here
diff --git a/lisp/pcomplete.el b/lisp/pcomplete.el
index 15b9880df8..6fe29d9dcf 100644
--- a/lisp/pcomplete.el
+++ b/lisp/pcomplete.el
@@ -119,6 +119,9 @@
 ;;; Code:
 
 (require 'comint)
+(eval-when-compile
+  (require 'cl-lib)
+  (require 'rx))
 
 (defgroup pcomplete nil
   "Programmable completion."
@@ -155,9 +158,6 @@ This mirrors the optional behavior of tcsh.
 A non-nil value is useful if `pcomplete-autolist' is non-nil too."
   :type 'boolean)
 
-(define-obsolete-variable-alias
-  'pcomplete-arg-quote-list 'comint-file-name-quote-list "24.3")
-
 (defcustom pcomplete-man-function #'man
   "A function to that will be called to display a manual page.
 It will be passed the name of the command to document."
@@ -364,11 +364,10 @@ modified to be an empty string, or the desired separation 
string."
 
 ;;; Alternative front-end using the standard completion facilities.
 
-;; The way pcomplete-parse-arguments, pcomplete-stub, and
-;; pcomplete-quote-argument work only works because of some deep
-;; hypothesis about the way the completion work.  Basically, it makes
-;; it pretty much impossible to have completion other than
-;; prefix-completion.
+;; The way pcomplete-parse-arguments and pcomplete-stub work only
+;; works because of some deep hypothesis about the way the completion
+;; work.  Basically, it makes it pretty much impossible to have
+;; completion other than prefix-completion.
 ;;
 ;; pcomplete--common-suffix and completion-table-subvert try to work around
 ;; this difficulty with heuristics, but it's really a hack.
@@ -485,6 +484,14 @@ Same as `pcomplete' but using the standard completion UI."
           (when completion-ignore-case
             (setq table (completion-table-case-fold table)))
           (list beg (point) table
+                :annotation-function
+                (lambda (cand)
+                  (when (stringp cand)
+                    (get-text-property 0 'pcomplete-annotation cand)))
+                :company-docsig
+                (lambda (cand)
+                  (when (stringp cand)
+                    (get-text-property 0 'pcomplete-help cand)))
                 :predicate pred
                 :exit-function
                ;; If completion is finished, add a terminating space.
@@ -841,9 +848,6 @@ this is `comint-dynamic-complete-functions'."
              (throw 'pcompleted t)
            pcomplete-args))))))
 
-(define-obsolete-function-alias
-  'pcomplete-quote-argument #'comint-quote-filename "24.3")
-
 ;; file-system completion lists
 
 (defsubst pcomplete-dirs-or-entries (&optional regexp predicate)
@@ -1332,6 +1336,133 @@ If specific documentation can't be given, be generic."
       (pcomplete-read-hosts pcomplete-hosts-file 'pcomplete--host-name-cache
                    'pcomplete--host-name-cache-timestamp)))
 
+;;; Parsing help messages
+
+(defvar pcomplete-from-help (make-hash-table :test #'equal)
+  "Memoization table for function `pcomplete-from-help'.")
+
+(cl-defun pcomplete-from-help (command
+                               &rest args
+                               &key
+                               (margin (rx bol (+ " ")))
+                               (argument (rx "-" (+ (any "-" alnum)) (? "=")))
+                               (metavar (rx (? " ")
+                                            (or (+ (any alnum "_-"))
+                                                (seq "[" (+? nonl) "]")
+                                                (seq "<" (+? nonl) ">")
+                                                (seq "{" (+? nonl) "}"))))
+                               (separator (rx ", " symbol-start))
+                               (description (rx (* nonl)
+                                                (* "\n" (>= 9 " ") (* nonl))))
+                               narrow-start
+                               narrow-end)
+  "Parse output of COMMAND into a list of completion candidates.
+
+COMMAND can be a string to be executed in a shell or a list of
+strings (program name and arguments).  It should print a help
+message.
+
+A list of arguments is collected after each match of MARGIN.
+Each argument should match ARGUMENT, possibly followed by a match
+of METAVAR.  If a match of SEPARATOR follows, then more
+argument-metavar pairs are collected.  Finally, a match of
+DESCRIPTION is collected.
+
+Keyword ARGS:
+
+MARGIN: regular expression after which argument descriptions are
+  to be found.  Parsing continues at the end of the first match
+  group or, failing that, the entire match.
+
+ARGUMENT: regular expression matching an argument name.  The
+  first match group (failing that, the entire match) is collected
+  as the argument name.  Parsing continues at the end of the
+  second matching group (failing that, the first group or entire
+  match).
+
+METAVAR: regular expression matching an argument parameter name.
+  The first match group (failing that, the entire match) is
+  collected as the parameter name and used as completion
+  annotation.  Parsing continues at the end of the second
+  matching group (failing that, the first group or entire match).
+
+SEPARATOR: regular expression matching the separator between
+  arguments.  Parsing continues at the end of the first match
+  group (failing that, the entire match).
+
+DESCRIPTION: regular expression matching the description of an
+  argument.  The first match group (failing that, the entire
+  match) is collected as the parameter name and used as
+  completion help.  Parsing continues at the end of the first
+  matching group (failing that, the entire match).
+
+NARROW-START, NARROW-END: if non-nil, parsing of the help message
+  is narrowed to the region between the end of the first match
+  group (failing that, the entire match) of these regular
+  expressions."
+  (with-memoization (gethash (cons command args) pcomplete-from-help)
+    (with-temp-buffer
+      (let ((case-fold-search nil)
+            (default-directory (expand-file-name "~/"))
+            (command (if (stringp command)
+                         (list shell-file-name
+                               shell-command-switch
+                               command)
+                       command))
+            i result)
+        (apply #'call-process (car command) nil t nil (cdr command))
+        (goto-char (point-min))
+        (narrow-to-region (or (and narrow-start
+                                   (re-search-forward narrow-start nil t)
+                                   (or (match-beginning 1) (match-beginning 
0)))
+                              (point-min))
+                          (or (and narrow-end
+                                   (re-search-forward narrow-end nil t)
+                                   (or (match-beginning 1) (match-beginning 
0)))
+                              (point-max)))
+        (goto-char (point-min))
+        (while (re-search-forward margin nil t)
+          (goto-char (or (match-end 1) (match-end 0)))
+          (setq i 0)
+          (while (and (or (zerop i)
+                          (and (looking-at separator)
+                               (goto-char (or (match-end 1)
+                                              (match-end 0)))))
+                      (looking-at argument))
+            (setq i (1+ i))
+            (goto-char (seq-some #'match-end '(2 1 0)))
+            (push (or (match-string 1) (match-string 0)) result)
+            (when (looking-at metavar)
+              (goto-char (seq-some #'match-end '(2 1 0)))
+              (put-text-property 0 1
+                                 'pcomplete-annotation
+                                 (or (match-string 1) (match-string 0))
+                                 (car result))))
+          (when (looking-at description)
+            (goto-char (seq-some #'match-end '(2 1 0)))
+            (let ((help (string-clean-whitespace
+                         (or (match-string 1) (match-string 0))))
+                  (items (take i result)))
+              (while items
+                (put-text-property 0 1 'pcomplete-help help
+                                   (pop items))))))
+        (nreverse result)))))
+
+(defun pcomplete-here-using-help (command &rest args)
+  "Perform completion for a simple command.
+Offer switches and directory entries as completion candidates.
+The switches are obtained by calling `pcomplete-from-help' with
+COMMAND and ARGS as arguments."
+  (while (cond
+          ((string= "--" (pcomplete-arg 1))
+           (while (pcomplete-here (pcomplete-entries))))
+          ((pcomplete-match "\\`--[^=]+=\\(.*\\)" 0)
+           (pcomplete-here (pcomplete-entries)
+                           (pcomplete-match-string 1 0)))
+          ((string-prefix-p "-" (pcomplete-arg 0))
+           (pcomplete-here (apply #'pcomplete-from-help command args)))
+          (t (pcomplete-here (pcomplete-entries))))))
+
 (provide 'pcomplete)
 
 ;;; pcomplete.el ends here
diff --git a/lisp/pixel-scroll.el b/lisp/pixel-scroll.el
index 167cb4fabe..10da9cb9ab 100644
--- a/lisp/pixel-scroll.el
+++ b/lisp/pixel-scroll.el
@@ -116,39 +116,37 @@ is always with pixel resolution.")
 
 (defvar mwheel-coalesce-scroll-events)
 
-(defvar pixel-scroll-precision-mode-map
-  (let ((map (make-sparse-keymap)))
-    (define-key map [wheel-down] 'pixel-scroll-precision)
-    (define-key map [wheel-up] 'pixel-scroll-precision)
-    (define-key map [touch-end] 'pixel-scroll-start-momentum)
-    (define-key map [mode-line wheel-down] 'pixel-scroll-precision)
-    (define-key map [mode-line wheel-up] 'pixel-scroll-precision)
-    (define-key map [mode-line touch-end] 'pixel-scroll-start-momentum)
-    (define-key map [header-line wheel-down] 'pixel-scroll-precision)
-    (define-key map [header-line wheel-up] 'pixel-scroll-precision)
-    (define-key map [header-line touch-end] 'pixel-scroll-start-momentum)
-    (define-key map [vertical-scroll-bar wheel-down] 'pixel-scroll-precision)
-    (define-key map [vertical-scroll-bar wheel-up] 'pixel-scroll-precision)
-    (define-key map [vertical-scroll-bar touch-end] 
'pixel-scroll-start-momentum)
-    (define-key map [tool-bar wheel-down] 'pixel-scroll-precision)
-    (define-key map [tool-bar wheel-up] 'pixel-scroll-precision)
-    (define-key map [tool-bar touch-end] 'pixel-scroll-start-momentum)
-    (define-key map [left-margin wheel-down] 'pixel-scroll-precision)
-    (define-key map [left-margin wheel-up] 'pixel-scroll-precision)
-    (define-key map [left-margin touch-end] 'pixel-scroll-start-momentum)
-    (define-key map [right-margin wheel-down] 'pixel-scroll-precision)
-    (define-key map [right-margin wheel-up] 'pixel-scroll-precision)
-    (define-key map [right-margin touch-end] 'pixel-scroll-start-momentum)
-    (define-key map [left-fringe wheel-down] 'pixel-scroll-precision)
-    (define-key map [left-fringe wheel-up] 'pixel-scroll-precision)
-    (define-key map [left-fringe touch-end] 'pixel-scroll-start-momentum)
-    (define-key map [right-fringe wheel-down] 'pixel-scroll-precision)
-    (define-key map [right-fringe wheel-up] 'pixel-scroll-precision)
-    (define-key map [right-fringe touch-end] 'pixel-scroll-start-momentum)
-    (define-key map [next] 'pixel-scroll-interpolate-down)
-    (define-key map [prior] 'pixel-scroll-interpolate-up)
-    map)
-  "The key map used by `pixel-scroll-precision-mode'.")
+(defvar-keymap pixel-scroll-precision-mode-map
+  :doc "The key map used by `pixel-scroll-precision-mode'."
+  "<wheel-down>"                       #'pixel-scroll-precision
+  "<wheel-up>"                         #'pixel-scroll-precision
+  "<touch-end>"                        #'pixel-scroll-start-momentum
+  "<mode-line> <wheel-down>"           #'pixel-scroll-precision
+  "<mode-line> <wheel-up>"             #'pixel-scroll-precision
+  "<mode-line> <touch-end>"            #'pixel-scroll-start-momentum
+  "<header-line> <wheel-down>"         #'pixel-scroll-precision
+  "<header-line> <wheel-up>"           #'pixel-scroll-precision
+  "<header-line> <touch-end>"          #'pixel-scroll-start-momentum
+  "<vertical-scroll-bar> <wheel-down>" #'pixel-scroll-precision
+  "<vertical-scroll-bar> <wheel-up>"   #'pixel-scroll-precision
+  "<vertical-scroll-bar> <touch-end>"  #'pixel-scroll-start-momentum
+  "<tool-bar> <wheel-down>"            #'pixel-scroll-precision
+  "<tool-bar> <wheel-up>"              #'pixel-scroll-precision
+  "<tool-bar> <touch-end>"             #'pixel-scroll-start-momentum
+  "<left-margin> <wheel-down>"         #'pixel-scroll-precision
+  "<left-margin> <wheel-up>"           #'pixel-scroll-precision
+  "<left-margin> <touch-end>"          #'pixel-scroll-start-momentum
+  "<right-margin> <wheel-down>"        #'pixel-scroll-precision
+  "<right-margin> <wheel-up>"          #'pixel-scroll-precision
+  "<right-margin> <touch-end>"         #'pixel-scroll-start-momentum
+  "<left-fringe> <wheel-down>"         #'pixel-scroll-precision
+  "<left-fringe> <wheel-up>"           #'pixel-scroll-precision
+  "<left-fringe> <touch-end>"          #'pixel-scroll-start-momentum
+  "<right-fringe> <wheel-down>"        #'pixel-scroll-precision
+  "<right-fringe> <wheel-up>"          #'pixel-scroll-precision
+  "<right-fringe> <touch-end>"         #'pixel-scroll-start-momentum
+  "<next>"                             #'pixel-scroll-interpolate-down
+  "<prior>"                            #'pixel-scroll-interpolate-up)
 
 (defcustom pixel-scroll-precision-use-momentum nil
   "If non-nil, continue to scroll the display after wheel movement stops.
@@ -195,7 +193,7 @@ Nil means to not interpolate such scrolls."
   :type 'float
   :version "29.1")
 
-(defcustom pixel-scroll-precision-interpolation-factor 4.0
+(defcustom pixel-scroll-precision-interpolation-factor 2.0
   "A factor to apply to the distance of an interpolated scroll."
   :group 'mouse
   :type 'float
@@ -605,19 +603,25 @@ the height of the current window."
       (when (< delta 0)
         (set-window-vscroll nil (- delta) t t)))))
 
-(defun pixel-scroll-precision-interpolate (delta &optional old-window)
+(defun pixel-scroll-precision-interpolate (delta &optional old-window factor)
   "Interpolate a scroll of DELTA pixels.
 OLD-WINDOW is the window which will be selected when redisplay
 takes place, or nil for the current window.  This results in the
-window being scrolled by DELTA pixels with an animation."
+window being scrolled by DELTA pixels with an animation.  FACTOR
+is a scale by which DELTA will be modified.  If nil, it defaults
+to `pixel-scroll-precision-interpolation-factor'."
   (let ((percentage 0)
         (total-time pixel-scroll-precision-interpolation-total-time)
-        (factor pixel-scroll-precision-interpolation-factor)
+        (factor (or factor pixel-scroll-precision-interpolation-factor))
         (last-time (float-time))
-        (time-elapsed 0.0)
+        (time-elapsed 0)
         (between-scroll pixel-scroll-precision-interpolation-between-scroll)
         (rem (window-parameter nil 'interpolated-scroll-remainder))
-        (time (window-parameter nil 'interpolated-scroll-remainder-time)))
+        (time (window-parameter nil 'interpolated-scroll-remainder-time))
+        (last-delta 0))
+    (unless (or (not rem) (eq (< delta 0) (< rem 0)))
+      ;; The direction changed.  Clear the remainder.
+      (setq rem nil))
     (when (and rem time
                (< (- (float-time) time) 1.0)
                (eq (< delta 0) (< rem 0)))
@@ -631,18 +635,19 @@ window being scrolled by DELTA pixels with an animation."
                                           (selected-window))
                   (redisplay t))
                 (sleep-for between-scroll)
-                (setq time-elapsed (+ time-elapsed
-                                      (- (float-time) last-time))
-                      percentage (/ time-elapsed total-time))
-                (let ((throw-on-input nil))
-                  (if (< delta 0)
-                      (pixel-scroll-precision-scroll-down
-                       (ceiling (abs (* (* delta factor)
-                                        (/ between-scroll total-time)))))
-                    (pixel-scroll-precision-scroll-up
-                     (ceiling (* (* delta factor)
-                                 (/ between-scroll total-time))))))
-                (setq last-time (float-time)))
+                (let ((time (float-time)))
+                  (setq time-elapsed (+ time-elapsed
+                                        (- time last-time))
+                        percentage (/ time-elapsed total-time))
+                  (let* ((throw-on-input nil)
+                         (absolute-delta (* (min 1 percentage) delta factor))
+                         (relative-delta (abs
+                                          (round (- absolute-delta 
last-delta)))))
+                    (setq last-delta absolute-delta)
+                    (if (< delta 0)
+                        (pixel-scroll-precision-scroll-down relative-delta)
+                      (pixel-scroll-precision-scroll-up relative-delta)))
+                  (setq last-time time)))
             (if (< percentage 1)
                 (progn
                   (set-window-parameter nil 'interpolated-scroll-remainder
@@ -821,14 +826,20 @@ It is a vector of the form [ VELOCITY TIME SIGN ]."
   "Interpolate a scroll downwards by one page."
   (interactive)
   (if pixel-scroll-precision-interpolate-page
-      (pixel-scroll-precision-interpolate (- (window-text-height nil t)))
+      (pixel-scroll-precision-interpolate (- (window-text-height nil t))
+                                          ;; Don't use an
+                                          ;; interpolation factor,
+                                          ;; since we want exactly 1
+                                          ;; page to be scrolled.
+                                          nil 1)
     (cua-scroll-up)))
 
 (defun pixel-scroll-interpolate-up ()
   "Interpolate a scroll upwards by one page."
   (interactive)
   (if pixel-scroll-precision-interpolate-page
-      (pixel-scroll-precision-interpolate (window-text-height nil t))
+      (pixel-scroll-precision-interpolate (window-text-height nil t)
+                                          nil 1)
     (cua-scroll-down)))
 
 ;;;###autoload
diff --git a/lisp/play/gamegrid.el b/lisp/play/gamegrid.el
index 8cff67c5bc..4e4982e7b0 100644
--- a/lisp/play/gamegrid.el
+++ b/lisp/play/gamegrid.el
@@ -80,12 +80,8 @@ directory will be used.")
 (defun gamegrid-calculate-glyph-size ()
   "Calculate appropriate glyph size in pixels based on display resolution.
 Return a multiple of 8 no less than 16."
-  (let (atts
+  (let ((atts (frame-monitor-attributes))
         y-pitch)
-    (dolist (mon (display-monitor-attributes-list))
-      (when-let ((frames (alist-get 'frames mon))
-                 (match (memq (selected-frame) frames)))
-        (setq atts mon)))
     (setq y-pitch (cond
                    (atts
                     (/ (nth 4 (assq 'geometry atts))
diff --git a/lisp/play/hanoi.el b/lisp/play/hanoi.el
index 58fb82b6ed..1a4b6dbeb1 100644
--- a/lisp/play/hanoi.el
+++ b/lisp/play/hanoi.el
@@ -149,10 +149,9 @@ BITS must be of length nrings.  Start at START-TIME."
   (setq show-trailing-whitespace nil)
   (unwind-protect
       (let*
-         (;; These lines can cause Emacs to crash if you ask for too
-          ;; many rings.  If you uncomment them, on most systems you
+         (;; This line can cause Emacs to crash if you ask for too
+          ;; many rings.  If you uncomment it, on most systems you
           ;; can get 10,000+ rings.
-          ;;(max-specpdl-size (max max-specpdl-size (* nrings 15)))
           ;;(max-lisp-eval-depth (max max-lisp-eval-depth (+ nrings 20)))
           (vert (not hanoi-horizontal-flag))
           (pole-width (length (format "%d" (max 0 (1- nrings)))))
diff --git a/lisp/printing.el b/lisp/printing.el
index d10de24e03..0654dcda3d 100644
--- a/lisp/printing.el
+++ b/lisp/printing.el
@@ -5546,13 +5546,11 @@ COMMAND.exe, COMMAND.bat and COMMAND.com in this order."
 (defvar pr-i-ps-send    'printer)
 
 
-(defvar pr-interface-map
-  (let ((map (make-sparse-keymap)))
-    (set-keymap-parent map widget-keymap)
-    (define-key map "q" 'pr-interface-quit)
-    (define-key map "?" 'pr-interface-help)
-    map)
-  "Keymap for `pr-interface'.")
+(defvar-keymap pr-interface-map
+  :doc "Keymap for `pr-interface'."
+  :parent widget-keymap
+  "q" #'pr-interface-quit
+  "?" #'pr-interface-help)
 
 (defmacro pr-interface-save (&rest body)
   `(with-current-buffer pr-i-buffer
diff --git a/lisp/proced.el b/lisp/proced.el
index a27638d367..a774f2dd1e 100644
--- a/lisp/proced.el
+++ b/lisp/proced.el
@@ -426,7 +426,14 @@ Important: the match ends just after the marker.")
   "Name of Proced Log buffer.")
 
 (defconst proced-help-string
-  "(n)ext, (p)revious, (m)ark, (u)nmark, (k)ill, (q)uit (type ? for more help)"
+  (concat "\\<proced-mode-map> "
+          "\\[next-line] next, "
+          "\\[previous-line] previous, "
+          "\\[proced-mark] mark, "
+          "\\[proced-unmark] unmark, "
+          "\\[proced-send-signal] kill, "
+          "\\[quit-window] quit "
+          "(type \\[proced-help] for more help)")
   "Help string for Proced.")
 
 (defconst proced-header-help-echo
@@ -445,60 +452,58 @@ Important: the match ends just after the marker.")
     (,(concat "^[" (char-to-string proced-marker-char) "]")
      ".+" (proced-move-to-goal-column) nil (0 'proced-marked))))
 
-(defvar proced-mode-map
-  (let ((km (make-sparse-keymap)))
-    ;; moving
-    (define-key km " " 'next-line)
-    (define-key km "n" 'next-line)
-    (define-key km "p" 'previous-line)
-    (define-key km "\C-n" 'next-line)
-    (define-key km "\C-p" 'previous-line)
-    (define-key km "\C-?" 'previous-line)
-    (define-key km [?\S-\ ] 'previous-line)
-    (define-key km [down] 'next-line)
-    (define-key km [up] 'previous-line)
-    ;; marking
-    (define-key km "d" 'proced-mark) ; Dired compatibility ("delete")
-    (define-key km "m" 'proced-mark)
-    (put 'proced-mark :advertised-binding "m")
-    (define-key km "u" 'proced-unmark)
-    (define-key km "\177" 'proced-unmark-backward)
-    (define-key km "M" 'proced-mark-all)
-    (define-key km "U" 'proced-unmark-all)
-    (define-key km "t" 'proced-toggle-marks)
-    (define-key km "C" 'proced-mark-children)
-    (define-key km "P" 'proced-mark-parents)
-    ;; filtering
-    (define-key km "f"  'proced-filter-interactive)
-    (define-key km [mouse-2] 'proced-refine)
-    (define-key km "\C-m" 'proced-refine)
-    ;; sorting
-    (define-key km "sc" 'proced-sort-pcpu)
-    (define-key km "sm" 'proced-sort-pmem)
-    (define-key km "sp" 'proced-sort-pid)
-    (define-key km "ss" 'proced-sort-start)
-    (define-key km "sS" 'proced-sort-interactive)
-    (define-key km "st" 'proced-sort-time)
-    (define-key km "su" 'proced-sort-user)
-    ;; similar to `Buffer-menu-sort-by-column'
-    (define-key km [header-line mouse-1] 'proced-sort-header)
-    (define-key km [header-line mouse-2] 'proced-sort-header)
-    (define-key km "T" 'proced-toggle-tree)
-    ;; formatting
-    (define-key km "F"  'proced-format-interactive)
-    ;; operate
-    (define-key km "o" 'proced-omit-processes)
-    (define-key km "x" 'proced-send-signal) ; Dired compatibility
-    (define-key km "k" 'proced-send-signal) ; kill processes
-    (define-key km "r" 'proced-renice) ; renice processes
-    ;; misc
-    (define-key km "h" 'describe-mode)
-    (define-key km "?" 'proced-help)
-    (define-key km [remap undo] 'proced-undo)
-    (define-key km [remap advertised-undo] 'proced-undo)
-    ;; Additional keybindings are inherited from `special-mode-map'
-    km)
-  "Keymap for Proced commands.")
+(defvar-keymap proced-mode-map
+  :doc "Keymap for Proced commands."
+  ;; moving
+  "SPC"       #'next-line
+  "n"         #'next-line
+  "p"         #'previous-line
+  "C-n"       #'next-line
+  "C-p"       #'previous-line
+  "S-SPC"     #'previous-line
+  "<down>"    #'next-line
+  "<up>"      #'previous-line
+  ;; marking
+  "d"         #'proced-mark ; Dired compatibility ("delete")
+  "m"         #'proced-mark
+  "u"         #'proced-unmark
+  "DEL"       #'proced-unmark-backward
+  "M"         #'proced-mark-all
+  "U"         #'proced-unmark-all
+  "t"         #'proced-toggle-marks
+  "C"         #'proced-mark-children
+  "P"         #'proced-mark-parents
+  ;; filtering
+  "f"         #'proced-filter-interactive
+  "<mouse-2>" #'proced-refine
+  "RET"       #'proced-refine
+  ;; sorting
+  "s c"       #'proced-sort-pcpu
+  "s m"       #'proced-sort-pmem
+  "s p"       #'proced-sort-pid
+  "s s"       #'proced-sort-start
+  "s S"       #'proced-sort-interactive
+  "s t"       #'proced-sort-time
+  "s u"       #'proced-sort-user
+  ;; similar to `Buffer-menu-sort-by-column'
+  "<header-line> <mouse-1>"   #'proced-sort-header
+  "<header-line> <mouse-2>"   #'proced-sort-header
+  "T"         #'proced-toggle-tree
+  ;; formatting
+  "F"         #'proced-format-interactive
+  ;; operate
+  "o"         #'proced-omit-processes
+  "x"         #'proced-send-signal ; Dired compatibility
+  "k"         #'proced-send-signal ; kill processes
+  "r"         #'proced-renice ; renice processes
+  ;; misc
+  "h"         #'describe-mode
+  "?"         #'proced-help
+  "<remap> <undo>"            #'proced-undo
+  "<remap> <advertised-undo>" #'proced-undo
+  ;; Additional keybindings are inherited from `special-mode-map'
+  )
+(put 'proced-mark :advertised-binding "m")
 
 (easy-menu-define proced-menu proced-mode-map
   "Proced Menu."
@@ -637,22 +642,27 @@ type \\<proced-mode-map>\\[proced-mark] to mark a process 
for later commands.
 Type \\[proced-send-signal] to send signals to marked processes.
 Type \\[proced-renice] to renice marked processes.
 
-The initial content of a listing is defined by the variable `proced-filter'
-and the variable `proced-format'.
-The variable `proced-filter' specifies which system processes are displayed.
-The variable `proced-format' specifies which attributes are displayed for
-each process.  Type \\[proced-filter-interactive] and 
\\[proced-format-interactive]
-to change the values of `proced-filter' and `proced-format'.
-The current value of the variable `proced-filter' is indicated in the
-mode line.
+The initial content of a listing is defined by the variable
+`proced-filter' and the variable `proced-format'.
+
+The variable `proced-filter' specifies which system processes are
+displayed.
+
+The variable `proced-format' specifies which attributes are
+displayed for each process.
+
+Type \\[proced-filter-interactive] and \\[proced-format-interactive] to \
+change the values of `proced-filter' and
+`proced-format'.  The current value of the variable
+`proced-filter' is indicated in the mode line.
 
 The sort order of Proced listings is defined by the variable `proced-sort'.
-Type \\[proced-sort-interactive] or click on a header in the header line
-to change the sort scheme.  The current sort scheme is indicated in the
-mode line, using \"+\" or \"-\" for ascending or descending sort order.
+Type \\[proced-sort-interactive] or click on a header in the header \
+line to change the sort scheme.
+The current sort scheme is indicated in the mode line, using
+\"+\" or \"-\" for ascending or descending sort order.
 
-Type \\[proced-toggle-tree] to toggle whether the listing is
-displayed as process tree.
+Type \\[proced-toggle-tree] to toggle whether the listing is displayed as 
process tree.
 
 Type \\[proced-toggle-auto-update] to automatically update the
 process list.  The time interval for updates can be configured
@@ -661,11 +671,11 @@ via `proced-auto-update-interval'.
 An existing Proced listing can be refined by typing \\[proced-refine].
 Refining an existing listing does not update the variable `proced-filter'.
 
-The attribute-specific rules for formatting, filtering, sorting, and refining
-are defined in `proced-grammar-alist'.
+The attribute-specific rules for formatting, filtering, sorting,
+and refining are defined in `proced-grammar-alist'.
 
-After displaying or updating a Proced buffer, Proced runs the normal hook
-`proced-post-display-hook'.
+After displaying or updating a Proced buffer, Proced runs the
+normal hook `proced-post-display-hook'.
 
 \\{proced-mode-map}"
   :interactive nil
@@ -1768,6 +1778,9 @@ The value returned is the value of the last form in BODY."
        (save-window-excursion
          ;; Analogous to `dired-pop-to-buffer'
          ;; Don't split window horizontally.  (Bug#1806)
+         ;; FIXME: `dired-pop-to-buffer' was removed and replaced with
+         ;;        `dired-mark-pop-up'.  Should we just use
+         ;;        `pop-to-buffer' here also?
          (display-buffer (current-buffer)
                          '(display-buffer-in-direction
                            (direction . bottom)
@@ -1977,7 +1990,7 @@ STRING is an overall summary of the failures."
   (proced-why)
   (if (eq last-command 'proced-help)
       (describe-mode)
-    (message proced-help-string)))
+    (message (substitute-command-keys proced-help-string))))
 
 (defun proced-undo ()
   "Undo in a Proced buffer.
diff --git a/lisp/progmodes/cc-align.el b/lisp/progmodes/cc-align.el
index e14f5b9058..7b45be3c5c 100644
--- a/lisp/progmodes/cc-align.el
+++ b/lisp/progmodes/cc-align.el
@@ -85,11 +85,14 @@ statement-cont.)
 Works with: topmost-intro-cont."
   (save-excursion
     (beginning-of-line)
-    (c-backward-syntactic-ws (c-langelem-pos langelem))
-    (if (and (memq (char-before) '(?} ?,))
-            (not (and c-overloadable-operators-regexp
-                      (c-after-special-operator-id))))
-       c-basic-offset)))
+    (unless (re-search-forward c-fun-name-substitute-key
+                              (c-point 'eol) t)
+      (beginning-of-line)
+      (c-backward-syntactic-ws (c-langelem-pos langelem))
+      (if (and (memq (char-before) '(?} ?,))
+              (not (and c-overloadable-operators-regexp
+                        (c-after-special-operator-id))))
+         c-basic-offset))))
 
 (defun c-lineup-gnu-DEFUN-intro-cont (langelem)
   "Line up the continuation lines of a DEFUN macro in the Emacs C source.
diff --git a/lisp/progmodes/cc-awk.el b/lisp/progmodes/cc-awk.el
index 57750a2b39..089fa9ffe4 100644
--- a/lisp/progmodes/cc-awk.el
+++ b/lisp/progmodes/cc-awk.el
@@ -56,6 +56,7 @@
 ;; Silence the byte compiler.
 (cc-bytecomp-defvar c-new-BEG)
 (cc-bytecomp-defvar c-new-END)
+(cc-bytecomp-defvar c-open-string-opener)
 (cc-bytecomp-defun c-restore-string-fences)
 (cc-bytecomp-defun c-clear-string-fences)
 
@@ -777,16 +778,20 @@
   ;; opener, t if it would be a division sign.
   ;;
   ;; This function does hidden buffer changes.
-  (search-forward-regexp c-awk-string-without-end-here-re nil t) ; a (possibly 
unterminated) string
-  (c-awk-set-string-regexp-syntax-table-properties
-   (match-beginning 0) (match-end 0))
-  (cond ((looking-at "\"")
-         (forward-char)
-         t)                             ; In AWK, ("15" / 5) gives 3 ;-)
-        ((looking-at "[\n\r]")          ; Unterminated string with EOL.
-         (forward-char)
-         nil)                           ; / on next line would start a regexp
-        (t nil)))                       ; Unterminated string at EOB
+  (let ((string-start (if (eq (char-after) ?_) (1+ (point)) (point))))
+    (search-forward-regexp c-awk-string-without-end-here-re nil t) ; a 
(possibly unterminated) string
+    (c-awk-set-string-regexp-syntax-table-properties
+     (match-beginning 0) (match-end 0))
+    (cond ((looking-at "\"")
+          (forward-char)
+          t)                           ; In AWK, ("15" / 5) gives 3 ;-)
+         ((looking-at "[\n\r]")        ; Unterminated string with EOL.
+          (setq c-open-string-opener string-start)
+          (forward-char)
+          nil)                         ; / on next line would start a regexp
+         (t                            ; Unterminated string at EOB
+          (setq c-open-string-opener string-start)
+          nil))))
 
 (defun c-awk-syntax-tablify-/ (anchor anchor-state-/div)
   ;; Point is at a /.  Determine whether this is a division sign or a regexp
diff --git a/lisp/progmodes/cc-defs.el b/lisp/progmodes/cc-defs.el
index f867625480..4f1a08cfa0 100644
--- a/lisp/progmodes/cc-defs.el
+++ b/lisp/progmodes/cc-defs.el
@@ -125,7 +125,7 @@ The result of the body appears to the compiler as a quoted 
constant.
 
 This variant works around bugs in `eval-when-compile' in various
 \(X)Emacs versions.  See cc-defs.el for details."
-    (declare (indent 0) (debug t))
+    (declare (indent 0) (debug (&rest def-form)))
     (if c-inside-eval-when-compile
        ;; XEmacs 21.4.6 has a bug in `eval-when-compile' in that it
        ;; evaluates its body at macro expansion time if it's nested
@@ -2629,6 +2629,20 @@ fallback definition for all modes, to break the cycle).")
 
 (defconst c-lang--novalue "novalue")
 
+(defmacro c-let*-maybe-max-specpdl-size (varlist &rest body)
+  ;; Like let*, but doesn't bind `max-specpdl-size' if that variable
+  ;; is in the bindings list and either doesn't exist or is obsolete.
+  (declare (debug let*) (indent 1))
+  (let ((-varlist- (copy-sequence varlist)) msp-binding)
+    (if (or (not (boundp 'max-specpdl-size))
+           (get 'max-specpdl-size 'byte-obsolete-variable))
+       (cond
+        ((memq 'max-specpdl-size -varlist-)
+         (setq -varlist- (delq 'max-specpdl-size -varlist-)))
+        ((setq msp-binding (assq 'max-specpdl-size -varlist-))
+         (setq -varlist- (delq msp-binding -varlist-)))))
+    `(let* ,-varlist- ,@body)))
+
 (defun c-get-lang-constant (name &optional source-files mode)
   ;; Used by `c-lang-const'.
 
@@ -2669,21 +2683,22 @@ fallback definition for all modes, to break the 
cycle).")
       ;; In that case we just continue with the "assignment" before
       ;; the one currently being evaluated, thereby creating the
       ;; illusion if a `setq'-like sequence of assignments.
-      (let* ((c-buffer-is-cc-mode mode)
-            (source-pos
-             (or (assq sym c-lang-constants-under-evaluation)
-                 (cons sym (vector source nil))))
-            ;; Append `c-lang-constants-under-evaluation' even if an
-            ;; earlier entry is found.  It's only necessary to get
-            ;; the recording of dependencies above correct.
-            (c-lang-constants-under-evaluation
-             (cons source-pos c-lang-constants-under-evaluation))
-            (fallback (get mode 'c-fallback-mode))
-            value
-            ;; Make sure the recursion limits aren't very low
-            ;; since the `c-lang-const' dependencies can go deep.
-            (max-specpdl-size (max max-specpdl-size 3000))
-            (max-lisp-eval-depth (max max-lisp-eval-depth 1000)))
+      (c-let*-maybe-max-specpdl-size
+         ((c-buffer-is-cc-mode mode)
+          (source-pos
+           (or (assq sym c-lang-constants-under-evaluation)
+               (cons sym (vector source nil))))
+          ;; Append `c-lang-constants-under-evaluation' even if an
+          ;; earlier entry is found.  It's only necessary to get
+          ;; the recording of dependencies above correct.
+          (c-lang-constants-under-evaluation
+           (cons source-pos c-lang-constants-under-evaluation))
+          (fallback (get mode 'c-fallback-mode))
+          value
+          ;; Make sure the recursion limits aren't very low
+          ;; since the `c-lang-const' dependencies can go deep.
+          (max-specpdl-size (max max-specpdl-size 3000))
+          (max-lisp-eval-depth (max max-lisp-eval-depth 1000)))
 
        (if (if fallback
                (let ((backup-source-pos (copy-sequence (cdr source-pos))))
diff --git a/lisp/progmodes/cc-engine.el b/lisp/progmodes/cc-engine.el
index b2d1f15d39..0ac96219a1 100644
--- a/lisp/progmodes/cc-engine.el
+++ b/lisp/progmodes/cc-engine.el
@@ -142,6 +142,11 @@
 ;;       Put on the brace which introduces a brace list and on the commas
 ;;       which separate the elements within it.
 ;;
+;; 'c-<>-c-types-set
+;;   This property is set on an opening angle bracket, and indicates that
+;;   any "," separators within the template/generic expression have been
+;;   marked with a 'c-type property value 'c-<>-arg-sep (see above).
+;;
 ;; 'c-awk-NL-prop
 ;;   Used in AWK mode to mark the various kinds of newlines.  See
 ;;   cc-awk.el.
@@ -6137,7 +6142,7 @@ comment at the start of cc-engine.el for more info."
                        (forward-char))))
          (backward-char)
          (if (let ((c-parse-and-markup-<>-arglists t)
-                   (c-restricted-<>-arglists t))
+                   c-restricted-<>-arglists)
                (c-forward-<>-arglist nil)) ; Should always work.
              (when (> (point) to)
                (setq bound-<> (point)))
@@ -8151,6 +8156,40 @@ multi-line strings (but not C++, for example)."
     (c-truncate-lit-pos-cache c-neutralize-pos)))
 
 
+(defun c-before-after-change-check-c++-modules (beg end &optional _old_len)
+  ;; Extend the region (c-new-BEG c-new-END) as needed to enclose complete
+  ;; C++20 module statements.  This function is called solely from
+  ;; `c-get-state-before-change-functions' and `c-before-font-lock-functions'
+  ;; as part of the before-change and after-change processing for C++.
+  ;;
+  ;; Point is undefined both on entry and exit, and the return value has no
+  ;; significance.
+  (c-save-buffer-state (res bos lit-start)
+    (goto-char end)
+    (if (setq lit-start (c-literal-start))
+       (goto-char lit-start))
+    (when (>= (point) beg)
+      (setq res (c-beginning-of-statement-1 nil t)) ; t is IGNORE-LABELS
+      (setq bos (point))
+      (when (and (memq res '(same previous))
+                (looking-at c-module-key))
+       (setq c-new-BEG (min c-new-BEG (point)))
+       (if (c-syntactic-re-search-forward
+            ";" (min (+ (point) 500) (point-max)) t)
+           (setq c-new-END (max c-new-END (point))))))
+    (when (or (not bos) (< beg bos))
+      (goto-char beg)
+      (when (not (c-literal-start))
+       (setq res (c-beginning-of-statement-1 nil t))
+       (setq bos (point))
+       (when (and (memq res '(same previous))
+                  (looking-at c-module-key))
+         (setq c-new-BEG (min c-new-BEG (point)))
+         (if (c-syntactic-re-search-forward
+              ";" (min (+ (point) 500) (point-max)) t)
+             (setq c-new-END (max c-new-END (point)))))))))
+
+
 ;; Handling of small scale constructs like types and names.
 
 ;; Dynamically bound variable that instructs `c-forward-type' to also
@@ -8469,25 +8508,40 @@ multi-line strings (but not C++, for example)."
        ;; recording of any found types that constitute an argument in
        ;; the arglist.
        (c-record-found-types (if c-record-type-identifiers t)))
-    (if (catch 'angle-bracket-arglist-escape
-         (setq c-record-found-types
-               (c-forward-<>-arglist-recur all-types)))
-       (progn
-         (when (consp c-record-found-types)
-           (let ((cur c-record-found-types))
-             (while (consp (car-safe cur))
-               (c-fontify-new-found-type
-                (buffer-substring-no-properties (caar cur) (cdar cur)))
-               (setq cur (cdr cur))))
-           (setq c-record-type-identifiers
-                 ;; `nconc' doesn't mind that the tail of
-                 ;; `c-record-found-types' is t.
-                 (nconc c-record-found-types c-record-type-identifiers)))
-         t)
-
-      (setq c-found-types old-found-types)
-      (goto-char start)
-      nil)))
+    ;; Special handling for C++20's "import <...>" operator.
+    (if (and (c-major-mode-is 'c++-mode)
+            (save-excursion
+              (and (zerop (c-backward-token-2))
+                   (looking-at "import\\>\\(?:[^_$]\\|$\\)"))))
+       (when (looking-at "<\\(?:\\\\.\\|[^\\\n\r\t>]\\)*\\(>\\)?")
+         (if (match-beginning 1)       ; A terminated <..>
+             (progn
+               (when c-parse-and-markup-<>-arglists
+                 (c-mark-<-as-paren (point))
+                 (c-mark->-as-paren (match-beginning 1))
+                 (c-truncate-lit-pos-cache (point)))
+               (goto-char (match-end 1))
+               t)
+           nil))
+      (if (catch 'angle-bracket-arglist-escape
+           (setq c-record-found-types
+                 (c-forward-<>-arglist-recur all-types)))
+         (progn
+           (when (consp c-record-found-types)
+             (let ((cur c-record-found-types))
+               (while (consp (car-safe cur))
+                 (c-fontify-new-found-type
+                  (buffer-substring-no-properties (caar cur) (cdar cur)))
+                 (setq cur (cdr cur))))
+             (setq c-record-type-identifiers
+                   ;; `nconc' doesn't mind that the tail of
+                   ;; `c-record-found-types' is t.
+                   (nconc c-record-found-types c-record-type-identifiers)))
+           t)
+
+       (setq c-found-types old-found-types)
+       (goto-char start)
+       nil))))
 
 (defun c-forward-<>-arglist-recur (all-types)
   ;; Recursive part of `c-forward-<>-arglist'.
@@ -8505,9 +8559,9 @@ multi-line strings (but not C++, for example)."
        arg-start-pos)
     ;; If the '<' has paren open syntax then we've marked it as an angle
     ;; bracket arglist before, so skip to the end.
-    (if (and (not c-parse-and-markup-<>-arglists)
-            syntax-table-prop-on-<)
-
+    (if (and syntax-table-prop-on-<
+            (or (not c-parse-and-markup-<>-arglists)
+                (c-get-char-property (point) 'c-<>-c-types-set)))
        (progn
          (forward-char)
          (if (and (c-go-up-list-forward)
@@ -8604,6 +8658,7 @@ multi-line strings (but not C++, for example)."
                               (c-unmark-<->-as-paren (point)))))
                      (c-mark-<-as-paren start)
                      (c-mark->-as-paren (1- (point)))
+                     (c-put-char-property start 'c-<>-c-types-set t)
                      (c-truncate-lit-pos-cache start))
                    (setq res t)
                    nil))               ; Exit the loop.
@@ -9457,17 +9512,151 @@ point unchanged and return nil."
 
 ;; Handling of large scale constructs like statements and declarations.
 
-(defun c-forward-declarator (&optional limit accept-anon)
+(defun c-forward-primary-expression (&optional limit)
+  ;; Go over the primary expression (if any) at point, moving to the next
+  ;; token and return non-nil.  If we're not at a primary expression leave
+  ;; point unchanged and return nil.
+  ;;
+  ;; Note that this function is incomplete, handling only those cases expected
+  ;; to be common in a C++20 requires clause.
+  (let ((here (point))
+       (c-restricted-<>-arglists t)
+       (c-parse-and-markup-<>-arglists nil)
+       )
+    (if        (cond
+        ((looking-at c-constant-key)
+         (goto-char (match-end 1))
+         (c-forward-syntactic-ws limit)
+         t)
+        ((eq (char-after) ?\()
+         (and (c-go-list-forward (point) limit)
+              (eq (char-before) ?\))
+              (progn (c-forward-syntactic-ws limit)
+                     t)))
+        ((c-forward-over-compound-identifier)
+         (c-forward-syntactic-ws limit)
+         (while (cond
+                 ((looking-at "<")
+                  (prog1
+                      (c-forward-<>-arglist nil)
+                    (c-forward-syntactic-ws limit)))
+                 ((looking-at c-opt-identifier-concat-key)
+                  (and
+                   (zerop (c-forward-token-2 1 nil limit))
+                   (prog1
+                       (c-forward-over-compound-identifier)
+                     (c-forward-syntactic-ws limit))))))
+         t)
+        ((looking-at c-fun-name-substitute-key) ; "requires"
+         (goto-char (match-end 1))
+         (c-forward-syntactic-ws limit)
+         (and
+          (or (not (eq (char-after) ?\())
+              (prog1
+                  (and (c-go-list-forward (point) limit)
+                       (eq (char-before) ?\)))
+                (c-forward-syntactic-ws)))
+          (eq (char-after) ?{)
+          (and (c-go-list-forward (point) limit)
+               (eq (char-before) ?}))
+          (progn
+            (c-forward-syntactic-ws limit)
+            t))))
+       t
+      (goto-char here)
+      nil)))
+
+(defun c-forward-c++-requires-clause (&optional limit)
+  ;; Point is at the keyword "requires".  Move forward over the requires
+  ;; clause to the next token after it and return non-nil.  If there is no
+  ;; valid requires clause at point, leave point unmoved and return nil.
+  (let ((here (point))
+       final-point)
+    (or limit (setq limit (point-max)))
+    (if (and
+        (zerop (c-forward-token-2 1 nil limit)) ; over "requires".
+        (prog1
+            (c-forward-primary-expression limit)
+          (setq final-point (point))
+          (while
+              (and (looking-at "\\(?:&&\\|||\\)")
+                   (progn (goto-char (match-end 0))
+                          (c-forward-syntactic-ws limit)
+                          (and (< (point) limit)
+                               (c-forward-primary-expression limit))))
+            (setq final-point (point)))))
+       (progn (goto-char final-point)
+              t)
+      (goto-char here)
+      nil)))
+
+(defun c-forward-decl-arglist (not-top id-in-parens &optional limit)
+  ;; Point is at an open parenthesis, assumed to be the arglist of a function
+  ;; declaration.  Move over this arglist and following syntactic whitespace,
+  ;; and return non-nil.  If the construct isn't such an arglist, leave point
+  ;; unmoved and return nil.
+  ;;
+  ;; Note that point is assumed to be at a place where an arglist is expected.
+  ;; Only for C++, where there are other possibilities, is any actual
+  ;; processing done.  Otherwise, t is simply returned.
+  (let ((here (point)) got-type)
+    (if        (or
+        (not (c-major-mode-is 'c++-mode))
+        (and
+         (or (not not-top)
+             id-in-parens              ; Id is in parens, etc.
+             (save-excursion
+               (forward-char)
+               (c-forward-syntactic-ws limit)
+               (looking-at "[*&]")))
+         (save-excursion
+           (let (c-last-identifier-range)
+             (forward-char)
+             (c-forward-syntactic-ws limit)
+             (catch 'is-function
+               (while
+                   ;; Go forward one argument at each iteration.
+                   (progn
+                     (while
+                         (cond
+                          ((looking-at c-decl-hangon-key)
+                           (c-forward-keyword-clause 1))
+                          ((looking-at
+                            c-noise-macro-with-parens-name-re)
+                           (c-forward-noise-clause))))
+                     (when (eq (char-after) ?\))
+                       (forward-char)
+                       (c-forward-syntactic-ws limit)
+                       (throw 'is-function t))
+                     (setq got-type (c-forward-type))
+                     (cond
+                      ((null got-type)
+                       (throw 'is-function nil))
+                      ((not (eq got-type 'maybe))
+                       (throw 'is-function t)))
+                     (c-forward-declarator limit t t)
+                     (eq (char-after) ?,))
+                 (forward-char)
+                 (c-forward-syntactic-ws))
+               t)))))
+       (and (c-go-list-forward (point) limit)
+            (progn (c-forward-syntactic-ws limit) t))
+      (goto-char here)
+      nil)))
+
+(defun c-forward-declarator (&optional limit accept-anon not-top)
   ;; Assuming point is at the start of a declarator, move forward over it,
   ;; leaving point at the next token after it (e.g. a ) or a ; or a ,), or at
   ;; end of buffer if there is no such token.
   ;;
-  ;; Return a list (ID-START ID-END BRACKETS-AFTER-ID GOT-INIT DECORATED),
-  ;; where ID-START and ID-END are the bounds of the declarator's identifier,
-  ;; and BRACKETS-AFTER-ID is non-nil if a [...] pair is present after the id.
-  ;; GOT-INIT is non-nil when the declarator is followed by "=" or "(",
-  ;; DECORATED is non-nil when the identifier is embellished by an operator,
-  ;; like "*x", or "(*x)".
+  ;; Return a list (ID-START ID-END BRACKETS-AFTER-ID GOT-INIT DECORATED
+  ;; ARGLIST), where ID-START and ID-END are the bounds of the declarator's
+  ;; identifier, BRACKETS-AFTER-ID is non-nil if a [...] pair is present after
+  ;; the id, and ARGLIST is non-nil either when an arglist has been moved
+  ;; over, or when we have stopped at an unbalanced open-paren.  GOT-INIT is
+  ;; non-nil when the declarator is followed by "=" or "(", DECORATED is
+  ;; non-nil when the identifier is embellished by an operator, like "*x", or
+  ;; "(*x)".
   ;;
   ;; If ACCEPT-ANON is non-nil, move forward over any "anonymous declarator",
   ;; i.e. something like the (*) in int (*), such as might be found in a
@@ -9486,7 +9675,8 @@ point unchanged and return nil."
   ;; array/struct initialization) or "=" or terminating delimiter
   ;; (e.g. "," or ";" or "}").
   (let ((here (point))
-       id-start id-end brackets-after-id paren-depth decorated)
+       id-start id-end brackets-after-id paren-depth decorated
+       got-init arglist)
     (or limit (setq limit (point-max)))
     (if        (and
         (< (point) limit)
@@ -9507,25 +9697,38 @@ point unchanged and return nil."
                ((and c-opt-cpp-prefix
                      (looking-at c-noise-macro-with-parens-name-re))
                 (c-forward-noise-clause))
+               ;; Special handling for operator<op>.
+               ((and c-opt-op-identifier-prefix
+                     (looking-at c-opt-op-identifier-prefix))
+                (goto-char (match-end 1))
+                (c-forward-syntactic-ws limit)
+                (setq id-start (point))
+                (if (looking-at c-overloadable-operators-regexp)
+                    (progn
+                      (goto-char (match-end 0))
+                      (c-forward-syntactic-ws limit)
+                      (setq got-identifier t)
+                      nil)
+                  t))
                ((and (looking-at c-type-decl-prefix-key)
                      (if (and (c-major-mode-is 'c++-mode)
                               (match-beginning 4)) ; Was 3 - 2021-01-01
-                         ;; If the third submatch matches in C++ then
+                         ;; If the fourth submatch matches in C++ then
                          ;; we're looking at an identifier that's a
                          ;; prefix only if it specifies a member pointer.
                          (progn
                            (setq id-start (point))
-                           (c-forward-name)
-                           (if (save-match-data
-                                 (looking-at "\\(::\\)"))
-                               ;; We only check for a trailing "::" and
-                               ;; let the "*" that should follow be
-                               ;; matched in the next round.
-                               t
-                             ;; It turned out to be the real identifier,
-                             ;; so flag that and stop.
-                             (setq got-identifier t)
-                             nil))
+                           (when (c-forward-name)
+                             (if (save-match-data
+                                   (looking-at "\\(::\\)"))
+                                 ;; We only check for a trailing "::" and
+                                 ;; let the "*" that should follow be
+                                 ;; matched in the next round.
+                                 t
+                               ;; It turned out to be the real identifier,
+                               ;; so flag that and stop.
+                               (setq got-identifier t)
+                               nil)))
                        t))
                 (if (save-match-data
                       (looking-at c-type-decl-operator-prefix-key))
@@ -9551,25 +9754,42 @@ point unchanged and return nil."
            (accept-anon
             (setq id-start nil id-end nil)
             t)
-           (t (/= (point) here))))
+           (t nil)))
 
         ;; Skip out of the parens surrounding the identifier.  If closing
         ;; parens are missing, this form returns nil.
         (or (= paren-depth 0)
-            (c-safe (goto-char (scan-lists (point) 1 paren-depth))))
+            (prog1
+                (c-safe (goto-char (scan-lists (point) 1 paren-depth)))
+              (c-forward-syntactic-ws)))
 
         (or (eq (point) (point-max))   ; No token after identifier.
             (< (point) limit))
 
         ;; Skip over any trailing bit, such as "__attribute__".
         (progn
-             (while (cond
-                     ((looking-at c-decl-hangon-key)
-                      (c-forward-keyword-clause 1))
-                     ((and c-opt-cpp-prefix
-                           (looking-at c-noise-macro-with-parens-name-re))
-                      (c-forward-noise-clause))))
-             (<= (point) limit))
+          (while (cond
+                  ((looking-at c-decl-hangon-key)
+                   (c-forward-keyword-clause 1))
+                  ((looking-at c-type-decl-suffix-key)
+                   (cond
+                    ((save-match-data
+                       (looking-at c-fun-name-substitute-key))
+                     (c-forward-c++-requires-clause))
+                    ((eq (char-after) ?\()
+                     (if (c-forward-decl-arglist not-top decorated limit)
+                         (progn (setq arglist t
+                                      got-init nil)
+                                t)
+                       (if (c-go-list-forward (point) limit)
+                           t
+                         (setq arglist t) ; For unbalanced (.
+                         nil)))
+                    (t (c-forward-keyword-clause 1))))
+                  ((and c-opt-cpp-prefix
+                        (looking-at c-noise-macro-with-parens-name-re))
+                   (c-forward-noise-clause))))
+          (<= (point) limit))
 
         ;; Search syntactically to the end of the declarator (";",
         ;; ",", a closing paren, eob etc) or to the beginning of an
@@ -9577,45 +9797,56 @@ point unchanged and return nil."
         ;; Note that square brackets are now not also treated as
         ;; initializers, since this broke when there were also
         ;; initializing brace lists.
-        (let (found)
-          (while
-              (and (< (point) limit)
-                   (progn
-                     ;; In the next loop, we keep searching forward whilst
-                     ;; we find ":"s which aren't single colons inside C++
-                     ;; "for" statements.
-                     (while
-                         (and
-                          (< (point) limit)
-                          (setq found
-                                (c-syntactic-re-search-forward
-                                 "[;:,]\\|\\s)\\|\\(=\\|\\s(\\)"
-                                 limit t t))
-                          (eq (char-before) ?:)
-                          (if (looking-at c-:-op-cont-regexp)
-                              (progn (goto-char (match-end 0)) t)
-                            (not
-                             (and (c-major-mode-is '(c++-mode java-mode))
-                                  (save-excursion
-                                    (and
-                                     (c-go-up-list-backward)
-                                     (eq (char-after) ?\()
-                                     (progn (c-backward-syntactic-ws)
-                                            (c-simple-skip-symbol-backward))
-                                     (looking-at c-paren-stmt-key))))))))
-                     found)
-                   (eq (char-before) ?\[)
-                   (c-go-up-list-forward))
-            (setq brackets-after-id t))
-          (when found (backward-char))
-          (<= (point) limit)))
-       (list id-start id-end brackets-after-id (match-beginning 1) decorated)
+        (or (eq (char-after) ?\()      ; Not an arglist.
+            (let (found)
+              (while
+                  (and (< (point) limit)
+                       (progn
+                         ;; In the next loop, we keep searching forward
+                         ;; whilst we find ":"s which aren't single colons
+                         ;; inside C++ "for" statements.
+                         (while
+                             (and
+                              (< (point) limit)
+                              (prog1
+                                  (setq found
+                                        (c-syntactic-re-search-forward
+                                         "[;:,]\\|\\(=\\|\\s(\\)"
+                                         limit 'limit t))
+                                (setq got-init
+                                      (and found (match-beginning 1))))
+                              (eq (char-before) ?:)
+                               (not
+                               (and (c-major-mode-is '(c++-mode java-mode))
+                                     (save-excursion
+                                       (and
+                                       (c-go-up-list-backward)
+                                       (eq (char-after) ?\()
+                                       (progn (c-backward-syntactic-ws)
+                                               (c-simple-skip-symbol-backward))
+                                       (looking-at c-paren-stmt-key)))))
+                              (if (looking-at c-:-op-cont-regexp)
+                                  (progn (goto-char (match-end 0)) t)
+                                ;; Does this : introduce the class
+                                ;; initialization list, or a bitfield?
+                                (not arglist)))) ; Carry on for a bitfield
+                         found)
+                       (when (eq (char-before) ?\[)
+                         (setq brackets-after-id t)
+                         (prog1 (c-go-up-list-forward)
+                           (c-forward-syntactic-ws)))))
+              (when (and found
+                         (memq (char-before) '(?\; ?\: ?, ?= ?\( ?\[ ?{)))
+                (backward-char))
+              (<= (point) limit))))
+       (list id-start id-end brackets-after-id got-init decorated arglist)
 
       (goto-char here)
       nil)))
 
 (defun c-do-declarators
-    (cdd-limit cdd-list cdd-not-top cdd-comma-prop cdd-function)
+    (cdd-limit cdd-list cdd-not-top cdd-comma-prop cdd-function
+              &optional cdd-accept-anon)
   "Assuming point is at the start of a comma separated list of declarators,
 apply CDD-FUNCTION to each declarator (when CDD-LIST is non-nil) or just the
 first declarator (when CDD-LIST is nil).  When CDD-FUNCTION is nil, no
@@ -9640,6 +9871,9 @@ Stop at or before CDD-LIMIT (which may NOT be nil).
 If CDD-NOT-TOP is non-nil, we are not at the top-level (\"top-level\" includes
 being directly inside a class or namespace, etc.).
 
+If CDD-ACCEPT-ANON is non-nil, we also process declarators without names,
+e.g. \"int (*)(int)\" in a function prototype.
+
 Return non-nil if we've reached the token after the last declarator (often a
 semicolon, or a comma when CDD-LIST is nil); otherwise (when we hit CDD-LIMIT,
 or fail otherwise) return nil, leaving point at the beginning of the putative
@@ -9651,67 +9885,25 @@ This function might do hidden buffer changes."
   ;; CDD-FUNCTION.
   (let
       ((cdd-pos (point)) cdd-next-pos cdd-id-start cdd-id-end
-       cdd-decl-res cdd-got-func cdd-got-type cdd-got-init
+       cdd-decl-res cdd-got-func cdd-got-init
        c-last-identifier-range cdd-exhausted cdd-after-block)
 
     ;; The following `while' applies `cdd-function' to a single declarator id
     ;; each time round.  It loops only when CDD-LIST is non-nil.
     (while
        (and (not cdd-exhausted)
-            (setq cdd-decl-res (c-forward-declarator cdd-limit)))
+            (setq cdd-decl-res (c-forward-declarator
+                                cdd-limit cdd-accept-anon cdd-not-top)))
+
       (setq cdd-next-pos (point)
            cdd-id-start (car cdd-decl-res)
            cdd-id-end (cadr cdd-decl-res)
-           cdd-got-func (and (eq (char-after) ?\()
-                         (or (not (c-major-mode-is 'c++-mode))
-                             (not cdd-not-top)
-                             (car (cddr (cddr cdd-decl-res))) ; Id is in
-                                       ; parens, etc.
-                             (save-excursion
-                               (forward-char)
-                               (c-forward-syntactic-ws)
-                               (looking-at "[*&]")))
-                         (not (car (cddr cdd-decl-res)))
-                         (or (not (c-major-mode-is 'c++-mode))
-                             (save-excursion
-                               (let (c-last-identifier-range)
-                                 (forward-char)
-                                 (c-forward-syntactic-ws)
-                                 (catch 'is-function
-                                   (while
-                                       (progn
-                                         (while
-                                             (cond
-                                              ((looking-at c-decl-hangon-key)
-                                               (c-forward-keyword-clause 1))
-                                              ((looking-at 
c-noise-macro-with-parens-name-re)
-                                               (c-forward-noise-clause))))
-                                         (if (eq (char-after) ?\))
-                                             (throw 'is-function t))
-                                         (setq cdd-got-type (c-forward-type))
-                                         (cond
-                                          ((null cdd-got-type)
-                                           (throw 'is-function nil))
-                                          ((not (eq cdd-got-type 'maybe))
-                                           (throw 'is-function t)))
-                                         (c-forward-declarator nil t)
-                                         (eq (char-after) ?,))
-                                     (forward-char)
-                                     (c-forward-syntactic-ws))
-                                   t)))))
-           cdd-got-init (and (cadr (cddr cdd-decl-res))
-                         (char-after)))
+           cdd-got-func (cadr (cddr (cddr cdd-decl-res)))
+           cdd-got-init (and (cadr (cddr cdd-decl-res)) (char-after)))
 
       ;; Jump past any initializer or function prototype to see if
       ;; there's a ',' to continue at.
-      (cond (cdd-got-func
-            ;; Skip a parenthesized initializer (C++) or a function
-            ;; prototype.
-            (if (c-go-list-forward (point) cdd-limit) ; over the parameter 
list.
-                (c-forward-syntactic-ws cdd-limit)
-              (setq cdd-exhausted t))) ; unbalanced parens
-
-           (cdd-got-init               ; "=" sign OR opening "(", "[", or "("
+      (cond (cdd-got-init              ; "=" sign OR opening "(", "[", or "("
             ;; Skip an initializer expression in braces, whether or not (in
             ;; C++ Mode) preceded by an "=".  Be careful that the brace list
             ;; isn't a code block or a struct (etc.) block.
@@ -9734,8 +9926,9 @@ This function might do hidden buffer changes."
            (t (c-forward-syntactic-ws cdd-limit)))
 
       (if cdd-function
-         (funcall cdd-function cdd-id-start cdd-id-end cdd-next-pos
-                  cdd-not-top cdd-got-func cdd-got-init))
+         (save-excursion
+           (funcall cdd-function cdd-id-start cdd-id-end cdd-next-pos
+                    cdd-not-top cdd-got-func cdd-got-init)))
 
       ;; If a ',' is found we set cdd-pos to the next declarator and iterate.
       (if (and cdd-list (< (point) cdd-limit) (looking-at ","))
@@ -9835,13 +10028,13 @@ This function might do hidden buffer changes."
   ;;
   ;;
   ;;
-  ;;   The second element of the return value is non-nil when a
-  ;;   `c-typedef-decl-kwds' specifier is found in the declaration.
-  ;;   Specifically it is a dotted pair (A . B) where B is t when a
-  ;;   `c-typedef-kwds' ("typedef") is present, and A is t when some
-  ;;   other `c-typedef-decl-kwds' (e.g. class, struct, enum)
-  ;;   specifier is present.  I.e., (some of) the declared
-  ;;   identifier(s) are types.
+  ;;   The second element of the return value is non-nil when something
+  ;;   indicating the identifier is a type occurs in the declaration.
+  ;;   Specifically it is nil, or a three element list (A B C) where C is t
+  ;;   when context is '<> and the "identifier" is a found type, B is t when a
+  ;;   `c-typedef-kwds' ("typedef") is present, and A is t when some other
+  ;;   `c-typedef-declkwds' (e.g. class, struct, enum) specifier is present.
+  ;;   I.e., (some of) the declared identifier(s) are types.
   ;;
   ;;   The third element of the return value is non-nil when the declaration
   ;;   parsed might be an expression.  The fourth element is the position of
@@ -9917,6 +10110,9 @@ This function might do hidden buffer changes."
        at-type-decl
        ;; Set if we've a "typedef" keyword.
        at-typedef
+       ;; Set if `context' is '<> and the identifier is definitely a type, or
+       ;; has already been recorded as a found type.
+       at-<>-type
        ;; Set if we've found a specifier that can start a declaration
        ;; where there's no type.
        maybe-typeless
@@ -9995,6 +10191,11 @@ This function might do hidden buffer changes."
            (setq kwd-sym (c-keyword-sym (match-string 1)))
            (save-excursion
              (c-forward-keyword-clause 1)
+             (when (and (c-major-mode-is 'c++-mode)
+                        (c-keyword-member kwd-sym 'c-<>-sexp-kwds)
+                        (save-match-data
+                          (looking-at c-fun-name-substitute-key)))
+               (c-forward-c++-requires-clause))
              (setq kwd-clause-end (point))))
           ((and c-opt-cpp-prefix
                 (looking-at c-noise-macro-with-parens-name-re))
@@ -10034,6 +10235,11 @@ This function might do hidden buffer changes."
                               (point))))
                      found-type-list))
 
+             ;; Might we have a C++20 concept?  i.e. template<foo bar>?
+             (setq at-<>-type
+                   (and (eq context '<>)
+                        (memq found-type '(t known prefix found))))
+
              ;; Signal a type declaration for "struct foo {".
              (when (and backup-at-type-decl
                         (eq (char-after) ?{))
@@ -10322,8 +10528,11 @@ This function might do hidden buffer changes."
                  t)
              (when (if (save-match-data (looking-at "\\s("))
                        (c-safe (c-forward-sexp 1) t)
-                     (goto-char (match-end 1))
-                     t)
+                     (if (save-match-data
+                           (looking-at c-fun-name-substitute-key)) ; requires
+                         (c-forward-c++-requires-clause)
+                       (goto-char (match-end 1))
+                       t))
                (when (and (not got-suffix-after-parens)
                           (= paren-depth 0))
                  (setq got-suffix-after-parens (match-beginning 0)))
@@ -10916,8 +11125,8 @@ This function might do hidden buffer changes."
            (c-forward-type))))
 
       (list id-start
-           (and (or at-type-decl at-typedef)
-                (cons at-type-decl at-typedef))
+           (and (or at-type-decl at-typedef at-<>-type)
+                (list at-type-decl at-typedef at-<>-type))
            maybe-expression
            type-start
            (or (eq context 'top) make-top)))
@@ -12374,6 +12583,8 @@ comment at the start of cc-engine.el for more info."
                       in-paren 'in-paren))
                ((looking-at c-pre-brace-non-bracelist-key)
                 (setq braceassignp nil))
+               ((looking-at c-fun-name-substitute-key)
+                (setq braceassignp nil))
                ((looking-at c-return-key))
                ((and (looking-at c-symbol-start)
                      (not (looking-at c-keywords-regexp)))
@@ -12384,6 +12595,11 @@ comment at the start of cc-engine.el for more info."
                   (setq after-type-id-pos (point))))
                ((eq (char-after) ?\()
                 (setq parens-before-brace t)
+                ;; Have we a requires with a parenthesis list?
+                (when (save-excursion
+                        (and (zerop (c-backward-token-2 1 nil lim))
+                             (looking-at c-fun-name-substitute-key)))
+                  (setq braceassignp nil))
                 nil)
                (t nil))
               (save-excursion
@@ -14146,6 +14362,25 @@ comment at the start of cc-engine.el for more info."
            (goto-char placeholder)
            (c-add-syntax 'inher-cont (c-point 'boi)))
 
+          ;; CASE 5D.7: Continuation of a "concept foo =" line in C++20 (or
+          ;; similar).
+          ((and c-equals-nontype-decl-key
+                (save-excursion
+                  (prog1
+                      (and (zerop (c-backward-token-2 1 nil lim))
+                           (looking-at c-operator-re)
+                           (equal (match-string 0) "=")
+                           (zerop (c-backward-token-2 1 nil lim))
+                           (looking-at c-symbol-start)
+                           (not (looking-at c-keywords-regexp))
+                           (zerop (c-backward-token-2 1 nil lim))
+                           (looking-at c-equals-nontype-decl-key)
+                           (eq (c-beginning-of-statement-1 lim) 'same))
+                    (setq placeholder (point)))))
+           (goto-char placeholder)
+           (c-add-stmt-syntax 'topmost-intro-cont nil nil containing-sexp
+                              paren-state))
+
           ;; CASE 5D.5: Continuation of the "expression part" of a
           ;; top level construct.  Or, perhaps, an unrecognized construct.
           (t
diff --git a/lisp/progmodes/cc-fonts.el b/lisp/progmodes/cc-fonts.el
index 12bb3d3751..2e71285cb3 100644
--- a/lisp/progmodes/cc-fonts.el
+++ b/lisp/progmodes/cc-fonts.el
@@ -99,6 +99,8 @@
 (cc-bytecomp-defun c-font-lock-invalid-string)
 (cc-bytecomp-defun c-font-lock-fontify-region)
 
+(cc-bytecomp-defvar font-lock-reference-face) ; For Emacs 29
+
 
 ;; Note that font-lock in XEmacs doesn't expand face names as
 ;; variables, so we have to use the (eval . FORM) in the font lock
@@ -112,8 +114,10 @@
         ;; In Emacs font-lock-builtin-face has traditionally been
         ;; used for preprocessor directives.
         'font-lock-builtin-face)
-       (t
-        'font-lock-reference-face)))
+       ((and (c-face-name-p 'font-lock-reference-face)
+             (eq font-lock-reference-face 'font-lock-reference-face))
+        'font-lock-reference-face)
+       (t 'font-lock-constant-face)))
 
 (cc-bytecomp-defvar font-lock-constant-face)
 
@@ -163,9 +167,8 @@
 
 (defconst c-doc-markup-face-name
   (if (c-face-name-p 'font-lock-doc-markup-face)
-        ;; If it happens to occur in the future.  (Well, the more
-        ;; pragmatic reason is to get unique faces for the test
-        ;; suite.)
+        ;; Exists in Emacs 28+.  (For other emacsen, the pragmatic
+        ;; reason is to get unique faces for the test suite.)
         'font-lock-doc-markup-face
     c-label-face-name))
 
@@ -558,8 +561,10 @@ stuff.  Used on level 1 and higher."
                                 (c-lang-const c-opt-cpp-prefix)
                                 re
                                 (c-lang-const c-syntactic-ws)
-                                "\\(<[^>\n\r]*>?\\)")
-                        `(,(+ ncle-depth re-depth sws-depth 1)
+                                "\\(<\\([^>\n\r]*\\)>?\\)")
+                        `(,(+ ncle-depth re-depth sws-depth
+                              (if (featurep 'xemacs) 2 1)
+                              )
                           font-lock-string-face t)
                         `((let ((beg (match-beginning
                                       ,(+ ncle-depth re-depth sws-depth 1)))
@@ -878,6 +883,27 @@ casts and declarations are fontified.  Used on level 2 and 
higher."
                                                  c-reference-face-name))
                        (goto-char (match-end 1))))))))))
 
+      ;; Module declarations (e.g. in C++20).
+      ,@(when (c-major-mode-is 'c++-mode)
+         '(c-font-lock-c++-modules))
+
+      ;; The next regexp is highlighted with narrowing.  This is so that the
+      ;; final "context" bit of the regexp, "\\(?:[^=]\\|$\\)", which cannot
+      ;; match anything non-empty at LIMIT, will match "$" instead.
+      ,@(when (c-lang-const c-equals-nontype-decl-kwds)
+         `((,(byte-compile
+              `(lambda (limit)
+                 (save-restriction
+                   (narrow-to-region (point-min) limit)
+                   ,(c-make-font-lock-search-form
+                     (concat (c-lang-const c-equals-nontype-decl-key) ;no \\(
+                             (c-lang-const c-simple-ws) "+\\("
+                             (c-lang-const c-symbol-key) "\\)"
+                             (c-lang-const c-simple-ws) "*"
+                             "=\\(?:[^=]\\|$\\)")
+                     `((,(+ 1 (c-lang-const c-simple-ws-depth))
+                        font-lock-type-face t)))))))))
+
       ;; Fontify the special declarations in Objective-C.
       ,@(when (c-major-mode-is 'objc-mode)
          `(;; Fontify class names in the beginning of message expressions.
@@ -934,17 +960,16 @@ casts and declarations are fontified.  Used on level 2 
and higher."
     (save-excursion
       (let ((pos (point)))
        (c-backward-syntactic-ws (max (- (point) 500) (point-min)))
-       (c-clear-char-properties
-        (if (and (not (bobp))
-                 (memq (c-get-char-property (1- (point)) 'c-type)
-                       '(c-decl-arg-start
-                         c-decl-end
-                         c-decl-id-start
-                         c-decl-type-start
-                         c-not-decl)))
-            (1- (point))
-          pos)
-        limit 'c-type)))
+       (when (and (not (bobp))
+                  (memq (c-get-char-property (1- (point)) 'c-type)
+                        '(c-decl-arg-start
+                          c-decl-end
+                          c-decl-id-start
+                          c-decl-type-start
+                          c-not-decl)))
+         (setq pos (1- (point))))
+       (c-clear-char-properties pos limit 'c-type)
+       (c-clear-char-properties pos limit 'c-<>-c-types-set)))
 
     ;; Update `c-state-cache' to the beginning of the region.  This will
     ;; make `c-beginning-of-syntax' go faster when it's used later on,
@@ -1067,7 +1092,7 @@ casts and declarations are fontified.  Used on level 2 
and higher."
   nil)
 
 (defun c-font-lock-declarators (limit list types not-top
-                                     &optional template-class)
+                                     &optional template-class accept-anon)
   ;; Assuming the point is at the start of a declarator in a declaration,
   ;; fontify the identifier it declares.  (If TYPES is t, it does this via the
   ;; macro `c-fontify-types-and-refs'.)
@@ -1087,6 +1112,8 @@ casts and declarations are fontified.  Used on level 2 
and higher."
   ;; a default (introduced by "="), it will be fontified as a type.
   ;; E.g. "<class X = Y>".
   ;;
+  ;; ACCEPT-ANON is non-nil when we accept anonymous declarators.
+  ;;
   ;; Nil is always returned.  The function leaves point at the delimiter after
   ;; the last declarator it processes.
   ;;
@@ -1099,37 +1126,35 @@ casts and declarations are fontified.  Used on level 2 
and higher."
      limit list not-top
      (cond ((eq types t) 'c-decl-type-start)
           ((null types) 'c-decl-id-start))
-     (lambda (id-start _id-end end-pos _not-top is-function init-char)
+     (lambda (id-start id-end end-pos _not-top is-function init-char)
        (if (eq types t)
-          ;; Register and fontify the identifier as a type.
-          (let ((c-promote-possible-types t))
-            (goto-char id-start)
-            (c-forward-type))
-        ;; The following doesn't work properly (yet, 2018-09-22).
-        ;; (c-put-font-lock-face id-start id-end
-        ;;                    (if is-function
-        ;;                        'font-lock-function-name-face
-        ;;                      'font-lock-variable-name-face))
-        (when (and c-last-identifier-range
-                   (not (get-text-property (car c-last-identifier-range)
-                                           'face)))
-          ;; We use `c-last-identifier-range' rather than `id-start' and
-          ;; `id-end', since the latter two can be erroneous.  E.g. in
-          ;; "~Foo", `id-start' is at the tilde.  This is a bug in
-          ;; `c-forward-declarator'.
-          (c-put-font-lock-face (car c-last-identifier-range)
-                                (cdr c-last-identifier-range)
-                                (cond
-                                 ((not (memq types '(nil t))) types)
-                                 (is-function 'font-lock-function-name-face)
-                                 (t 'font-lock-variable-name-face)))))
+          (when id-start
+            ;; Register and fontify the identifier as a type.
+            (let ((c-promote-possible-types t))
+              (goto-char id-start)
+              (c-forward-type)))
+        (when id-start
+          (goto-char id-start)
+          (when c-opt-identifier-prefix-key
+            (unless (and (looking-at c-opt-identifier-prefix-key) ; For 
operator~
+                         (eq (match-end 1) id-end))
+              (while (and (< (point) id-end)
+                          (re-search-forward c-opt-identifier-prefix-key 
id-end t))
+                (c-forward-syntactic-ws limit))))
+          (when (not (get-text-property (point) 'face))
+            (c-put-font-lock-face (point) id-end
+                                  (cond
+                                   ((not (memq types '(nil t))) types)
+                                   (is-function 'font-lock-function-name-face)
+                                   (t 'font-lock-variable-name-face))))))
        (and template-class
            (eq init-char ?=)           ; C++ "<class X = Y>"?
            (progn
              (goto-char end-pos)
              (c-forward-token-2 1 nil limit) ; Over "="
              (let ((c-promote-possible-types t))
-               (c-forward-type t))))))
+               (c-forward-type t)))))
+     accept-anon)                      ; Last argument to c-do-declarators.
     nil))
 
 (defun c-get-fontification-context (match-pos not-front-decl &optional toplev)
@@ -1270,15 +1295,19 @@ casts and declarations are fontified.  Used on level 2 
and higher."
                    (or (memq type '(c-decl-arg-start c-decl-type-start))
                        (and
                         (progn (c-backward-syntactic-ws) t)
-                        (c-back-over-compound-identifier)
-                        (progn
-                          (c-backward-syntactic-ws)
-                          (or (bobp)
-                              (progn
-                                (setq type (c-get-char-property (1- (point))
-                                                                'c-type))
-                                (memq type '(c-decl-arg-start
-                                             c-decl-type-start))))))))))
+                        (or
+                         (and
+                          (c-back-over-compound-identifier)
+                          (progn
+                            (c-backward-syntactic-ws)
+                            (or (bobp)
+                                (progn
+                                  (setq type (c-get-char-property (1- (point))
+                                                                  'c-type))
+                                  (memq type '(c-decl-arg-start
+                                               c-decl-type-start))))))
+                         (and (zerop (c-backward-token-2))
+                              (looking-at c-fun-name-substitute-key))))))))
           (cons 'decl nil))
          (t (cons 'arglist t)))))
 
@@ -1355,9 +1384,12 @@ casts and declarations are fontified.  Used on level 2 
and higher."
                                     'c-decl-type-start
                                   'c-decl-id-start)))))
       (c-font-lock-declarators
-       (min limit (point-max)) decl-list
+       (min limit (point-max))
+       decl-list
        (not (null (cadr decl-or-cast)))
-       (not toplev) template-class))
+       (not toplev)
+       template-class
+       (memq context '(decl <>))))
 
     ;; A declaration has been successfully identified, so do all the
     ;; fontification of types and refs that've been recorded.
@@ -1910,6 +1942,163 @@ casts and declarations are fontified.  Used on level 2 
and higher."
            (forward-char))))) ; over the terminating "]" or other close paren.
   nil)
 
+(defun c-forward-c++-module-name (limit)
+  ;; Is there a C++20 module name at point?  If so, return a cons of the start
+  ;; and end of that name, in which case point will be moved over the name and
+  ;; following whitespace.  Otherwise nil will be returned and point will be
+  ;; unmoved.  This function doesn't regard a partition as part of the name.
+  ;; The entire construct must end not after LIMIT.
+  (when (and
+        (looking-at c-module-name-re)
+        (<= (match-end 0) limit)
+        (not (looking-at c-keywords-regexp)))
+    (goto-char (match-end 0))
+    (prog1 (cons (match-beginning 0) (match-end 0))
+      (c-forward-syntactic-ws limit))))
+
+(defun c-forward-c++-module-partition-name (limit)
+  ;; Is there a C++20 module partition name (starting with its colon) at
+  ;; point?  If so return a cons of the start and end of the name, not
+  ;; including the colon, in which case point will be move to after the name
+  ;; and following whitespace.  Otherwise nil will be returned and point not
+  ;; moved.  The entire construct must end not after LIMIT.
+  (when (and
+        (eq (char-after) ?:)
+        (progn
+          (forward-char)
+          (c-forward-syntactic-ws limit)
+          (looking-at c-module-name-re))
+        (<= (match-end 0) limit)
+        (not (looking-at c-keywords-regexp)))
+    (goto-char (match-end 0))
+    (prog1 (cons (match-beginning 0) (match-end 0))
+      (c-forward-syntactic-ws limit))))
+
+(defun c-font-lock-c++-modules (limit)
+  ;; Fontify the C++20 module stanzas, characterized by the keywords `module',
+  ;; `export' and `import'.  Note that this has to be done by a function (as
+  ;; opposed to regexps) due to the presence of optional C++ attributes.
+  ;;
+  ;; This function will be called from font-lock for a region bounded by POINT
+  ;; and LIMIT, as though it were to identify a keyword for
+  ;; font-lock-keyword-face.  It always returns NIL to inhibit this and
+  ;; prevent a repeat invocation.  See elisp/lispref page "Search-based
+  ;; Fontification".
+  (while (and (< (point) limit)
+             (re-search-forward 
+              "\\<\\(module\\|export\\|import\\)\\>\\(?:[^_$]\\|$\\)"
+              limit t))
+    (goto-char (match-end 1))
+    (let (name-bounds pos beg end
+                     module-names)     ; A list of conses of start and end
+                                       ; of pertinent module names
+      (unless (c-skip-comments-and-strings limit)
+       (when
+           (cond
+            ;; module foo...; Note we don't handle module; or module
+            ;; :private; here, since they don't really need handling.
+            ((save-excursion
+               (when (equal (match-string-no-properties 1) "export")
+                 (c-forward-syntactic-ws limit)
+                 (re-search-forward "\\=\\(module\\)\\>\\(?:[^_$]\\|$\\)"
+                                    limit t))
+               (and (equal (match-string-no-properties 1) "module")
+                    (< (point) limit)
+                    (progn (c-forward-syntactic-ws limit)
+                           (setq name-bounds (c-forward-c++-module-name
+                                              limit)))
+                    (setq pos (point))))
+             (push name-bounds module-names)
+             (goto-char pos)
+             ;; Is there a partition name?
+             (when (setq name-bounds (c-forward-c++-module-partition-name
+                                      limit))
+               (push name-bounds module-names))
+             t)
+
+            ;; import
+            ((save-excursion
+               (when (equal (match-string-no-properties 1) "export")
+                 (c-forward-syntactic-ws limit)
+                 (re-search-forward "\\=\\(import\\)\\>\\(?:[^_$]\\|$\\)"
+                                    limit t))
+               (and (equal (match-string-no-properties 1) "import")
+                    (< (point) limit)
+                    (progn (c-forward-syntactic-ws limit)
+                           (setq pos (point)))))
+             (goto-char pos)
+             (cond
+              ;; import foo;
+              ((setq name-bounds (c-forward-c++-module-name limit))
+               (push name-bounds module-names)
+               t)
+              ;; import :foo;
+              ((setq name-bounds (c-forward-c++-module-partition-name limit))
+               (push name-bounds module-names)
+               t)
+              ;; import "foo";
+              ((and (eq (char-after) ?\")
+                    (setq pos (point))
+                    (c-safe (c-forward-sexp) t)) ; Should already have string 
face.
+               (when (eq (char-before) ?\")
+                 (setq beg pos
+                       end (point)))
+               (c-forward-syntactic-ws limit)
+               t)
+              ;; import <foo>;
+              ((and (looking-at "<\\(?:\\\\.\\|[^\\\n\r\t>]\\)*\\(>\\)?")
+                    (< (match-end 0) limit))
+               (setq beg (point))
+               (goto-char (match-end 0))
+               (when (match-end 1)
+                 (setq end (point)))
+               (if (featurep 'xemacs)
+                   (c-put-font-lock-face
+                    (1+ beg) (if end (1- end) (point)) font-lock-string-face)
+                 (c-put-font-lock-face
+                  beg (or end (point)) font-lock-string-face))
+               (c-forward-syntactic-ws limit)
+               t)
+              (t nil)))
+
+            ;; export
+            ;; There is no fontification to be done here, but we need to
+            ;; skip over the declaration or declaration sequence.
+            ((save-excursion
+               (when (equal (match-string-no-properties 0) "export")
+                 (c-forward-syntactic-ws limit)
+                 (setq pos (point))))
+             (goto-char (point))
+             (if (eq (char-after) ?{)
+                 ;; Declaration sequence.
+                 (unless (and (c-go-list-forward nil limit)
+                              (eq (char-before) ?}))
+                   (goto-char limit)
+                   nil)
+               ;; Single declaration
+               (unless (c-end-of-decl-1)
+                 (goto-char limit)
+                 nil))))               ; Nothing more to do, here.
+
+         ;; Optional attributes?
+         (while (and (c-looking-at-c++-attribute)
+                     (< (match-end 0) limit))
+           (goto-char (match-end 0))
+           (c-forward-syntactic-ws limit))
+         ;; Finally, there must be a semicolon.
+         (if (and (< (point) limit)
+                  (eq (char-after) ?\;))
+             (progn
+               (forward-char)
+               ;; Fontify any module names we've encountered.
+               (dolist (name module-names)
+                 (c-put-font-lock-face (car name) (cdr name)
+                                       c-reference-face-name)))
+           ;; No semicolon, so put warning faces on any delimiters.
+           (when beg
+             (c-put-font-lock-face beg (1+ beg) font-lock-warning-face))
+           (when end
+             (c-put-font-lock-face (1- end) end font-lock-warning-face))))))))
 
 (c-lang-defconst c-simple-decl-matchers
   "Simple font lock matchers for types and declarations.  These are used
@@ -2290,8 +2479,12 @@ higher."
          (widen)
          (goto-char (point-min))
          (while (re-search-forward target-re nil t)
-           (put-text-property (match-beginning 0) (match-end 0)
-                              'fontified nil)
+           (when (and
+                  (get-text-property (match-beginning 0) 'fontified)
+                  (not (memq (c-get-char-property (match-beginning 0) 'face)
+                             c-literal-faces)))
+             (c-put-font-lock-face (match-beginning 0) (match-end 0)
+                                   font-lock-type-face))
            (dolist (win-boundary window-boundaries)
              (when (and (< (match-beginning 0) (cdr win-boundary))
                         (> (match-end 0) (car win-boundary))
diff --git a/lisp/progmodes/cc-langs.el b/lisp/progmodes/cc-langs.el
index 068b4a65b2..cd23483a58 100644
--- a/lisp/progmodes/cc-langs.el
+++ b/lisp/progmodes/cc-langs.el
@@ -456,6 +456,7 @@ so that all identifiers are recognized as words.")
        c-depropertize-CPP
        c-before-change-check-ml-strings
        c-before-change-check-<>-operators
+       c-before-after-change-check-c++-modules
        c-truncate-bs-cache
        c-before-change-check-unbalanced-strings
        c-parse-quotes-before-change
@@ -516,6 +517,7 @@ parameters \(point-min) and \(point-max).")
        c-parse-quotes-after-change
        c-after-change-mark-abnormal-strings
        c-extend-font-lock-region-for-macros
+       c-before-after-change-check-c++-modules
        c-neutralize-syntax-in-CPP
        c-restore-<>-properties
        c-change-expand-fl-region)
@@ -932,6 +934,8 @@ This value is by default merged into `c-operators'."
                             t)))
       (when ops
        (c-make-keywords-re 'appendable ops))))
+(c-lang-defvar c-opt-identifier-prefix-key
+              (c-lang-const c-opt-identifier-prefix-key))
 
 (c-lang-defconst c-after-id-concat-ops
   "Operators that can occur after a binary operator on `c-identifier-ops'
@@ -1018,6 +1022,16 @@ e.g. identifiers with template arguments such as 
\"A<X,Y>\" in C++."
              "")))
 (c-lang-defvar c-identifier-key (c-lang-const c-identifier-key))
 
+(c-lang-defconst c-module-name-re
+  "This regexp matches (a component of) a module name.
+Currently (2022-09) just C++ Mode uses this."
+  t nil
+  c++ (concat (c-lang-const c-symbol-key)
+             "\\(?:\\."
+             (c-lang-const c-symbol-key)
+             "\\)*"))
+(c-lang-defvar c-module-name-re (c-lang-const c-module-name-re))
+
 (c-lang-defconst c-identifier-last-sym-match
   ;; This was a docstring constant in 5.30 but it's no longer used.
   ;; It's only kept to avoid breaking third party code.
@@ -1316,6 +1330,10 @@ since CC Mode treats every identifier as an expression."
                  ,@(when (c-major-mode-is 'java-mode)
                      '(">>>")))
 
+      ;; The C++ "spaceship" operator.
+      ,@(when (c-major-mode-is 'c++-mode)
+         `((left-assoc "<=>")))
+
       ;; Relational.
       (left-assoc "<" ">" "<=" ">="
                  ,@(when (c-major-mode-is 'java-mode)
@@ -1429,7 +1447,7 @@ form\".  See also `c-op-identifier-prefix'."
         "^" "??'" "xor" "&" "bitand" "|" "??!" "bitor" "~" "??-" "compl"
         "!" "=" "<" ">" "+=" "-=" "*=" "/=" "%=" "^="
         "??'=" "xor_eq" "&=" "and_eq" "|=" "??!=" "or_eq"
-        "<<" ">>" ">>=" "<<=" "==" "!=" "not_eq" "<=" ">="
+        "<<" ">>" ">>=" "<<=" "==" "!=" "not_eq" "<=>" "<=" ">="
         "&&" "and" "||" "??!??!" "or" "++" "--" "," "->*" "->"
         "()" "[]" "<::>" "??(??)")
   ;; These work like identifiers in Pike.
@@ -1551,8 +1569,10 @@ operators."
   "List of all arithmetic operators, including \"+=\", etc."
   ;; Note: in the following, there are too many operators for AWK and IDL.
   t (append (c-lang-const c-assignment-operators)
-           '("+" "-" "*" "/" "%"
+           `("+" "-" "*" "/" "%"
              "<<" ">>"
+             ,@(if (c-major-mode-is 'c++-mode)
+                   '("<=>"))
              "<" ">" "<=" ">="
              "==" "!="
              "&" "^" "|"
@@ -2202,7 +2222,7 @@ the appropriate place for that."
        '("_Bool" "_Complex" "_Imaginary") ; Conditionally defined in C99.
        (c-lang-const c-primitive-type-kwds))
   c++  (append
-       '("bool" "wchar_t" "char16_t" "char32_t")
+       '("bool" "wchar_t" "char8_t" "char16_t" "char32_t")
        (c-lang-const c-primitive-type-kwds))
   ;; Objective-C extends C, but probably not the new stuff in C99.
   objc (append
@@ -2581,6 +2601,35 @@ will be handled."
   t (c-make-keywords-re t (c-lang-const c-equals-type-clause-kwds)))
 (c-lang-defvar c-equals-type-clause-key (c-lang-const 
c-equals-type-clause-key))
 
+(c-lang-defconst c-equals-nontype-decl-kwds
+  "Keywords which are followed by an identifier then an \"=\"
+sign, which declares the identifier to be something other than a
+type."
+  t nil
+  c++ '("concept"))
+
+(c-lang-defconst c-equals-nontype-decl-key
+  ;; An unadorned regular expression which matches any member of
+  ;; `c-equals-decl-kwds', or nil if such don't exist in the current language.
+  t (when (c-lang-const c-equals-nontype-decl-kwds)
+      (c-make-keywords-re nil (c-lang-const c-equals-nontype-decl-kwds))))
+(c-lang-defvar c-equals-nontype-decl-key
+              (c-lang-const c-equals-nontype-decl-key))
+
+(c-lang-defconst c-fun-name-substitute-kwds
+  "Keywords which take the place of type+declarator at the beginning
+of a function-like structure, such as a C++20 \"requires\"
+clause.  An arglist may or may not follow such a keyword."
+  t nil
+  c++ '("requires"))
+
+(c-lang-defconst c-fun-name-substitute-key
+  ;; An adorned regular expression which matches any member of
+  ;; `c-fun-name-substitute-kwds'.
+  t (c-make-keywords-re t (c-lang-const c-fun-name-substitute-kwds)))
+(c-lang-defvar c-fun-name-substitute-key
+              (c-lang-const c-fun-name-substitute-key))
+
 (c-lang-defconst c-modifier-kwds
   "Keywords that can prefix normal declarations of identifiers
 \(and typically act as flags).  Things like argument declarations
@@ -2594,8 +2643,8 @@ will be handled."
   t    nil
   (c c++) '("extern" "inline" "register" "static")
   c    (append '("auto") (c-lang-const c-modifier-kwds))
-  c++  (append '("constexpr" "explicit" "friend" "mutable" "template"
-                "thread_local" "virtual")
+  c++  (append '("consteval" "constexpr" "constinit" "explicit"
+                "friend" "mutable" "template" "thread_local" "virtual")
               ;; "using" is now handled specially (2020-09-14).
               (c-lang-const c-modifier-kwds))
   objc '("auto" "bycopy" "byref" "extern" "in" "inout" "oneway" "out" "static")
@@ -2624,6 +2673,7 @@ If any of these also are on `c-type-list-kwds', 
`c-ref-list-kwds',
 `c-<>-type-kwds', or `c-<>-arglist-kwds' then the associated clauses
 will be handled."
   t       nil
+  c++     '("export")
   objc    '("@class" "@defs" "@end" "@property" "@dynamic" "@synthesize"
            "@compatibility_alias")
   java    '("import" "package")
@@ -2669,7 +2719,8 @@ one of `c-type-list-kwds', `c-ref-list-kwds',
   (c c++) '(;; GCC extension.
            "__attribute__"
            ;; MSVC extension.
-           "__declspec"))
+           "__declspec")
+  c++ (append (c-lang-const c-decl-hangon-kwds) '("alignas")))
 
 (c-lang-defconst c-decl-hangon-key
   ;; Adorned regexp matching `c-decl-hangon-kwds'.
@@ -2893,7 +2944,7 @@ contain type identifiers."
            "__attribute__"
            ;; MSVC extension.
            "__declspec")
-  c++ (append (c-lang-const c-paren-nontype-kwds) '("noexcept")))
+  c++ (append (c-lang-const c-paren-nontype-kwds) '("noexcept" "alignas")))
 
 (c-lang-defconst c-paren-nontype-key
   t (c-make-keywords-re t (c-lang-const c-paren-nontype-kwds)))
@@ -2925,6 +2976,17 @@ if this isn't nil."
         ;; In CORBA PSDL:
         "ref"))
 
+(c-lang-defconst c-pre-concept-<>-kwds
+  "Keywords that may be followed by an angle bracket expression containing
+uses of \"concepts\".  This is currently (2022-09) used only by C++."
+  t nil
+  c++ '("template"))
+
+(c-lang-defconst c-pre-concept-<>-key
+  ;; Regexp matching any element of `c-pre-concept-<>-kwds'.
+  t (c-make-keywords-re t (c-lang-const c-pre-concept-<>-kwds)))
+(c-lang-defvar c-pre-concept-<>-key (c-lang-const c-pre-concept-<>-key))
+
 (c-lang-defconst c-<>-arglist-kwds
   "Keywords that can be followed by a C++ style template arglist; see
 `c-recognize-<>-arglists' for details.  That language constant is
@@ -2937,7 +2999,8 @@ assumed to be set if this isn't nil."
 (c-lang-defconst c-<>-sexp-kwds
   ;; All keywords that can be followed by an angle bracket sexp.
   t (c--delete-duplicates (append (c-lang-const c-<>-type-kwds)
-                                 (c-lang-const c-<>-arglist-kwds))
+                                 (c-lang-const c-<>-arglist-kwds)
+                                 (c-lang-const c-import-<>-kwds))
                          :test 'string-equal))
 
 (c-lang-defconst c-opt-<>-sexp-key
@@ -3099,6 +3162,25 @@ This construct is \"<keyword> <expression> :\"."
   idl  nil
   awk  nil)
 
+(c-lang-defconst c-import-<>-kwds
+  "Keywords which can start an expression like \"import <...>\" in C++20.
+The <, and > operators are like those of #include <...>, they are
+not really template operators."
+  t nil
+  c++ '("import"))
+
+(c-lang-defconst c-module-kwds
+  "The keywords which introduce module constructs in C++20 onwards."
+  t nil
+  c++ '("module" "import" "export"))
+
+(c-lang-defconst c-module-key
+  ;; Adorned regexp matching module declaration keywords, or nil if there are
+  ;; none.
+  t (if (c-lang-const c-module-kwds)
+       (c-make-keywords-re t (c-lang-const c-module-kwds))))
+(c-lang-defvar c-module-key (c-lang-const c-module-key))
+
 (c-lang-defconst c-constant-kwds
   "Keywords for constants."
   t       nil
@@ -3113,6 +3195,10 @@ This construct is \"<keyword> <expression> :\"."
   java    '("true" "false" "null") ; technically "literals", not keywords
   pike    '("UNDEFINED")) ;; Not a keyword, but practically works as one.
 
+(c-lang-defconst c-constant-key
+  t (c-make-keywords-re t (c-lang-const c-constant-kwds)))
+(c-lang-defvar c-constant-key (c-lang-const c-constant-key))
+
 (c-lang-defconst c-primary-expr-kwds
   "Keywords besides constants and operators that start primary expressions."
   t    nil
@@ -3748,7 +3834,10 @@ is in effect when this is matched (see 
`c-identifier-syntax-table')."
                     ;; "throw" in `c-type-modifier-kwds' is followed
                     ;; by a parenthesis list, but no extra measures
                     ;; are necessary to handle that.
-                    (regexp-opt (c-lang-const c-type-modifier-kwds) t)
+                    (regexp-opt 
+                     (append (c-lang-const c-fun-name-substitute-kwds)
+                             (c-lang-const c-type-modifier-kwds))
+                     t)
                     "\\>")
                  "")
                "\\)")
diff --git a/lisp/progmodes/cc-mode.el b/lisp/progmodes/cc-mode.el
index 9327dbf775..2003b09ded 100644
--- a/lisp/progmodes/cc-mode.el
+++ b/lisp/progmodes/cc-mode.el
@@ -1421,6 +1421,13 @@ Note that the style variables are always made local to 
the buffer."
       (c-clear-syn-tab (point)))
      (t (c-benign-error "c-remove-string-fences: Wrong position")))))
 
+(defvar c-open-string-opener nil
+  "The position of the opening delimiter of an unterminated string or nil.
+This is valid only immediately after a buffer change, and refers
+only to an opener in the (logical) line containing the END
+position of `after-change-functions'.")
+(make-variable-buffer-local 'c-open-string-opener)
+
 (defun c-before-change-check-unbalanced-strings (beg end)
   ;; If BEG or END is inside an unbalanced string, remove the syntax-table
   ;; text property from respectively the start or end of the string.  Also
@@ -1685,13 +1692,14 @@ Note that the style variables are always made local to 
the buffer."
            (c-put-syn-tab (1- (point)) '(15))
            (c-put-syn-tab (match-end 0) '(15))
            (setq c-new-BEG (min c-new-BEG (point))
-                 c-new-END (max c-new-END (match-end 0))))
+                 c-new-END (max c-new-END (match-end 0)))
+           (setq c-open-string-opener (1- (point))))
           ((or (eq (match-end 0) (point-max))
                (eq (char-after (match-end 0)) ?\\)) ; \ at EOB
            (c-put-syn-tab (1- (point)) '(15))
            (setq c-new-BEG (min c-new-BEG (point))
                  c-new-END (max c-new-END (match-end 0))) ; Do we need 
c-new-END?
-           ))
+           (setq c-open-string-opener (1- (point)))))
          (goto-char (min (1+ (match-end 0)) (point-max))))
        (setq s nil)))))
 
@@ -2130,6 +2138,7 @@ with // and /*, not more generic line and block comments."
        ;; (c-new-BEG c-new-END) will be the region to fontify.
        (setq c-new-BEG beg  c-new-END end)
        (setq c-maybe-stale-found-type nil)
+       (setq c-open-string-opener nil)
        ;; A workaround for syntax-ppss's failure to notice syntax-table text
        ;; property changes.
        (when (fboundp 'syntax-ppss)
@@ -2394,7 +2403,7 @@ with // and /*, not more generic line and block comments."
                           (setq pseudo (c-cheap-inside-bracelist-p 
(c-parse-state)))))))
               (goto-char pseudo))
             t)
-          (> (point) bod-lim)
+          (>= (point) bod-lim)
           (progn (c-forward-syntactic-ws)
                  ;; Have we got stuck in a comment at EOB?
                  (not (and (eobp)
@@ -2418,7 +2427,8 @@ with // and /*, not more generic line and block comments."
             (and (> (point) bod-lim)
                  (or (memq (char-before) '(?\( ?\[))
                      (and (eq (char-before) ?\<)
-                          (eq (c-get-char-property
+                          (equal
+                           (c-get-char-property
                                (1- (point)) 'syntax-table)
                               c-<-as-paren-syntax))
                      (and (eq (char-before) ?{)
@@ -2448,9 +2458,12 @@ with // and /*, not more generic line and block 
comments."
   (goto-char pos)
   (let ((lit-start (c-literal-start))
        (lim (c-determine-limit 1000))
-       enclosing-attribute pos1)
+       enclosing-attribute pos1 ml-delim)
     (if lit-start
        (goto-char lit-start))
+    (when (and lit-start c-ml-string-opener-re
+              (setq ml-delim (c-ml-string-opener-around-point)))
+      (goto-char (car ml-delim)))
     (c-backward-syntactic-ws lim)
     (when (setq enclosing-attribute (c-enclosing-c++-attribute))
       (goto-char (car enclosing-attribute)) ; Only happens in C++ Mode.
@@ -2461,38 +2474,43 @@ with // and /*, not more generic line and block 
comments."
       (c-backward-syntactic-ws lim))
     (when (setq pos1 (c-on-identifier))
       (goto-char pos1)
-      (let ((lim (save-excursion
-                  (and (c-beginning-of-macro)
-                       (progn (c-end-of-macro) (point))))))
-       (and (c-forward-declarator lim)
-            (if (and (eq (char-after) ?\()
-                     (c-go-list-forward nil lim))
-                (and
-                 (progn (c-forward-syntactic-ws lim)
-                        (not (eobp)))
-                 (progn
-                   (if (looking-at c-symbol-char-key)
-                       ;; Deal with baz (foo((bar)) type var), where
-                       ;; foo((bar)) is not semantically valid.  The result
-                       ;; must be after var).
-                       (and
-                        (goto-char pos)
-                        (setq pos1 (c-on-identifier))
-                        (goto-char pos1)
-                        (progn
-                          (c-backward-syntactic-ws lim)
-                          (eq (char-before) ?\())
-                        (c-fl-decl-end (1- (point))))
-                     (c-backward-syntactic-ws lim)
-                     (point))))
-              (if (progn (c-forward-syntactic-ws lim)
-                         (not (eobp)))
-                  (c-forward-over-token)
-                (let ((lit-start (c-literal-start)))
-                  (when lit-start
-                      (goto-char lit-start))
-                  (c-backward-syntactic-ws)))
-              (and (>= (point) pos) (point))))))))
+      (let* ((lim1 (save-excursion
+                    (and (c-beginning-of-macro)
+                         (progn (c-end-of-macro) (point)))))
+            (decl-res (c-forward-declarator)))
+       (if (or (cadr (cddr (cddr decl-res))) ; We scanned an arglist.
+               (and (eq (char-after) ?\()    ; Move over a non arglist (...).
+                    (prog1 (c-go-list-forward)
+                      (c-forward-syntactic-ws))))
+           (if (looking-at c-symbol-char-key)
+               ;; Deal with baz (foo((bar)) type var), where `pos'
+               ;; was inside foo, but foo((bar)) is not semantically
+               ;; valid.  The result must be after var).
+               (and
+                (goto-char pos)
+                (setq pos1 (c-on-identifier))
+                (goto-char pos1)
+                (progn
+                  (c-backward-syntactic-ws lim1)
+                  (eq (char-before) ?\())
+                (c-fl-decl-end (1- (point))))
+             (c-forward-over-token)
+             (point))
+         (if (progn (c-forward-syntactic-ws)
+                    (not (eobp)))
+             (progn
+               (c-forward-over-token)
+               ;; Cope with having POS withing a syntactically invalid
+               ;; (...), by moving backward out of the parens and trying
+               ;; again.
+               (when (and (eq (char-before) ?\))
+                          (c-go-list-backward (point) lim1))
+                 (c-fl-decl-end (point))))
+           (let ((lit-start (c-literal-start)))
+             (when lit-start
+               (goto-char lit-start))
+             (c-backward-syntactic-ws)))
+         (and (>= (point) pos) (point)))))))
 
 (defun c-change-expand-fl-region (_beg _end _old-len)
   ;; Expand the region (c-new-BEG c-new-END) to an after-change font-lock
@@ -2698,11 +2716,9 @@ This function is called from `c-common-init', once per 
mode initialization."
 At the time of call, point is just after the newly inserted CHAR.
 
 When CHAR is \" and not within a comment, t will be returned if
-the quotes on the current line are already balanced (i.e. if the
-last \" is not marked with a string fence syntax-table text
-property).  For other cases, the default value of
-`electric-pair-inhibit-predicate' is called and its value
-returned.
+the quotes on the current line are already balanced.  For other
+cases, the default value of `electric-pair-inhibit-predicate' is
+called and its value returned.
 
 This function is the appropriate value of
 `electric-pair-inhibit-predicate' for CC Mode modes, which mark
@@ -2710,11 +2726,7 @@ invalid strings with such a syntax table text property 
on the
 opening \" and the next unescaped end of line."
   (if (and (eq char ?\")
           (not (memq (cadr (c-semi-pp-to-literal (1- (point)))) '(c c++))))
-      (let ((last-quote (save-match-data
-                         (save-excursion
-                           (goto-char (c-point 'eoll))
-                           (search-backward "\"")))))
-       (not (equal (c-get-char-property last-quote 'c-fl-syn-tab) '(15))))
+      (not c-open-string-opener)
     (funcall (default-value 'electric-pair-inhibit-predicate) char)))
 
 
@@ -3148,8 +3160,6 @@ Key bindings:
   (message "Using CC Mode version %s" c-version)
   (c-keep-region-active))
 
-(define-obsolete-variable-alias 'c-prepare-bug-report-hooks
-  'c-prepare-bug-report-hook "24.3")
 (defvar c-prepare-bug-report-hook nil)
 
 ;; Dynamic variables used by reporter.
diff --git a/lisp/progmodes/compile.el b/lisp/progmodes/compile.el
index 5ce80e0657..ded5d2130e 100644
--- a/lisp/progmodes/compile.el
+++ b/lisp/progmodes/compile.el
@@ -1235,10 +1235,10 @@ POS and RES.")
         (if win (set-window-point win pos)))
       (when compilation-auto-jump-to-first-error
         (cl-case compilation-auto-jump-to-first-error
-          ('if-location-known
+          (if-location-known
            (when (compilation--file-known-p)
             (compile-goto-error)))
-          ('first-known
+          (first-known
            (let (match)
              (while (and (not (compilation--file-known-p))
                          (setq match (text-property-search-forward
diff --git a/lisp/progmodes/cperl-mode.el b/lisp/progmodes/cperl-mode.el
index a3995e2969..20a73e238e 100644
--- a/lisp/progmodes/cperl-mode.el
+++ b/lisp/progmodes/cperl-mode.el
@@ -2,7 +2,7 @@
 
 ;; Copyright (C) 1985-2022 Free Software Foundation, Inc.
 
-;; Author: Ilya Zakharevich
+;; Author: Ilya Zakharevich <ilyaz@cpan.org>
 ;;     Bob Olson
 ;;     Jonathan Rockway <jon@jrock.us>
 ;; Maintainer: emacs-devel@gnu.org
@@ -24,8 +24,6 @@
 ;; You should have received a copy of the GNU General Public License
 ;; along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.
 
-;; Corrections made by Ilya Zakharevich ilyaz@cpan.org
-
 ;;; Commentary:
 
 ;; You can either fine-tune the bells and whistles of this mode or
@@ -54,7 +52,7 @@
 ;;     (define-key global-map [M-S-down-mouse-3] #'imenu)
 
 ;; This version supports the syntax added by the MooseX::Declare CPAN
-;; module, as well as Perl 5.10 keyword support.
+;; module, as well as Perl 5.10 keywords.
 
 ;;; Code:
 
@@ -634,7 +632,7 @@ mode-compile.el.
 If your Emacs does not default to `cperl-mode' on Perl files, and you
 want it to: put the following into your .emacs file:
 
-  (defalias \\='perl-mode \\='cperl-mode)
+  (add-to-list \\='major-mode-remap-alist \\='(perl-mode . cperl-mode))
 
 Get perl5-info from
   $CPAN/doc/manual/info/perl5-old/perl5-info.tar.gz
@@ -958,13 +956,6 @@ Unless KEEP, removes the old indentation."
   "Abbrev table in use in CPerl mode buffers."
   :parents (list cperl-mode-electric-keywords-abbrev-table))
 
-;; ;; TODO: Commented out as we don't know what it is used for.  If
-;; ;;       there are no bug reports about this for Emacs 28.1, this
-;; ;;       can probably be removed.  (Code search online reveals nothing.)
-;; (when (boundp 'edit-var-mode-alist)
-;;   ;; FIXME: What package uses this?
-;;   (add-to-list 'edit-var-mode-alist '(perl-mode (regexp . "^cperl-"))))
-
 (defvar cperl-mode-map
   (let ((map (make-sparse-keymap)))
     (define-key map "{" 'cperl-electric-lbrace)
@@ -3016,7 +3007,7 @@ and closing parentheses and brackets."
               ;; Now it is a hash reference
               (+ cperl-indent-level cperl-close-paren-offset))
             ;; Labels do not take :: ...
-            (if (looking-at "\\(\\w\\|_\\)+[ \t]*:")
+            (if (looking-at "\\(\\w\\|_\\)+[ \t]*:[^:]")
                 (if (> (current-indentation) cperl-min-label-indent)
                     (- (current-indentation) cperl-label-offset)
                   ;; Do not move `parse-data', this should
@@ -3171,7 +3162,7 @@ Returns true if comment is found.  In POD will not move 
the point."
 Mark as generic string if STRING, as generic comment otherwise.
 A single character is marked as punctuation and directly
 fontified.  Do nothing if BEGIN and END are equal.  If
-`cperl-use-syntax-text-property' is nil, just fontify."
+`cperl-use-syntax-table-text-property' is nil, just fontify."
   (if (and cperl-use-syntax-table-text-property
            (> end begin))
       (progn
@@ -3727,7 +3718,6 @@ This is part of `cperl-find-pods-heres' (below)."
           overshoot
           warning-message)))
 
-;; Debugging this may require (setq max-specpdl-size 2000)...
 (defun cperl-find-pods-heres (&optional min max non-inter end ignore-max 
end-of-here-doc)
   "Scan the buffer for hard-to-parse Perl constructions.
 If `cperl-pod-here-fontify' is non-nil after evaluation,
@@ -6045,39 +6035,6 @@ Style of printout regulated by the variable 
`cperl-ps-print-face-properties'."
     (ps-extend-face-list cperl-ps-print-face-properties)
     (ps-print-buffer-with-faces file)))
 
-;; (defun cperl-ps-print-init ()
-;;   "Initialization of `ps-print' components for faces used in CPerl."
-;;   ;; Guard against old versions
-;;   (defvar ps-underlined-faces nil)
-;;   (defvar ps-bold-faces nil)
-;;   (defvar ps-italic-faces nil)
-;;   (setq ps-bold-faces
-;;     (append '(font-lock-emphasized-face
-;;               cperl-array-face
-;;               font-lock-keyword-face
-;;               font-lock-variable-name-face
-;;               font-lock-constant-face
-;;               font-lock-reference-face
-;;               font-lock-other-emphasized-face
-;;               cperl-hash-face)
-;;             ps-bold-faces))
-;;   (setq ps-italic-faces
-;;     (append '(cperl-nonoverridable-face
-;;               font-lock-constant-face
-;;               font-lock-reference-face
-;;               font-lock-other-emphasized-face
-;;               cperl-hash-face)
-;;             ps-italic-faces))
-;;   (setq ps-underlined-faces
-;;     (append '(font-lock-emphasized-face
-;;               cperl-array-face
-;;               font-lock-other-emphasized-face
-;;               cperl-hash-face
-;;               cperl-nonoverridable-face font-lock-type-face)
-;;             ps-underlined-faces))
-;;   (cons 'font-lock-type-face ps-underlined-faces))
-
-
 (cperl-windowed-init)
 
 (defconst cperl-styles-entries
diff --git a/lisp/progmodes/elisp-mode.el b/lisp/progmodes/elisp-mode.el
index 4ada27a1ac..7e7ea6aeb9 100644
--- a/lisp/progmodes/elisp-mode.el
+++ b/lisp/progmodes/elisp-mode.el
@@ -220,8 +220,8 @@ All commands in `lisp-mode-shared-map' are inherited by 
this map."
 Load the compiled code when finished.
 
 Use `emacs-lisp-byte-compile-and-load' in combination with
-`native-comp-deferred-compilation' set to t to achieve asynchronous
-native compilation."
+`inhibit-automatic-native-compilation' set to nil to achieve
+asynchronous native compilation."
   (interactive nil emacs-lisp-mode)
   (emacs-lisp--before-compile-buffer)
   (load (native-compile buffer-file-name)))
diff --git a/lisp/progmodes/etags.el b/lisp/progmodes/etags.el
index db2c8efbd4..85c5992998 100644
--- a/lisp/progmodes/etags.el
+++ b/lisp/progmodes/etags.el
@@ -1784,10 +1784,10 @@ Bind `case-fold-search' during the evaluation, 
depending on the value of
 (defun tags--compat-initialize (initialize)
   (fileloop-initialize
    (tags--compat-files initialize)
+   (lambda () (tags-loop-eval tags-loop-scan))
    (if tags-loop-operate
        (lambda () (tags-loop-eval tags-loop-operate))
-     (lambda () (message "Scanning file %s...found" buffer-file-name) nil))
-   (lambda () (tags-loop-eval tags-loop-scan))))
+     (lambda () (message "Scanning file %s...found" buffer-file-name) nil))))
 
 ;;;###autoload
 (defun tags-loop-continue (&optional first-time)
diff --git a/lisp/progmodes/flymake.el b/lisp/progmodes/flymake.el
index 15ee5cb7d5..5bbbfa822f 100644
--- a/lisp/progmodes/flymake.el
+++ b/lisp/progmodes/flymake.el
@@ -1538,7 +1538,7 @@ POS can be a buffer position or a button"
    (flymake-show-diagnostic (if (button-type pos) (button-start pos) pos))))
 
 (defun flymake--tabulated-entries-1 (diags project-root)
-  "Helper for `flymake--diagnostic-buffer-entries'.
+  "Helper for `flymake--diagnostics-buffer-entries'.
 PROJECT-ROOT indicates that each entry should be preceded by the
 filename of the diagnostic relative to that directory."
   (cl-loop
diff --git a/lisp/progmodes/gdb-mi.el b/lisp/progmodes/gdb-mi.el
index bab80719db..6e8032b7ea 100644
--- a/lisp/progmodes/gdb-mi.el
+++ b/lisp/progmodes/gdb-mi.el
@@ -4033,11 +4033,12 @@ DOC is an optional documentation string."
          (file (gdb-mi--field frame 'fullname))
          (line (gdb-mi--field frame 'line)))
     (if file
-      (format "-data-disassemble -f %s -l %s -n -1 -- 0" file line)
-    ;; If we're unable to get a file name / line for $PC, simply
-    ;; follow $PC, disassembling the next 10 (x ~15 (on IA) ==
-    ;; 150 bytes) instructions.
-    "-data-disassemble -s $pc -e \"$pc + 150\" -- 0"))
+        (format "-data-disassemble -f %s -l %s -n -1 -- 0"
+                (file-local-name file) line)
+      ;; If we're unable to get a file name / line for $PC, simply
+      ;; follow $PC, disassembling the next 10 (x ~15 (on IA) ==
+      ;; 150 bytes) instructions.
+      "-data-disassemble -s $pc -e \"$pc + 150\" -- 0"))
   gdb-disassembly-handler
   ;; We update disassembly only after we have actual frame information
   ;; about all threads, so no there's `update' signal in this list
diff --git a/lisp/progmodes/glasses.el b/lisp/progmodes/glasses.el
index c7b0587336..c0bd6f220c 100644
--- a/lisp/progmodes/glasses.el
+++ b/lisp/progmodes/glasses.el
@@ -84,12 +84,22 @@ performed."
 
 
 (defcustom glasses-face nil
-  "Face to be put on capitals of an identifier looked through glasses.
-If it is nil, no face is placed at the capitalized letter.
+  "Face to use for capital letters of identifiers where separators were added.
+If it is nil, the capital letters will display with their usual faces.
 
 For example, you can set `glasses-separator' to an empty string and
 `glasses-face' to `bold'.  Then unreadable identifiers will have no separators,
-but will have their capitals in bold."
+but will have their capitals in bold.
+
+As another example, you may wish to have a clear visual indication of
+where the `glasses-separator' string was inserted by `glasses-mode',
+as opposed to where they are part of the original identifiers.  This
+can be useful when the program source code uses mixed CamelCase and
+normal_readable identifiers, and you want to know which underscores
+were added by this mode.  Customizing this face to something like `bold'
+will show the capital letters following the inserted `glasses-separator'
+in a distinct face.  Note that you must use `customize-variable' for
+changing the face; just assigning the value has no effect."
   :type '(choice (const :tag "None" nil) face)
   :set 'glasses-custom-set
   :initialize 'custom-initialize-default)
diff --git a/lisp/progmodes/gud.el b/lisp/progmodes/gud.el
index ccc5720575..281762fb0a 100644
--- a/lisp/progmodes/gud.el
+++ b/lisp/progmodes/gud.el
@@ -159,143 +159,96 @@ Used to gray out relevant toolbar icons.")
           (t
            (comint-interrupt-subjob)))))
 
-(easy-mmode-defmap gud-menu-map
-  '(([help]     "Info (debugger)" . gud-goto-info)
-    ([tooltips] menu-item "Show GUD tooltips" gud-tooltip-mode
-                  :enable (and (not emacs-basic-display)
-                              (display-graphic-p)
-                              (fboundp 'x-show-tip))
-                 :visible (memq gud-minor-mode
-                               '(gdbmi guiler dbx sdb xdb pdb))
-                 :button (:toggle . gud-tooltip-mode))
-    ([refresh] "Refresh" . gud-refresh)
-    ([run]     menu-item "Run" gud-run
-                  :enable (not gud-running)
-                 :visible (or (memq gud-minor-mode '(gdb dbx jdb))
-                              (and (eq gud-minor-mode 'gdbmi)
-                                   (or (not (gdb-show-run-p))
-                                       (bound-and-true-p
-                                        gdb-active-process)))))
-    ([go]     .        (menu-item (if (bound-and-true-p gdb-active-process)
-                              "Continue" "Run")
-                          gud-go
-                 :visible (and (eq gud-minor-mode 'gdbmi)
-                                (gdb-show-run-p))))
-    ([stop]    menu-item "Stop" gud-stop-subjob
-                 :visible (or (not (memq gud-minor-mode '(gdbmi pdb)))
-                              (and (eq gud-minor-mode 'gdbmi)
-                                    (gdb-show-stop-p))))
-    ([until]   menu-item "Continue to selection" gud-until
-                  :enable (not gud-running)
-                 :visible (and (memq gud-minor-mode '(gdbmi gdb perldb))
-                               (gud-tool-bar-item-visible-no-fringe)))
-    ([remove]  menu-item "Remove Breakpoint" gud-remove
-                  :enable (not gud-running)
-                 :visible (gud-tool-bar-item-visible-no-fringe))
-    ([tbreak]  menu-item "Temporary Breakpoint" gud-tbreak
-                  :enable (not gud-running)
-                 :visible (memq gud-minor-mode
-                               '(gdbmi gdb sdb xdb)))
-    ([break]   menu-item "Set Breakpoint" gud-break
-                  :enable (not gud-running)
-                 :visible (gud-tool-bar-item-visible-no-fringe))
-    ([up]      menu-item "Up Stack" gud-up
-                 :enable (not gud-running)
-                 :visible (memq gud-minor-mode
-                                '(gdbmi gdb guiler dbx xdb jdb pdb)))
-    ([down]    menu-item "Down Stack" gud-down
-                 :enable (not gud-running)
-                 :visible (memq gud-minor-mode
-                                '(gdbmi gdb guiler dbx xdb jdb pdb)))
-    ([pp]      menu-item "Print S-expression" gud-pp
-                  :enable (and (not gud-running)
-                                 (bound-and-true-p gdb-active-process))
-                 :visible (and (string-equal
-                                (buffer-local-value
-                                 'gud-target-name gud-comint-buffer)
-                                "emacs")
-                               (eq gud-minor-mode 'gdbmi)))
-    ([print*] . (menu-item (if (eq gud-minor-mode 'jdb)
-                              "Dump object"
-                            "Print Dereference")
-                          gud-pstar
-                  :enable (not gud-running)
-                 :visible (memq gud-minor-mode '(gdbmi gdb jdb))))
-    ([print]   menu-item "Print Expression" gud-print
-                  :enable (not gud-running))
-    ([watch]   menu-item "Watch Expression" gud-watch
-                 :enable (not gud-running)
-                 :visible (eq gud-minor-mode 'gdbmi))
-    ([finish]  menu-item "Finish Function" gud-finish
-                  :enable (not gud-running)
-                 :visible (memq gud-minor-mode
-                                '(gdbmi gdb guiler xdb jdb pdb)))
-    ([stepi]   menu-item "Step Instruction" gud-stepi
-                  :enable (not gud-running)
-                 :visible (memq gud-minor-mode '(gdbmi gdb dbx)))
-    ([nexti]   menu-item "Next Instruction" gud-nexti
-                  :enable (not gud-running)
-                 :visible (memq gud-minor-mode '(gdbmi gdb dbx)))
-    ([step]    menu-item "Step Line" gud-step
-                  :enable (not gud-running))
-    ([next]    menu-item "Next Line" gud-next
-                  :enable (not gud-running))
-    ([cont]    menu-item "Continue" gud-cont
-                  :enable (not gud-running)
-                 :visible (not (eq gud-minor-mode 'gdbmi))))
-  "Menu for `gud-mode'."
-  :name "Gud")
-
-(easy-mmode-defmap gud-minor-mode-map
-  (append
-     `(([menu-bar debug] . ("Gud" . ,gud-menu-map)))
-     ;; Get tool bar like functionality from the menu bar on a text only
-     ;; terminal.
-   (unless window-system
-     `(([menu-bar down]
-       . (,(propertize "down" 'face 'font-lock-doc-face) . gud-down))
-       ([menu-bar up]
-       . (,(propertize "up" 'face 'font-lock-doc-face) . gud-up))
-       ([menu-bar finish]
-       . (,(propertize "finish" 'face 'font-lock-doc-face) . gud-finish))
-       ([menu-bar step]
-       . (,(propertize "step" 'face 'font-lock-doc-face) . gud-step))
-       ([menu-bar next]
-       . (,(propertize "next" 'face 'font-lock-doc-face) . gud-next))
-       ([menu-bar until] menu-item
-       ,(propertize "until" 'face 'font-lock-doc-face) gud-until
-                 :visible (memq gud-minor-mode '(gdbmi gdb perldb)))
-       ([menu-bar cont] menu-item
-       ,(propertize "cont" 'face 'font-lock-doc-face) gud-cont
-       :visible (not (eq gud-minor-mode 'gdbmi)))
-       ([menu-bar run] menu-item
-       ,(propertize "run" 'face 'font-lock-doc-face) gud-run
-       :visible (memq gud-minor-mode '(gdbmi gdb dbx jdb)))
-       ([menu-bar go] menu-item
-       ,(propertize " go " 'face 'font-lock-doc-face) gud-go
-       :visible (and (eq gud-minor-mode 'gdbmi)
-                      (gdb-show-run-p)))
-       ([menu-bar stop] menu-item
-       ,(propertize "stop" 'face 'font-lock-doc-face) gud-stop-subjob
-       :visible (or (and (eq gud-minor-mode 'gdbmi)
-                          (gdb-show-stop-p))
-                    (not (eq gud-minor-mode 'gdbmi))))
-       ([menu-bar print]
-       . (,(propertize "print" 'face 'font-lock-doc-face) . gud-print))
-       ([menu-bar tools] . undefined)
-       ([menu-bar buffer] . undefined)
-       ([menu-bar options] . undefined)
-       ([menu-bar edit] . undefined)
-       ([menu-bar file] . undefined))))
-  "Map used in visited files.")
-
-(setf (alist-get 'gud-minor-mode minor-mode-map-alist)
-      gud-minor-mode-map)
-
-(defvar gud-mode-map
+(defvar-keymap gud-mode-map
   ;; Will inherit from comint-mode via define-derived-mode.
-  (make-sparse-keymap)
-  "`gud-mode' keymap.")
+  :doc "`gud-mode' keymap.")
+
+(defvar-keymap gud-minor-mode-map
+  :parent gud-mode-map)
+
+(easy-menu-define gud-menu-map gud-mode-map
+  "Menu for `gud-mode'."
+  '("Gud"
+    ["Continue" gud-cont
+     :enable (not gud-running)
+     :visible (not (eq gud-minor-mode 'gdbmi))]
+    ["Next Line" gud-next
+     :enable (not gud-running)]
+    ["Step Line" gud-step
+     :enable (not gud-running)]
+    ["Next Instruction" gud-nexti
+     :enable (not gud-running)
+     :visible (memq gud-minor-mode '(gdbmi gdb dbx))]
+    ["Step Instruction" gud-stepi
+     :enable (not gud-running)
+     :visible (memq gud-minor-mode '(gdbmi gdb dbx))]
+    ["Finish Function" gud-finish
+     :enable (not gud-running)
+     :visible (memq gud-minor-mode '(gdbmi gdb guiler xdb jdb pdb))]
+    ["Watch Expression" gud-watch
+     :enable (not gud-running)
+     :visible (eq gud-minor-mode 'gdbmi)]
+    ["Print Expression" gud-print
+     :enable (not gud-running)]
+    ["Dump object-Derefenrece" gud-pstar
+     :label (if (eq gud-minor-mode 'jdb)
+               "Dump object"
+              "Print Dereference")
+     :enable (not gud-running)
+     :visible (memq gud-minor-mode '(gdbmi gdb jdb))]
+    ["Print S-expression" gud-pp
+     :enable (and (not gud-running)
+                 (bound-and-true-p gdb-active-process))
+     :visible (and (string-equal
+                   (buffer-local-value
+                    'gud-target-name gud-comint-buffer)
+                   "emacs")
+                  (eq gud-minor-mode 'gdbmi))]
+    ["Down Stack" gud-down
+     :enable (not gud-running)
+     :visible (memq gud-minor-mode '(gdbmi gdb guiler dbx xdb jdb pdb))]
+    ["Up Stack" gud-up
+     :enable (not gud-running)
+     :visible (memq gud-minor-mode
+                   '(gdbmi gdb guiler dbx xdb jdb pdb))]
+    ["Set Breakpoint" gud-break
+     :enable (not gud-running)
+     :visible (gud-tool-bar-item-visible-no-fringe)]
+    ["Temporary Breakpoint" gud-tbreak
+     :enable (not gud-running)
+     :visible (memq gud-minor-mode '(gdbmi gdb sdb xdb))]
+    ["Remove Breakpoint" gud-remove
+     :enable (not gud-running)
+     :visible (gud-tool-bar-item-visible-no-fringe)]
+    ["Continue to selection" gud-until
+     :enable (not gud-running)
+     :visible (and (memq gud-minor-mode '(gdbmi gdb perldb))
+                  (gud-tool-bar-item-visible-no-fringe))]
+    ["Stop" gud-stop-subjob
+     :visible (or (not (memq gud-minor-mode '(gdbmi pdb)))
+                 (and (eq gud-minor-mode 'gdbmi)
+                       (gdb-show-stop-p)))]
+    ["Continue-Run" gud-go
+     :label (if (bound-and-true-p gdb-active-process)
+               "Continue" "Run")
+     :visible (and (eq gud-minor-mode 'gdbmi)
+                   (gdb-show-run-p))]
+    ["Run" gud-run
+     :enable (not gud-running)
+     :visible (or (memq gud-minor-mode '(gdb dbx jdb))
+                 (and (eq gud-minor-mode 'gdbmi)
+                      (or (not (gdb-show-run-p))
+                          (bound-and-true-p
+                           gdb-active-process))))]
+    ["Refresh" gud-refresh]
+    ["Show GUD tooltips" gud-tooltip-mode
+     :enable (and (not emacs-basic-display)
+                 (display-graphic-p)
+                 (fboundp 'x-show-tip))
+     :visible (memq gud-minor-mode
+                   '(gdbmi guiler dbx sdb xdb pdb))
+     :button (:toggle . gud-tooltip-mode)]
+    ["Info (debugger)" gud-goto-info]))
 
 (defvar gud-tool-bar-map
   (let ((map (make-sparse-keymap)))
diff --git a/lisp/progmodes/hideif.el b/lisp/progmodes/hideif.el
index d09e1f4cdf..767216c03f 100644
--- a/lisp/progmodes/hideif.el
+++ b/lisp/progmodes/hideif.el
@@ -196,9 +196,7 @@ Effective only if `hide-ifdef-expand-reinclusion-guard' is 
t."
   "C"   #'hif-clear-all-ifdef-defined
   "C-q" #'hide-ifdef-toggle-read-only
   "C-w" #'hide-ifdef-toggle-shadowing
-  "<remap> <read-only-mode>" #'hide-ifdef-toggle-outside-read-only
-  ;; `toggle-read-only' is obsoleted by `read-only-mode'.
-  "<remap> <toggle-read-only>" #'hide-ifdef-toggle-outside-read-only)
+  "<remap> <read-only-mode>" #'hide-ifdef-toggle-outside-read-only)
 
 (defcustom hide-ifdef-mode-prefix-key "\C-c@"
   "Prefix key for all Hide-Ifdef mode commands."
@@ -407,7 +405,7 @@ overlays created."
   ;; hidden with `hide-ifdef-lines' equals to nil while another part with 't,
   ;; this case happens.
   ;; TODO: Should we merge? or just create a container overlay? -- this can
-  ;; prevent `hideif-show-ifdef' expanding too many hidden contents since there
+  ;; prevent `show-ifdefs' expanding too many hidden contents since there
   ;; is only a big overlay exists there without any smaller overlays.
   (save-restriction
     (widen) ; Otherwise `point-min' and `point-max' will be restricted and thus
@@ -727,7 +725,7 @@ Assuming we've just regexp-matched with 
`hif-decfloat-regexp' and it matched.
 if REMATCH is t, do a rematch."
   ;; In elisp `(string-to-number "01.e2")' will return 1 instead of the 
expected
   ;; 100.0; therefore we need to write our own.
-  ;; This function relies on the regexp groups of `hif-dexfloat-regexp'
+  ;; This function relies on the regexp groups of `hif-hexfloat-regexp'
   (if (or fix exp)
       (setq fix (hif-delete-char-in-string ?' fix)
             exp (hif-delete-char-in-string ?' exp))
diff --git a/lisp/progmodes/hideshow.el b/lisp/progmodes/hideshow.el
index c0796fc2ee..2a1b6d6b7b 100644
--- a/lisp/progmodes/hideshow.el
+++ b/lisp/progmodes/hideshow.el
@@ -786,6 +786,14 @@ and `case-fold-search' are both t."
            (case-fold-search t))
        ,@body)))
 
+(defun hs-find-block-beginning-match ()
+  "Reposition point at the end of match of the block-start regexp.
+Return point, or nil if original point was not in a block."
+  (when (and (funcall hs-find-block-beginning-func)
+            (funcall hs-looking-at-block-start-p-func))
+    ;; point is inside a block
+    (goto-char (match-end 0))))
+
 (defun hs-overlay-at (position)
   "Return hideshow overlay at POSITION, or nil if none to be found."
   (let ((overlays (overlays-at position))
@@ -802,12 +810,13 @@ and `case-fold-search' are both t."
       (if (and c-reg (nth 0 c-reg))
           ;; point is inside a comment, and that comment is hideable
           (goto-char (nth 0 c-reg))
-        (end-of-line)
-        (when (and (not c-reg)
-                   (funcall hs-find-block-beginning-func)
-                  (funcall hs-looking-at-block-start-p-func))
-          ;; point is inside a block
-          (goto-char (match-end 0)))))
+        (when (not c-reg)
+          (end-of-line)
+          (when (not (hs-find-block-beginning-match))
+            ;; We should also consider ourselves "in" a hidden block when
+            ;; point is right at the edge after a hidden block (bug#52092).
+            (beginning-of-line)
+            (hs-find-block-beginning-match)))))
     (end-of-line)
     (hs-overlay-at (point))))
 
@@ -948,9 +957,9 @@ The hook `hs-hide-hook' is run; see `run-hooks'."
   "Toggle hiding/showing of a block.
 See `hs-hide-block' and `hs-show-block'.
 Argument E should be the event that triggered this action."
-  (interactive)
+  (interactive (list last-nonmenu-event))
   (hs-life-goes-on
-   (posn-set-point (event-end e))
+   (when e (posn-set-point (event-end e)))
    (if (hs-already-hidden-p)
        (hs-show-block)
      (hs-hide-block))))
diff --git a/lisp/progmodes/octave.el b/lisp/progmodes/octave.el
index 721dfa51ad..18b9899169 100644
--- a/lisp/progmodes/octave.el
+++ b/lisp/progmodes/octave.el
@@ -1722,12 +1722,12 @@ code line."
                  (dir (file-name-directory
                        (directory-file-name (file-name-directory file)))))
             (replace-match "" nil nil nil 1)
-            (insert (substitute-command-keys "`"))
+            (insert (substitute-quotes "`"))
             ;; Include the parent directory which may be regarded as
             ;; the category for the FN.
             (help-insert-xref-button (file-relative-name file dir)
                                      'octave-help-file fn)
-            (insert (substitute-command-keys "'"))))
+            (insert (substitute-quotes "'"))))
         ;; Make 'See also' clickable.
         (with-syntax-table octave-mode-syntax-table
           (when (re-search-forward "^\\s-*See also:" nil t)
diff --git a/lisp/progmodes/perl-mode.el b/lisp/progmodes/perl-mode.el
index 70cb460568..7b7a2cdf01 100644
--- a/lisp/progmodes/perl-mode.el
+++ b/lisp/progmodes/perl-mode.el
@@ -242,6 +242,12 @@
                                          (not (nth 3 (syntax-ppss
                                                       (match-beginning 0))))))
                             (string-to-syntax ". p"))))
+      ;; If "\" is acting as a backslash operator, it shouldn't start an
+      ;; escape sequence, so change its syntax.  This allows us to handle
+      ;; correctly the \() construct (Bug#11996) as well as references
+      ;; to string values.
+      ("\\(\\\\\\)['`\"($]" (1 (unless (nth 3 (syntax-ppss))
+                                       (string-to-syntax "."))))
       ;; Handle funny names like $DB'stop.
       ("\\$ ?{?\\^?[_[:alpha:]][_[:alnum:]]*\\('\\)[_[:alpha:]]" (1 "_"))
       ;; format statements
@@ -280,6 +286,7 @@
                                       (backward-sexp 1)
                                       (member (buffer-substring (point) end)
                                               
perl--syntax-exp-intro-keywords)))
+                               (bobp)
                                (memq (char-before)
                                      '(?? ?: ?. ?, ?\; ?= ?! ?~ ?\( ?\[))))))
                nil ;; A division sign instead of a regexp-match.
diff --git a/lisp/progmodes/prog-mode.el b/lisp/progmodes/prog-mode.el
index 7738de6a74..f87230bd2f 100644
--- a/lisp/progmodes/prog-mode.el
+++ b/lisp/progmodes/prog-mode.el
@@ -100,11 +100,9 @@
 
   menu)
 
-(defvar prog-mode-map
-  (let ((map (make-sparse-keymap)))
-    (define-key map [?\C-\M-q] 'prog-indent-sexp)
-    map)
-  "Keymap used for programming modes.")
+(defvar-keymap prog-mode-map
+  :doc "Keymap used for programming modes."
+  "C-M-q" #'prog-indent-sexp)
 
 (defvar prog-indentation-context nil
   "When non-nil, provides context for indenting embedded code chunks.
diff --git a/lisp/progmodes/project.el b/lisp/progmodes/project.el
index 30f51704dc..ee94d0d85d 100644
--- a/lisp/progmodes/project.el
+++ b/lisp/progmodes/project.el
@@ -1498,7 +1498,8 @@ the progress.  The function returns the number of detected
 projects."
   (interactive "DDirectory: \nP")
   (project--ensure-read-project-list)
-  (let ((queue (directory-files dir t nil t)) (count 0)
+  (let ((queue (list dir))
+        (count 0)
         (known (make-hash-table
                 :size (* 2 (length project--list))
                 :test #'equal )))
@@ -1506,15 +1507,20 @@ projects."
       (puthash project t known))
     (while queue
       (when-let ((subdir (pop queue))
-                 ((file-directory-p subdir))
-                 ((not (gethash subdir known))))
-        (when-let (pr (project--find-in-directory subdir))
-          (project-remember-project pr t)
-          (message "Found %s..." (project-root pr))
+                 ((file-directory-p subdir)))
+        (when-let ((project (project--find-in-directory subdir))
+                   (project-root (project-root project))
+                   ((not (gethash project-root known))))
+          (project-remember-project project t)
+          (puthash project-root t known)
+          (message "Found %s..." project-root)
           (setq count (1+ count)))
-        (when (and recursive (file-symlink-p subdir))
-          (setq queue (nconc (directory-files subdir t nil t) queue))
-          (puthash subdir t known))))
+        (when (and recursive (file-directory-p subdir))
+          (setq queue
+                (nconc
+                 (directory-files
+                  subdir t directory-files-no-dot-files-regexp t)
+                 queue)))))
     (unless (eq recursive 'in-progress)
       (if (zerop count)
           (message "No projects were found")
diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el
index b498baec60..801432cd18 100644
--- a/lisp/progmodes/python.el
+++ b/lisp/progmodes/python.el
@@ -5,7 +5,7 @@
 ;; Author: Fabián E. Gallina <fgallina@gnu.org>
 ;; URL: https://github.com/fgallina/python.el
 ;; Version: 0.28
-;; Package-Requires: ((emacs "24.4") (cl-lib "1.0"))
+;; Package-Requires: ((emacs "24.4") (compat "28.1.2.1") (seq "2.23"))
 ;; Maintainer: emacs-devel@gnu.org
 ;; Created: Jul 2010
 ;; Keywords: languages
@@ -34,7 +34,8 @@
 ;; Implements Syntax highlighting, Indentation, Movement, Shell
 ;; interaction, Shell completion, Shell virtualenv support, Shell
 ;; package support, Shell syntax highlighting, Pdb tracking, Symbol
-;; completion, Skeletons, FFAP, Code Check, ElDoc, Imenu.
+;; completion, Skeletons, FFAP, Code Check, ElDoc, Imenu, Flymake,
+;; Import management.
 
 ;; Syntax highlighting: Fontification of code is provided and supports
 ;; python's triple quoted strings properly.
@@ -69,7 +70,7 @@
 ;; variables.  This example enables IPython globally:
 
 ;; (setq python-shell-interpreter "ipython"
-;;       python-shell-interpreter-args "-i")
+;;       python-shell-interpreter-args "--simple-prompt")
 
 ;; Using the "console" subcommand to start IPython in server-client
 ;; mode is known to fail intermittently due a bug on IPython itself
@@ -240,6 +241,21 @@
 ;; I'd recommend the first one since you'll get the same behavior for
 ;; all modes out-of-the-box.
 
+;; Flymake: A Flymake backend, using the pyflakes program by default,
+;; is provided.  You can also use flake8 or pylint by customizing
+;; `python-flymake-command'.
+
+;; Import management: The commands `python-sort-imports',
+;; `python-add-import', `python-remove-import', and
+;; `python-fix-imports' automate the editing of import statements at
+;; the top of the buffer, which tend to be a tedious task in larger
+;; projects.  These commands require that the isort library is
+;; available to the interpreter pointed at by `python-interpreter'.
+;; The last command also requires pyflakes.  These dependencies can be
+;; installed, among other methods, with the following command:
+;;
+;;     pip install isort pyflakes
+
 ;;; Code:
 
 (require 'ansi-color)
@@ -248,6 +264,9 @@
 (eval-when-compile (require 'subr-x))   ;For `string-empty-p' and 
`string-join'.
 (require 'treesit)
 (require 'pcase)
+(require 'compat nil 'noerror)
+(require 'project nil 'noerror)
+(require 'seq)
 
 ;; Avoid compiler warnings
 (defvar compilation-error-regexp-alist)
@@ -273,6 +292,14 @@ Currently `python-mode' uses tree-sitter for font-locking, 
imenu,
 and movement functions."
   :type 'boolean)
 
+(defcustom python-interpreter "python"
+  "Python interpreter for noninteractive use.
+To customize the Python shell, modify `python-shell-interpreter'
+instead."
+  :version "29.1"
+  :type 'string)
+
+
 
 ;;; Bindings
 
@@ -282,6 +309,7 @@ and movement functions."
     (define-key map [remap backward-sentence] #'python-nav-backward-block)
     (define-key map [remap forward-sentence] #'python-nav-forward-block)
     (define-key map [remap backward-up-list] #'python-nav-backward-up-list)
+    (define-key map [remap up-list] #'python-nav-up-list)
     (define-key map [remap mark-defun] #'python-mark-defun)
     (define-key map "\C-c\C-j" #'imenu)
     ;; Indent specific
@@ -310,6 +338,11 @@ and movement functions."
     (define-key map "\C-c\C-v" #'python-check)
     (define-key map "\C-c\C-f" #'python-eldoc-at-point)
     (define-key map "\C-c\C-d" #'python-describe-at-point)
+    ;; Import management
+    (define-key map "\C-c\C-ia" #'python-add-import)
+    (define-key map "\C-c\C-if" #'python-fix-imports)
+    (define-key map "\C-c\C-ir" #'python-remove-import)
+    (define-key map "\C-c\C-is" #'python-sort-imports)
     ;; Utilities
     (substitute-key-definition #'complete-symbol #'completion-at-point
                                map global-map)
@@ -355,7 +388,17 @@ and movement functions."
         ["Help on symbol" python-eldoc-at-point
          :help "Get help on symbol at point"]
         ["Complete symbol" completion-at-point
-         :help "Complete symbol before point"]))
+         :help "Complete symbol before point"]
+        "-----"
+        ["Add import" python-add-import
+         :help "Add an import statement to the top of this buffer"]
+        ["Remove import" python-remove-import
+         :help "Remove an import statement from the top of this buffer"]
+        ["Sort imports" python-sort-imports
+         :help "Sort the import statements at the top of this buffer"]
+        ["Fix imports" python-fix-imports
+         :help "Add missing imports and remove unused ones from the current 
buffer"]
+        ))
     map)
   "Keymap for `python-mode'.")
 
@@ -495,16 +538,6 @@ The type returned can be `comment', `string' or `paren'."
   (eql (syntax-class (syntax-after (point)))
        (syntax-class (string-to-syntax ")"))))
 
-(define-obsolete-function-alias
-  'python-info-ppss-context #'python-syntax-context "24.3")
-
-(define-obsolete-function-alias
-  'python-info-ppss-context-type #'python-syntax-context-type "24.3")
-
-(define-obsolete-function-alias
-  'python-info-ppss-comment-or-string-p
-  #'python-syntax-comment-or-string-p "24.3")
-
 (defun python-font-lock-syntactic-face-function (state)
   "Return syntactic face given STATE."
   (if (nth 3 state)
@@ -513,11 +546,22 @@ The type returned can be `comment', `string' or `paren'."
         font-lock-string-face)
     font-lock-comment-face))
 
+(defconst python--f-string-start-regexp
+  (rx bow
+      (or "f" "F" "fr" "Fr" "fR" "FR" "rf" "rF" "Rf" "RF")
+      (or "\"" "\"\"\"" "'" "'''"))
+  "A regular expression matching the beginning of an f-string.
+
+See URL 
`https://docs.python.org/3/reference/lexical_analysis.html#string-and-bytes-literals'.")
+
 (defun python--f-string-p (ppss)
   "Return non-nil if the pos where PPSS was found is inside an f-string."
   (and (nth 3 ppss)
-       (let ((spos (1- (nth 8 ppss))))
-         (and (memq (char-after spos) '(?f ?F))
+       (let* ((spos (1- (nth 8 ppss)))
+              (before-quote
+               (buffer-substring-no-properties (max (- spos 4) (point-min))
+                                               (min (+ spos 2) (point-max)))))
+         (and (string-match-p python--f-string-start-regexp before-quote)
               (or (< (point-min) spos)
                   (not (memq (char-syntax (char-before spos)) '(?w ?_))))))))
 
@@ -536,7 +580,7 @@ the {...} holes that appear within f-strings."
     (while
         (progn
           (while (and (not (python--f-string-p ppss))
-                      (re-search-forward "\\<f['\"]" limit 'move))
+                      (re-search-forward python--f-string-start-regexp limit 
'move))
             (setq ppss (syntax-ppss)))
           (< (point) limit))
       (cl-assert (python--f-string-p ppss))
@@ -1050,17 +1094,11 @@ Do not fontify the initial f for f-strings."
 
 ;;; Indentation
 
-(define-obsolete-variable-alias
-  'python-indent 'python-indent-offset "24.3")
-
 (defcustom python-indent-offset 4
   "Default indentation offset for Python."
   :type 'integer
   :safe 'integerp)
 
-(define-obsolete-variable-alias
-  'python-guess-indent 'python-indent-guess-indent-offset "24.3")
-
 (defcustom python-indent-guess-indent-offset t
   "Non-nil tells Python mode to guess `python-indent-offset' value."
   :type 'boolean
@@ -2452,6 +2490,16 @@ virtualenv."
   "`compilation-error-regexp-alist' for inferior Python."
   :type '(alist regexp))
 
+(defcustom python-shell-dedicated nil
+  "Whether to make Python shells dedicated by default.
+This option influences `run-python' when called without a prefix
+argument.  If `buffer' or `project', create a Python shell
+dedicated to the current buffer or its project (if one is found)."
+  :version "29.1"
+  :type '(choice (const :tag "To buffer" buffer)
+                 (const :tag "To project" project)
+                 (const :tag "Not dedicated" nil)))
+
 (defvar python-shell-output-filter-in-progress nil)
 (defvar python-shell-output-filter-buffer nil)
 
@@ -2814,12 +2862,19 @@ from `python-shell-prompt-regexp',
 
 (defun python-shell-get-process-name (dedicated)
   "Calculate the appropriate process name for inferior Python process.
-If DEDICATED is t returns a string with the form
-`python-shell-buffer-name'[`buffer-name'] else returns the value
-of `python-shell-buffer-name'."
-  (if dedicated
-      (format "%s[%s]" python-shell-buffer-name (buffer-name))
-    python-shell-buffer-name))
+If DEDICATED is nil, this is simply `python-shell-buffer-name'.
+If DEDICATED is `buffer' or `project', append the current buffer
+name respectively the current project name."
+  (pcase dedicated
+    ('nil python-shell-buffer-name)
+    ('project
+     (if-let ((proj (and (featurep 'project)
+                         (project-current))))
+         (format "%s[%s]" python-shell-buffer-name (file-name-nondirectory
+                                                    (directory-file-name
+                                                     (project-root proj))))
+       python-shell-buffer-name))
+    (_ (format "%s[%s]" python-shell-buffer-name (buffer-name)))))
 
 (defun python-shell-internal-get-process-name ()
   "Calculate the appropriate process name for Internal Python process.
@@ -3182,8 +3237,8 @@ interpreter is run.  Variables
 `python-shell-font-lock-enable',
 `python-shell-completion-setup-code',
 `python-shell-completion-string-code',
-`python-eldoc-setup-code', `python-eldoc-string-code',
-`python-ffap-setup-code' and `python-ffap-string-code' can
+`python-eldoc-setup-code',
+`python-ffap-setup-code' can
 customize this mode for different Python interpreters.
 
 This mode resets `comint-output-filter-functions' locally, so you
@@ -3277,8 +3332,8 @@ killed."
 Argument CMD defaults to `python-shell-calculate-command' return
 value.  When called interactively with `prefix-arg', it allows
 the user to edit such value and choose whether the interpreter
-should be DEDICATED for the current buffer.  When numeric prefix
-arg is other than 0 or 4 do not SHOW.
+should be DEDICATED to the current buffer or project.  When
+numeric prefix arg is other than 0 or 4 do not SHOW.
 
 For a given buffer and same values of DEDICATED, if a process is
 already running for it, it will do nothing.  This means that if
@@ -3292,15 +3347,47 @@ process buffer for a list of commands.)"
    (if current-prefix-arg
        (list
         (read-shell-command "Run Python: " (python-shell-calculate-command))
-        (y-or-n-p "Make dedicated process? ")
+        (alist-get (car (read-multiple-choice "Make dedicated process?"
+                                              '((?b "to buffer")
+                                                (?p "to project")
+                                                (?n "no"))))
+                   '((?b . buffer) (?p . project)))
         (= (prefix-numeric-value current-prefix-arg) 4))
-     (list (python-shell-calculate-command) nil t)))
-  (let ((buffer
-         (python-shell-make-comint
-          (or cmd (python-shell-calculate-command))
-          (python-shell-get-process-name dedicated) show)))
+     (list (python-shell-calculate-command)
+           python-shell-dedicated
+           t)))
+  (let* ((project (and (eq 'project dedicated)
+                       (featurep 'project)
+                       (project-current t)))
+         (default-directory (if project
+                                (project-root project)
+                              default-directory))
+         (buffer (python-shell-make-comint
+                  (or cmd (python-shell-calculate-command))
+                  (python-shell-get-process-name dedicated)
+                  show)))
     (get-buffer-process buffer)))
 
+(defun python-shell-restart (&optional show)
+  "Restart the Python shell.
+Optional argument SHOW (interactively, the prefix argument), if
+non-nil, means also display the Python shell buffer."
+  (interactive "P")
+  (with-current-buffer
+      (or (and (derived-mode-p 'inferior-python-mode)
+               (current-buffer))
+          (seq-some (lambda (dedicated)
+                      (get-buffer (format "*%s*" (python-shell-get-process-name
+                                                  dedicated))))
+                    '(buffer project nil))
+          (user-error "No Python shell"))
+    (when-let ((proc (get-buffer-process (current-buffer))))
+      (kill-process proc)
+      (while (accept-process-output proc)))
+    (python-shell-make-comint (python-shell-calculate-command)
+                              (string-trim (buffer-name) "\\*" "\\*")
+                              show)))
+
 (defun run-python-internal ()
   "Run an inferior Internal Python process.
 Input and output via buffer named after
@@ -3328,15 +3415,13 @@ startup."
 If current buffer is in `inferior-python-mode', return it."
   (if (derived-mode-p 'inferior-python-mode)
       (current-buffer)
-    (let* ((dedicated-proc-name (python-shell-get-process-name t))
-           (dedicated-proc-buffer-name (format "*%s*" dedicated-proc-name))
-           (global-proc-name  (python-shell-get-process-name nil))
-           (global-proc-buffer-name (format "*%s*" global-proc-name))
-           (dedicated-running (comint-check-proc dedicated-proc-buffer-name))
-           (global-running (comint-check-proc global-proc-buffer-name)))
-      ;; Always prefer dedicated
-      (or (and dedicated-running dedicated-proc-buffer-name)
-          (and global-running global-proc-buffer-name)))))
+    (seq-some
+     (lambda (dedicated)
+       (let* ((proc-name (python-shell-get-process-name dedicated))
+              (buffer-name (format "*%s*" proc-name)))
+         (when (comint-check-proc buffer-name)
+           buffer-name)))
+     '(buffer project nil))))
 
 (defun python-shell-get-process ()
   "Return inferior Python process for current buffer."
@@ -3377,17 +3462,11 @@ be asked for their values."
  "Instead call `python-shell-get-process' and create one if returns nil."
  "25.1")
 
-(define-obsolete-variable-alias
-  'python-buffer 'python-shell-internal-buffer "24.3")
-
 (defvar python-shell-internal-buffer nil
   "Current internal shell buffer for the current buffer.
 This is really not necessary at all for the code to work but it's
 there for compatibility with CEDET.")
 
-(define-obsolete-variable-alias
-  'python-preoutput-result 'python-shell-internal-last-output "24.3")
-
 (defvar python-shell-internal-last-output nil
   "Last output captured by the internal shell.
 This is really not necessary at all for the code to work but it's
@@ -3400,9 +3479,6 @@ there for compatibility with CEDET.")
         (get-process proc-name)
       (run-python-internal))))
 
-(define-obsolete-function-alias
-  'python-proc #'python-shell-internal-get-or-create-process "24.3")
-
 (defun python-shell--save-temp-file (string)
   (let* ((temporary-file-directory
           (if (file-remote-p default-directory)
@@ -3519,12 +3595,6 @@ Returns the output.  See 
`python-shell-send-string-no-output'."
          (replace-regexp-in-string "_emacs_out +" "" string)
          (python-shell-internal-get-or-create-process))))
 
-(define-obsolete-function-alias
-  'python-send-receive #'python-shell-internal-send-string "24.3")
-
-(define-obsolete-function-alias
-  'python-send-string #'python-shell-internal-send-string "24.3")
-
 (defun python-shell-buffer-substring (start end &optional nomain no-cookie)
   "Send buffer substring from START to END formatted for shell.
 This is a wrapper over `buffer-substring' that takes care of
@@ -4690,9 +4760,6 @@ JUSTIFY should be used (if applicable) as in 
`fill-paragraph'."
 
 ;;; Skeletons
 
-(define-obsolete-variable-alias
-  'python-use-skeletons 'python-skeleton-autoinsert "24.3")
-
 (defcustom python-skeleton-autoinsert nil
   "Non-nil means template skeletons will be automagically inserted.
 This happens when pressing \"if<SPACE>\", for example, to prompt for
@@ -5696,11 +5763,11 @@ operator."
   "Check if point is at `beginning-of-defun' using SYNTAX-PPSS.
 When CHECK-STATEMENT is non-nil, the current statement is checked
 instead of the current physical line."
-  (and (not (python-syntax-context-type (or syntax-ppss (syntax-ppss))))
-       (save-excursion
-         (when check-statement
-           (python-nav-beginning-of-statement))
-         (beginning-of-line 1)
+  (save-excursion
+    (when check-statement
+      (python-nav-beginning-of-statement))
+    (beginning-of-line 1)
+    (and (not (python-syntax-context-type (or syntax-ppss (syntax-ppss))))
          (looking-at python-nav-beginning-of-defun-regexp))))
 
 (defun python-info-looking-at-beginning-of-block ()
@@ -6078,6 +6145,225 @@ REPORT-FN is Flymake's callback function."
       (process-send-eof python--flymake-proc))))
 
 
+;;; Import management
+(defconst python--list-imports "\
+from isort import find_imports_in_stream, find_imports_in_paths
+from sys import argv, stdin
+
+query, files, result = argv[1] or None, argv[2:], {}
+
+if files:
+    imports = find_imports_in_paths(files, top_only=True)
+else:
+    imports = find_imports_in_stream(stdin, top_only=True)
+
+for imp in imports:
+    if query is None or query == (imp.alias or imp.attribute or imp.module):
+        key = (imp.module, imp.attribute or '', imp.alias or '')
+        if key not in result:
+            result[key] = imp.statement()
+
+for key in sorted(result):
+    print(result[key])
+"
+  "Script to list import statements in Python code.")
+
+(defvar python-import-history nil
+  "History variable for `python-import' commands.")
+
+(defun python--import-sources ()
+  "List files containing Python imports that may be useful in the current 
buffer."
+  (if-let (((featurep 'project))        ;For compatibility with Emacs < 26
+           (proj (project-current)))
+      (seq-filter (lambda (s) (string-match-p "\\.py[ciw]?\\'" s))
+                  (project-files proj))
+    (list default-directory)))
+
+(defun python--list-imports (name source)
+  "List all Python imports matching NAME in SOURCE.
+If NAME is nil, list all imports.  SOURCE can be a buffer or a
+list of file names or directories; the latter are searched
+recursively."
+  (let ((buffer (current-buffer)))
+    (with-temp-buffer
+      (let* ((temp (current-buffer))
+             (status (if (bufferp source)
+                         (with-current-buffer source
+                           (call-process-region (point-min) (point-max)
+                                                python-interpreter
+                                                nil (list temp nil) nil
+                                                "-c" python--list-imports
+                                                (or name "")))
+                       (with-current-buffer buffer
+                         (apply #'call-process
+                                python-interpreter
+                                nil (list temp nil) nil
+                                "-c" python--list-imports
+                                (or name "")
+                                (mapcar #'file-local-name source)))))
+             lines)
+        (unless (eq 0 status)
+          (error "%s exited with status %s (maybe isort is missing?)"
+                 python-interpreter status))
+        (goto-char (point-min))
+        (while (not (eobp))
+         (push (buffer-substring-no-properties (point) (pos-eol))
+                lines)
+         (forward-line 1))
+        (nreverse lines)))))
+
+(defun python--query-import (name source prompt)
+  "Read a Python import statement defining NAME.
+A list of candidates is produced by `python--list-imports' using
+the NAME and SOURCE arguments.  An interactive query, using the
+PROMPT string, is made unless there is a single candidate."
+  (let* ((cands (python--list-imports name source))
+         ;; Don't use DEF argument of `completing-read', so it is able
+         ;; to return the empty string.
+         (minibuffer-default-add-function
+          (lambda ()
+            (setq minibuffer-default (with-minibuffer-selected-window
+                                       (thing-at-point 'symbol)))))
+         (statement (cond ((and name (length= cands 1))
+                           (car cands))
+                          (prompt
+                           (completing-read prompt
+                                            (or cands python-import-history)
+                                            nil nil nil
+                                            'python-import-history)))))
+    (unless (string-empty-p statement)
+      statement)))
+
+(defun python--do-isort (&rest args)
+  "Edit the current buffer using isort called with ARGS.
+Return non-nil if the buffer was actually modified."
+  (let ((buffer (current-buffer)))
+    (with-temp-buffer
+      (let ((temp (current-buffer)))
+        (with-current-buffer buffer
+          (let ((status (apply #'call-process-region
+                               (point-min) (point-max)
+                               python-interpreter
+                               nil (list temp nil) nil
+                               "-m" "isort" "-" args))
+                (tick (buffer-chars-modified-tick)))
+            (unless (eq 0 status)
+              (error "%s exited with status %s (maybe isort is missing?)"
+                     python-interpreter status))
+            (replace-buffer-contents temp)
+            (not (eq tick (buffer-chars-modified-tick)))))))))
+
+;;;###autoload
+(defun python-add-import (name)
+  "Add an import statement to the current buffer.
+
+Interactively, ask for an import statement using all imports
+found in the current project as suggestions.  With a prefix
+argument, restrict the suggestions to imports defining the symbol
+at point.  If there is only one such suggestion, act without
+asking.
+
+When calling from Lisp, use a non-nil NAME to restrict the
+suggestions to imports defining NAME."
+  (interactive (list (when current-prefix-arg (thing-at-point 'symbol))))
+  (when-let ((statement (python--query-import name
+                                              (python--import-sources)
+                                              "Add import: ")))
+    (if (python--do-isort "--add" statement)
+        (message "Added `%s'" statement)
+      (message "(No changes in Python imports needed)"))))
+
+;;;###autoload
+(defun python-import-symbol-at-point ()
+  "Add an import statement for the symbol at point to the current buffer.
+This works like `python-add-import', but with the opposite
+behavior regarding the prefix argument."
+  (interactive nil)
+  (python-add-import (unless current-prefix-arg (thing-at-point 'symbol))))
+
+;;;###autoload
+(defun python-remove-import (name)
+  "Remove an import statement from the current buffer.
+
+Interactively, ask for an import statement to remove, displaying
+the imports of the current buffer as suggestions.  With a prefix
+argument, restrict the suggestions to imports defining the symbol
+at point.  If there is only one such suggestion, act without
+asking."
+  (interactive (list (when current-prefix-arg (thing-at-point 'symbol))))
+  (when-let ((statement (python--query-import name (current-buffer)
+                                              "Remove import: ")))
+    (if (python--do-isort "--rm" statement)
+        (message "Removed `%s'" statement)
+      (message "(No changes in Python imports needed)"))))
+
+;;;###autoload
+(defun python-sort-imports ()
+  "Sort Python imports in the current buffer."
+  (interactive)
+  (if (python--do-isort)
+      (message "Sorted imports")
+    (message "(No changes in Python imports needed)")))
+
+;;;###autoload
+(defun python-fix-imports ()
+  "Add missing imports and remove unused ones from the current buffer."
+  (interactive)
+  (let ((buffer (current-buffer))
+        undefined unused add remove)
+    ;; Compute list of undefined and unused names
+    (with-temp-buffer
+      (let ((temp (current-buffer)))
+        (with-current-buffer buffer
+          (call-process-region (point-min) (point-max)
+                               python-interpreter
+                               nil temp nil
+                               "-m" "pyflakes"))
+        (goto-char (point-min))
+        (when (looking-at-p ".* No module named pyflakes$")
+          (error "%s couldn't find pyflakes" python-interpreter))
+        (while (not (eobp))
+          (cond ((looking-at ".* undefined name '\\([^']+\\)'$")
+                 (push (match-string 1) undefined))
+                ((looking-at ".*'\\([^']+\\)' imported but unused$")
+                 (push (match-string 1) unused)))
+         (forward-line 1))))
+    ;; Compute imports to be added
+    (dolist (name (seq-uniq undefined))
+      (when-let ((statement (python--query-import name
+                                                  (python--import-sources)
+                                                  (format "\
+Add import for undefined name `%s' (empty to skip): "
+                                                          name))))
+        (push statement add)))
+    ;; Compute imports to be removed
+    (dolist (name (seq-uniq unused))
+      ;; The unused imported names, as provided by pyflakes, are of
+      ;; the form "module.var" or "module.var as alias", independently
+      ;; of style of import statement used.
+      (let* ((filter
+              (lambda (statement)
+                (string= name
+                         (thread-last
+                           statement
+                           (replace-regexp-in-string "^\\(from\\|import\\) " 
"")
+                           (replace-regexp-in-string " import " ".")))))
+             (statements (seq-filter filter (python--list-imports nil 
buffer))))
+        (when (length= statements 1)
+          (push (car statements) remove))))
+    ;; Edit buffer and say goodbye
+    (if (not (or add remove))
+        (message "(No changes in Python imports needed)")
+      (apply #'python--do-isort
+             (append (mapcan (lambda (x) (list "--add" x)) add)
+                     (mapcan (lambda (x) (list "--rm" x)) remove)))
+      (message "%s" (concat (when add "Added ")
+                            (when add (string-join add ", "))
+                            (when remove (if add " and removed " "Removed "))
+                            (when remove (string-join remove ", " )))))))
+
+
+;;; Major mode
 (defun python-electric-pair-string-delimiter ()
   (when (and electric-pair-mode
              (memq last-command-event '(?\" ?\'))
@@ -6214,8 +6500,10 @@ REPORT-FN is Flymake's callback function."
 
 ;;; Completion predicates for M-x
 ;; Commands that only make sense when editing Python code
-(dolist (sym '(python-check
+(dolist (sym '(python-add-import
+               python-check
                python-fill-paragraph
+               python-fix-imports
                python-indent-dedent-line
                python-indent-dedent-line-backspace
                python-indent-guess-indent-offset
@@ -6240,9 +6528,11 @@ REPORT-FN is Flymake's callback function."
                python-nav-forward-statement
                python-nav-if-name-main
                python-nav-up-list
+               python-remove-import
                python-shell-send-buffer
                python-shell-send-defun
-               python-shell-send-statement))
+               python-shell-send-statement
+               python-sort-imports))
   (put sym 'completion-predicate #'python--completion-predicate))
 
 (defun python-shell--completion-predicate (_ buffer)
diff --git a/lisp/progmodes/sh-script.el b/lisp/progmodes/sh-script.el
index be9f325d93..558b62b20a 100644
--- a/lisp/progmodes/sh-script.el
+++ b/lisp/progmodes/sh-script.el
@@ -31,6 +31,9 @@
 ;; available for filenames, variables known from the script, the shell and
 ;; the environment as well as commands.
 
+;; A Flymake backend using the "shellcheck" program is provided.  See
+;; https://www.shellcheck.net/ for installation instructions.
+
 ;;; Known Bugs:
 
 ;; - In Bourne the keyword `in' is not anchored to case, for, select ...
@@ -141,7 +144,9 @@
 (eval-when-compile
   (require 'skeleton)
   (require 'cl-lib)
-  (require 'comint))
+  (require 'comint)
+  (require 'let-alist)
+  (require 'subr-x))
 (require 'executable)
 
 (autoload 'comint-completion-at-point "comint")
@@ -1580,6 +1585,7 @@ with your script for an edit-interpret-debug cycle."
         ((equal (file-name-nondirectory buffer-file-name) ".profile") "sh")
          (t sh-shell-file))
    nil nil)
+  (add-hook 'flymake-diagnostic-functions #'sh-shellcheck-flymake nil t)
   (add-hook 'hack-local-variables-hook
     #'sh-after-hack-local-variables nil t))
 
@@ -2982,14 +2988,6 @@ option followed by a colon `:' if the option accepts an 
argument."
           (match-string 1))))))
 
 
-(defun sh-maybe-here-document (arg)
-  "Insert self.  Without prefix, following unquoted `<' inserts here document.
-The document is bounded by `sh-here-document-word'."
-  (declare (obsolete sh-electric-here-document-mode "24.3"))
-  (interactive "*P")
-  (self-insert-command (prefix-numeric-value arg))
-  (or arg (sh--maybe-here-document)))
-
 (defun sh--maybe-here-document ()
   (when (and (looking-back "[^<]<<[ E-]" (line-beginning-position))
              (save-excursion
@@ -3111,6 +3109,88 @@ shell command and conveniently use this command."
            (delete-region (1+ (point))
                           (progn (skip-chars-backward " \t") (point)))))))
 
+;;; Flymake backend
+
+(defcustom sh-shellcheck-program "shellcheck"
+  "Name of the shellcheck executable."
+  :type 'string
+  :version "29.1")
+
+(defcustom sh-shellcheck-arguments nil
+  "Additional arguments to the shellcheck program."
+  :type '(repeat string)
+  :version "29.1")
+
+(defvar-local sh--shellcheck-process nil)
+
+(defalias 'sh--json-read
+  (if (fboundp 'json-parse-buffer)
+      (lambda () (json-parse-buffer :object-type 'alist))
+    (require 'json)
+    'json-read))
+
+(defun sh-shellcheck-flymake (report-fn &rest _args)
+  "Flymake backend using the shellcheck program.
+Takes a Flymake callback REPORT-FN as argument, as expected of a
+member of `flymake-diagnostic-functions'."
+  (when (process-live-p sh--shellcheck-process)
+    (kill-process sh--shellcheck-process))
+  (let* ((source (current-buffer))
+         (dialect (named-let recur ((s sh-shell))
+                    (pcase s
+                      ((or 'bash 'dash 'sh) (symbol-name s))
+                      ('ksh88 "ksh")
+                      ((guard s)
+                       (recur (alist-get s sh-ancestor-alist))))))
+         (sentinel
+          (lambda (proc _event)
+            (when (memq (process-status proc) '(exit signal))
+              (unwind-protect
+                  (if (with-current-buffer source
+                        (not (eq proc sh--shellcheck-process)))
+                      (flymake-log :warning "Canceling obsolete check %s" proc)
+                    (with-current-buffer (process-buffer proc)
+                      (goto-char (point-min))
+                      (thread-last
+                        (sh--json-read)
+                        (alist-get 'comments)
+                        (seq-filter
+                         (lambda (item)
+                           (let-alist item (string= .file "-"))))
+                        (mapcar
+                         (lambda (item)
+                           (let-alist item
+                             (flymake-make-diagnostic
+                              source
+                              (cons .line .column)
+                              (unless (and (eq .line .endLine)
+                                           (eq .column .endColumn))
+                                (cons .endLine .endColumn))
+                              (pcase .level
+                                ("error" :error)
+                                ("warning" :warning)
+                                (_ :note))
+                              (format "SC%s: %s" .code .message)))))
+                        (funcall report-fn))))
+                (kill-buffer (process-buffer proc)))))))
+    (unless dialect
+      (error "`sh-shellcheck-flymake' is not suitable for shell type `%s'"
+             sh-shell))
+    (setq sh--shellcheck-process
+          (make-process
+           :name "shellcheck" :noquery t :connection-type 'pipe
+           :buffer (generate-new-buffer " *flymake-shellcheck*")
+           :command `(,sh-shellcheck-program
+                      "--format=json1"
+                      "-s" ,dialect
+                      ,@sh-shellcheck-arguments
+                      "-")
+           :sentinel sentinel))
+    (save-restriction
+      (widen)
+      (process-send-region sh--shellcheck-process (point-min) (point-max))
+      (process-send-eof sh--shellcheck-process))))
+
 (provide 'sh-script)
 
 ;;; sh-script.el ends here
diff --git a/lisp/progmodes/subword.el b/lisp/progmodes/subword.el
index e06eb9a6f7..34327f756e 100644
--- a/lisp/progmodes/subword.el
+++ b/lisp/progmodes/subword.el
@@ -79,12 +79,11 @@
   "\\(\\(\\W\\|[[:lower:][:digit:]]\\)\\([[:upper:]]+\\W*\\)\\|\\W\\w+\\)"
   "Regexp used by `subword-backward-internal'.")
 
-(defvar subword-mode-map
+(defvar-keymap subword-mode-map
   ;; We originally remapped motion keys here, but now use Emacs core
   ;; hooks.  Leave this keymap around so that user additions to it
   ;; keep working.
-  (make-sparse-keymap)
-  "Keymap used in `subword-mode' minor mode.")
+  :doc "Keymap used in `subword-mode' minor mode.")
 
 ;;;###autoload
 (define-obsolete-function-alias
diff --git a/lisp/progmodes/xref.el b/lisp/progmodes/xref.el
index a7e372c2ac..ac04b64ce5 100644
--- a/lisp/progmodes/xref.el
+++ b/lisp/progmodes/xref.el
@@ -1,7 +1,7 @@
 ;;; xref.el --- Cross-referencing commands              -*-lexical-binding:t-*-
 
 ;; Copyright (C) 2014-2022 Free Software Foundation, Inc.
-;; Version: 1.5.0
+;; Version: 1.5.1
 ;; Package-Requires: ((emacs "26.1"))
 
 ;; This is a GNU ELPA :core package.  Avoid functionality that is not
diff --git a/lisp/recentf.el b/lisp/recentf.el
index b80ee3dd7d..32badb1a37 100644
--- a/lisp/recentf.el
+++ b/lisp/recentf.el
@@ -411,7 +411,8 @@ returned nil."
   "Convert file NAME to absolute, and canonicalize it.
 NAME is first passed to the function `expand-file-name', then to
 `recentf-filename-handlers' to post process it."
-  (recentf-apply-filename-handlers (expand-file-name name)))
+  (let ((non-essential t))
+    (recentf-apply-filename-handlers (expand-file-name name))))
 
 (defun recentf-include-p (filename)
   "Return non-nil if FILENAME should be included in the recent list.
diff --git a/lisp/repeat.el b/lisp/repeat.el
index a32f3a4c50..0ae68d6024 100644
--- a/lisp/repeat.el
+++ b/lisp/repeat.el
@@ -89,9 +89,15 @@
 
 ;;;;; ************************* USER OPTIONS ************************** ;;;;;
 
+(defgroup repeat nil
+  "Convenient way to repeat previous commands."
+  :prefix "repeat-"
+  :version "29.1"
+  :group 'convenience)
+
 (defcustom repeat-too-dangerous '(kill-this-buffer)
   "Commands too dangerous to repeat with \\[repeat]."
-  :group 'convenience
+  :group 'repeat
   :type '(repeat function))
 
 ;; If the last command was self-insert-command, the char to be inserted was
@@ -120,7 +126,7 @@ if `repeat' is bound to C-x z, typing C-x z z z repeats the 
previous command
 3 times.  If this variable is a sequence of characters, then re-execution
 only occurs if the final character by which `repeat' was invoked is a
 member of that sequence.  If this variable is nil, no re-execution occurs."
-  :group 'convenience
+  :group 'repeat
   :type '(choice (const :tag "Repeat for all keys" t)
                 (const :tag "Don't repeat" nil)
                 (sexp :tag "Repeat for specific keys")))
@@ -338,8 +344,8 @@ recently executed command not bound to an input event\"."
   "Key that stops the modal repeating of keys in sequence.
 For example, you can set it to <return> like `isearch-exit'."
   :type '(choice (const :tag "No special key to exit repeating sequence" nil)
-                 (key-sequence :tag "Key that exits repeating sequence"))
-  :group 'convenience
+                 (key :tag "Kbd keys that exit repeating sequence"))
+  :group 'repeat
   :version "28.1")
 
 (defcustom repeat-exit-timeout nil
@@ -350,16 +356,19 @@ You can also set the property `repeat-exit-timeout' on 
the command symbol.
 This property can override the value of this variable."
   :type '(choice (const :tag "No timeout to exit repeating sequence" nil)
                  (number :tag "Timeout in seconds to exit repeating"))
-  :group 'convenience
+  :group 'repeat
   :version "28.1")
 
+(defvar repeat-exit-function nil
+  "Function that exits the repeating sequence.")
+
 (defvar repeat-exit-timer nil
   "Timer activated after the last key typed in the repeating key sequence.")
 
 (defcustom repeat-keep-prefix nil
   "Whether to keep the prefix arg of the previous command when repeating."
   :type 'boolean
-  :group 'convenience
+  :group 'repeat
   :version "28.1")
 
 (defcustom repeat-check-key t
@@ -377,7 +386,7 @@ When the variable value is non-nil, but the property value 
is `no',
 then don't check the last key.  Also when the variable value is nil,
 but the property value is `t', then check the last key."
   :type 'boolean
-  :group 'convenience
+  :group 'repeat
   :version "28.1")
 
 (defcustom repeat-echo-function #'repeat-echo-message
@@ -390,7 +399,7 @@ a repeating map, or nil after deactivating the transient 
repeating mode."
                         repeat-echo-mode-line)
                  (const :tag "No visual feedback" ignore)
                  (function :tag "Function"))
-  :group 'convenience
+  :group 'repeat
   :version "28.1")
 
 (defvar repeat-in-progress nil
@@ -408,7 +417,7 @@ the map can't be set on the command symbol property 
`repeat-map'.")
 When Repeat mode is enabled, and the command symbol has the property named
 `repeat-map', this map is activated temporarily for the next command.
 See `describe-repeat-maps' for a list of all repeatable commands."
-  :global t :group 'convenience
+  :global t :group 'repeat
   (if (not repeat-mode)
       (remove-hook 'post-command-hook 'repeat-post-hook)
     (add-hook 'post-command-hook 'repeat-post-hook)
@@ -466,36 +475,47 @@ See `describe-repeat-maps' for a list of all repeatable 
commands."
 
               ;; Adding an exit key
               (when repeat-exit-key
-                (define-key map repeat-exit-key 'ignore))
+                (define-key map (if (key-valid-p repeat-exit-key)
+                                    (kbd repeat-exit-key)
+                                  repeat-exit-key)
+                            'ignore))
 
               (when (and repeat-keep-prefix (not prefix-arg))
                 (setq prefix-arg current-prefix-arg))
 
               (setq repeat-in-progress t)
               (let ((exitfun (set-transient-map map)))
-
-                (when repeat-exit-timer
-                  (cancel-timer repeat-exit-timer)
-                  (setq repeat-exit-timer nil))
+                (repeat--exit)
+                (setq repeat-exit-function exitfun)
 
                 (let* ((prop (repeat--command-property 'repeat-exit-timeout))
                        (timeout (unless (eq prop 'no) (or prop 
repeat-exit-timeout))))
                   (when timeout
                     (setq repeat-exit-timer
-                          (run-with-idle-timer
-                           timeout nil
-                           (lambda ()
-                             (setq repeat-in-progress nil)
-                             (funcall exitfun)
-                             (funcall repeat-echo-function nil))))))))))))
+                          (run-with-idle-timer timeout nil 
#'repeat-exit))))))))))
 
     (setq repeat-map nil)
     (setq repeat--prev-mb (cons (minibuffer-depth) current-minibuffer-command))
     (when (and was-in-progress (not repeat-in-progress))
-      (when repeat-exit-timer
-        (cancel-timer repeat-exit-timer)
-        (setq repeat-exit-timer nil))
-      (funcall repeat-echo-function nil))))
+      (repeat-exit))))
+
+;;;###autoload
+(defun repeat-exit ()
+  "Exit the repeating sequence.
+This function can be used to force exit of repetition while it's active."
+  (interactive)
+  (setq repeat-in-progress nil)
+  (repeat--exit)
+  (funcall repeat-echo-function nil))
+
+(defun repeat--exit ()
+  "Internal function to clean up previously set exit function and timer."
+  (when repeat-exit-timer
+    (cancel-timer repeat-exit-timer)
+    (setq repeat-exit-timer nil))
+  (when repeat-exit-function
+    (funcall repeat-exit-function)
+    (setq repeat-exit-function nil)))
 
 (defun repeat-echo-message-string (keymap)
   "Return a string with a list of repeating keys."
@@ -510,7 +530,9 @@ See `describe-repeat-maps' for a list of all repeatable 
commands."
                     (if repeat-exit-key
                         (substitute-command-keys
                          (format ", or exit with \\`%s'"
-                                 (key-description repeat-exit-key)))
+                                 (if (key-valid-p repeat-exit-key)
+                                     repeat-exit-key
+                                   (key-description repeat-exit-key))))
                       ""))))
 
 (defun repeat-echo-message (keymap)
diff --git a/lisp/replace.el b/lisp/replace.el
index 2bb9c1b90d..8f81ec33a6 100644
--- a/lisp/replace.el
+++ b/lisp/replace.el
@@ -73,14 +73,6 @@ See `query-replace-from-history-variable' and
 This is a list of cons cells (FROM-STRING . TO-STRING), or nil
 if there are no default values.")
 
-(defvar query-replace-interactive nil
-  "Non-nil means `query-replace' uses the last search string.
-That becomes the \"string to replace\".")
-(make-obsolete-variable 'query-replace-interactive
-                       "use `M-n' to pull the last incremental search string
-to the minibuffer that reads the string to replace, or invoke replacements
-from Isearch by using a key sequence like `C-s C-s M-%'." "24.3")
-
 (defcustom query-replace-from-to-separator " → "
   "String that separates FROM and TO in the history of replacement pairs.
 When nil, the pair will not be added to the history (same behavior
@@ -213,96 +205,94 @@ by this function to the end of values available via
 Prompt with PROMPT.  REGEXP-FLAG non-nil means the response should be a regexp.
 The return value can also be a pair (FROM . TO) indicating that the user
 wants to replace FROM with TO."
-  (if query-replace-interactive
-      (car (if regexp-flag regexp-search-ring search-ring))
-    (let* ((history-add-new-input nil)
-          (separator-string
-           (when query-replace-from-to-separator
-             ;; Check if the first non-whitespace char is displayable
-             (if (char-displayable-p
-                  (string-to-char (string-replace
-                                   " " "" query-replace-from-to-separator)))
-                 query-replace-from-to-separator
-               " -> ")))
-          (separator
-           (when separator-string
-             (propertize separator-string
-                         'display separator-string
-                         'face 'minibuffer-prompt
-                         'separator t)))
-          (minibuffer-history
-           (append
-            (when separator
-              (mapcar (lambda (from-to)
-                        (concat (query-replace-descr (car from-to))
-                                separator
-                                (query-replace-descr (cdr from-to))))
-                      query-replace-defaults))
-            (symbol-value query-replace-from-history-variable)))
-          (minibuffer-allow-text-properties t) ; separator uses text-properties
-          (default (when (and query-replace-read-from-default (not 
regexp-flag))
-                     (funcall query-replace-read-from-default)))
-          (prompt
-            (cond ((and query-replace-read-from-regexp-default regexp-flag) 
prompt)
-                  (default (format-prompt prompt default))
-                  ((and query-replace-defaults separator)
-                   (format-prompt prompt (car minibuffer-history)))
-                  (query-replace-defaults
-                   (format-prompt
-                    prompt (format "%s -> %s"
-                                   (query-replace-descr
-                                    (caar query-replace-defaults))
-                                   (query-replace-descr
-                                    (cdar query-replace-defaults)))))
-                  (t (format-prompt prompt nil))))
-          (from
-           ;; The save-excursion here is in case the user marks and copies
-           ;; a region in order to specify the minibuffer input.
-           ;; That should not clobber the region for the query-replace itself.
-           (save-excursion
-              (minibuffer-with-setup-hook
-                  (lambda ()
-                    (setq-local text-property-default-nonsticky
-                                (append '((separator . t) (face . t))
-                                        text-property-default-nonsticky)))
-                (if regexp-flag
-                    (read-regexp
-                     (if query-replace-read-from-regexp-default
-                         (string-remove-suffix ": " prompt)
-                       prompt)
-                     query-replace-read-from-regexp-default
-                     'minibuffer-history)
-                  (read-from-minibuffer
-                   prompt nil nil nil nil
-                   (if default
-                       (delete-dups
-                        (cons default (query-replace-read-from-suggestions)))
-                     (query-replace-read-from-suggestions))
-                   t)))))
-           (to))
-      (if (and (zerop (length from)) query-replace-defaults (not default))
-         (cons (caar query-replace-defaults)
-               (query-replace-compile-replacement
-                (cdar query-replace-defaults) regexp-flag))
-        (setq from (or (and (zerop (length from)) default)
-                       (query-replace--split-string from)))
-        (when (consp from) (setq to (cdr from) from (car from)))
-        (add-to-history query-replace-from-history-variable from nil t)
-        ;; Warn if user types \n or \t, but don't reject the input.
-        (and regexp-flag
-             (string-match "\\(\\`\\|[^\\]\\)\\(\\\\\\\\\\)*\\(\\\\[nt]\\)" 
from)
-             (let ((match (match-string 3 from)))
-               (cond
-                ((string= match "\\n")
-                 (message "Note: `\\n' here doesn't match a newline; to do 
that, type C-q C-j instead"))
-                ((string= match "\\t")
-                 (message "Note: `\\t' here doesn't match a tab; to do that, 
just type TAB")))
-               (sit-for 2)))
-        (if (not to)
-            from
-          (add-to-history query-replace-to-history-variable to nil t)
-          (add-to-history 'query-replace-defaults (cons from to) nil t)
-          (cons from (query-replace-compile-replacement to regexp-flag)))))))
+  (let* ((history-add-new-input nil)
+         (separator-string
+          (when query-replace-from-to-separator
+            ;; Check if the first non-whitespace char is displayable
+            (if (char-displayable-p
+                 (string-to-char (string-replace
+                                  " " "" query-replace-from-to-separator)))
+                query-replace-from-to-separator
+              " -> ")))
+         (separator
+          (when separator-string
+            (propertize separator-string
+                        'display separator-string
+                        'face 'minibuffer-prompt
+                        'separator t)))
+         (minibuffer-history
+          (append
+           (when separator
+             (mapcar (lambda (from-to)
+                       (concat (query-replace-descr (car from-to))
+                               separator
+                               (query-replace-descr (cdr from-to))))
+                     query-replace-defaults))
+           (symbol-value query-replace-from-history-variable)))
+         (minibuffer-allow-text-properties t) ; separator uses text-properties
+         (default (when (and query-replace-read-from-default (not regexp-flag))
+                    (funcall query-replace-read-from-default)))
+         (prompt
+          (cond ((and query-replace-read-from-regexp-default regexp-flag) 
prompt)
+                (default (format-prompt prompt default))
+                ((and query-replace-defaults separator)
+                 (format-prompt prompt (car minibuffer-history)))
+                (query-replace-defaults
+                 (format-prompt
+                  prompt (format "%s -> %s"
+                                 (query-replace-descr
+                                  (caar query-replace-defaults))
+                                 (query-replace-descr
+                                  (cdar query-replace-defaults)))))
+                (t (format-prompt prompt nil))))
+         (from
+          ;; The save-excursion here is in case the user marks and copies
+          ;; a region in order to specify the minibuffer input.
+          ;; That should not clobber the region for the query-replace itself.
+          (save-excursion
+            (minibuffer-with-setup-hook
+                (lambda ()
+                  (setq-local text-property-default-nonsticky
+                              (append '((separator . t) (face . t))
+                                      text-property-default-nonsticky)))
+              (if regexp-flag
+                  (read-regexp
+                   (if query-replace-read-from-regexp-default
+                       (string-remove-suffix ": " prompt)
+                     prompt)
+                   query-replace-read-from-regexp-default
+                   'minibuffer-history)
+                (read-from-minibuffer
+                 prompt nil nil nil nil
+                 (if default
+                     (delete-dups
+                      (cons default (query-replace-read-from-suggestions)))
+                   (query-replace-read-from-suggestions))
+                 t)))))
+         (to))
+    (if (and (zerop (length from)) query-replace-defaults (not default))
+        (cons (caar query-replace-defaults)
+              (query-replace-compile-replacement
+               (cdar query-replace-defaults) regexp-flag))
+      (setq from (or (and (zerop (length from)) default)
+                     (query-replace--split-string from)))
+      (when (consp from) (setq to (cdr from) from (car from)))
+      (add-to-history query-replace-from-history-variable from nil t)
+      ;; Warn if user types \n or \t, but don't reject the input.
+      (and regexp-flag
+           (string-match "\\(\\`\\|[^\\]\\)\\(\\\\\\\\\\)*\\(\\\\[nt]\\)" from)
+           (let ((match (match-string 3 from)))
+             (cond
+              ((string= match "\\n")
+               (message "Note: `\\n' here doesn't match a newline; to do that, 
type C-q C-j instead"))
+              ((string= match "\\t")
+               (message "Note: `\\t' here doesn't match a tab; to do that, 
just type TAB")))
+             (sit-for 2)))
+      (if (not to)
+          from
+        (add-to-history query-replace-to-history-variable to nil t)
+        (add-to-history 'query-replace-defaults (cons from to) nil t)
+        (cons from (query-replace-compile-replacement to regexp-flag))))))
 
 (defun query-replace-compile-replacement (to regexp-flag)
   "Maybe convert a regexp replacement TO to Lisp.
@@ -448,6 +438,10 @@ Arguments FROM-STRING, TO-STRING, DELIMITED, START, END, 
BACKWARD, and
 REGION-NONCONTIGUOUS-P are passed to `perform-replace' (which see).
 
 To customize possible responses, change the bindings in `query-replace-map'."
+  (declare (interactive-args
+           (start (use-region-beginning))
+           (end (use-region-end))
+           (region-noncontiguous-p (use-region-noncontiguous-p))))
   (interactive
    (let ((common
          (query-replace-read-args
@@ -461,10 +455,9 @@ To customize possible responses, change the bindings in 
`query-replace-map'."
           ;; These are done separately here
           ;; so that command-history will record these expressions
           ;; rather than the values they had this time.
-          (if (use-region-p) (region-beginning))
-          (if (use-region-p) (region-end))
+          (use-region-beginning) (use-region-end)
           (nth 3 common)
-          (if (use-region-p) (region-noncontiguous-p)))))
+          (use-region-noncontiguous-p))))
   (perform-replace from-string to-string t nil delimited nil nil start end 
backward region-noncontiguous-p))
 
 (define-key esc-map "%" 'query-replace)
@@ -541,6 +534,10 @@ Use \\[repeat-complex-command] after this command for 
details.
 
 Arguments REGEXP, TO-STRING, DELIMITED, START, END, BACKWARD, and
 REGION-NONCONTIGUOUS-P are passed to `perform-replace' (which see)."
+  (declare (interactive-args
+           (start (use-region-beginning))
+           (end (use-region-end))
+           (region-noncontiguous-p (use-region-noncontiguous-p))))
   (interactive
    (let ((common
          (query-replace-read-args
@@ -555,10 +552,9 @@ REGION-NONCONTIGUOUS-P are passed to `perform-replace' 
(which see)."
           ;; These are done separately here
           ;; so that command-history will record these expressions
           ;; rather than the values they had this time.
-          (if (use-region-p) (region-beginning))
-          (if (use-region-p) (region-end))
+          (use-region-beginning) (use-region-end)
           (nth 3 common)
-          (if (use-region-p) (region-noncontiguous-p)))))
+          (use-region-noncontiguous-p))))
   (perform-replace regexp to-string t t delimited nil nil start end backward 
region-noncontiguous-p))
 
 (define-key esc-map [?\C-%] 'query-replace-regexp)
@@ -592,6 +588,10 @@ Fourth and fifth arg START and END specify the region to 
operate on.
 
 Arguments REGEXP, START, END, and REGION-NONCONTIGUOUS-P are passed to
 `perform-replace' (which see)."
+  (declare (interactive-args
+           (start (use-region-beginning))
+           (end (use-region-end))
+           (region-noncontiguous-p (use-region-noncontiguous-p))))
   (interactive
    (let* ((from (read-regexp "Map query replace (regexp): " nil
                             query-replace-from-history-variable))
@@ -603,9 +603,8 @@ Arguments REGEXP, START, END, and REGION-NONCONTIGUOUS-P 
are passed to
      (list from to
           (and current-prefix-arg
                (prefix-numeric-value current-prefix-arg))
-          (if (use-region-p) (region-beginning))
-          (if (use-region-p) (region-end))
-          (if (use-region-p) (region-noncontiguous-p)))))
+          (use-region-beginning) (use-region-end)
+          (use-region-noncontiguous-p))))
   (let (replacements)
     (if (listp to-strings)
        (setq replacements to-strings)
@@ -665,9 +664,10 @@ which will run faster and will not set the mark or print 
anything.
 and TO-STRING is also null.)"
   (declare (interactive-only
            "use `search-forward' and `replace-match' instead.")
-           (interactive-args
+          (interactive-args
            (start (use-region-beginning))
-           (end (use-region-end))))
+           (end (use-region-end))
+           (region-noncontiguous-p (use-region-noncontiguous-p))))
   (interactive
    (let ((common
          (query-replace-read-args
@@ -681,7 +681,7 @@ and TO-STRING is also null.)"
      (list (nth 0 common) (nth 1 common) (nth 2 common)
           (use-region-beginning) (use-region-end)
           (nth 3 common)
-          (if (use-region-p) (region-noncontiguous-p)))))
+          (use-region-noncontiguous-p))))
   (perform-replace from-string to-string nil nil delimited nil nil start end 
backward region-noncontiguous-p))
 
 (defun replace-regexp (regexp to-string &optional delimited start end backward 
region-noncontiguous-p)
@@ -746,7 +746,11 @@ What you probably want is a loop like this:
     (replace-match TO-STRING nil nil))
 which will run faster and will not set the mark or print anything."
   (declare (interactive-only
-           "use `re-search-forward' and `replace-match' instead."))
+           "use `re-search-forward' and `replace-match' instead.")
+          (interactive-args
+           (start (use-region-beginning))
+           (end (use-region-end))
+           (region-noncontiguous-p (use-region-noncontiguous-p))))
   (interactive
    (let ((common
          (query-replace-read-args
@@ -758,10 +762,9 @@ which will run faster and will not set the mark or print 
anything."
                   (if (use-region-p) " in region" ""))
           t)))
      (list (nth 0 common) (nth 1 common) (nth 2 common)
-          (if (use-region-p) (region-beginning))
-          (if (use-region-p) (region-end))
+          (use-region-beginning) (use-region-end)
           (nth 3 common)
-          (if (use-region-p) (region-noncontiguous-p)))))
+          (use-region-noncontiguous-p))))
   (perform-replace regexp to-string nil t delimited nil nil start end backward 
region-noncontiguous-p))
 
 
@@ -2814,8 +2817,8 @@ which will run faster and probably do exactly what you 
want.  Please
 see the documentation of `replace-match' to find out how to simulate
 `case-replace'.
 
-This function returns nil if and only if there were no matches to
-make, or the user didn't cancel the call.
+This function returns nil if there were no matches to make, or
+the user cancelled the call.
 
 REPLACEMENTS is either a string, a list of strings, or a cons cell
 containing a function and its first argument.  The function is
diff --git a/lisp/reveal.el b/lisp/reveal.el
index 64e9ceef64..b3b42177f9 100644
--- a/lisp/reveal.el
+++ b/lisp/reveal.el
@@ -210,13 +210,11 @@ that text."
   (let ((reveal-auto-hide t))
     (reveal-post-command)))
 
-(defvar reveal-mode-map
-  (let ((map (make-sparse-keymap)))
-    ;; Override the default move-beginning-of-line and move-end-of-line
-    ;; which skips valuable invisible text.
-    (define-key map [remap move-beginning-of-line] 'beginning-of-line)
-    (define-key map [remap move-end-of-line] 'end-of-line)
-    map))
+(defvar-keymap reveal-mode-map
+  ;; Override the default move-beginning-of-line and move-end-of-line
+  ;; which skips valuable invisible text.
+  "<remap> <move-beginning-of-line>" #'beginning-of-line
+  "<remap> <move-end-of-line>"       #'end-of-line)
 
 ;;;###autoload
 (define-minor-mode reveal-mode
diff --git a/lisp/server.el b/lisp/server.el
index dd7bccaf33..3caa335c4e 100644
--- a/lisp/server.el
+++ b/lisp/server.el
@@ -27,12 +27,12 @@
 
 ;;; Commentary:
 
-;; This Lisp code is run in Emacs when it is to operate as
-;; a server for other processes.
+;; This library allows Emacs to operate as a server for other
+;; processes.
 
-;; Load this library and do M-x server-edit to enable Emacs as a server.
+;; Load this library and do `M-x server-start' to enable Emacs as a server.
 ;; Emacs opens up a socket for communication with clients.  If there are no
-;; client buffers to edit, server-edit acts like (switch-to-buffer
+;; client buffers to edit, `server-edit' acts like (switch-to-buffer
 ;; (other-buffer))
 
 ;; When some other program runs "the editor" to edit a file,
@@ -42,10 +42,10 @@
 
 ;; Note that any number of clients may dispatch files to Emacs to be edited.
 
-;; When you finish editing a Server buffer, again call server-edit
+;; When you finish editing a Server buffer, again call `server-edit'
 ;; to mark that buffer as done for the client and switch to the next
 ;; Server buffer.  When all the buffers for a client have been edited
-;; and exited with server-edit, the client "editor" will return
+;; and exited with `server-edit', the client "editor" will return
 ;; to the program that invoked it.
 
 ;; Your editing commands and Emacs's display output go to and from
@@ -54,25 +54,28 @@
 ;; the client.  This is possible in four cases:
 
 ;; 1. On a window system, where Emacs runs in one window and the
-;; program that wants to use "the editor" runs in another.
+;;    program that wants to use "the editor" runs in another.
 
-;; 2. On a multi-terminal system, where Emacs runs on one terminal and the
-;; program that wants to use "the editor" runs on another.
+;; 2. On a multi-terminal system, where Emacs runs on one terminal and
+;;    the program that wants to use "the editor" runs on another.
 
-;; 3. When the program that wants to use "the editor" is running
-;; as a subprocess of Emacs.
+;; 3. When the program that wants to use "the editor" is running as a
+;;    subprocess of Emacs.
 
-;; 4. On a system with job control, when Emacs is suspended, the program
-;; that wants to use "the editor" will stop and display
-;; "Waiting for Emacs...".  It can then be suspended, and Emacs can be
-;; brought into the foreground for editing.  When done editing, Emacs is
-;; suspended again, and the client program is brought into the foreground.
+;; 4. On a system with job control, when Emacs is suspended, the
+;;    program that wants to use "the editor" will stop and display
+;;    "Waiting for Emacs...".  It can then be suspended, and Emacs can
+;;    be brought into the foreground for editing.  When done editing,
+;;    Emacs is suspended again, and the client program is brought into
+;;    the foreground.
 
-;; The buffer local variable "server-buffer-clients" lists
+;; The buffer local variable `server-buffer-clients' lists
 ;; the clients who are waiting for this buffer to be edited.
-;; The global variable "server-clients" lists all the waiting clients,
+;; The global variable `server-clients' lists all the waiting clients,
 ;; and which files are yet to be edited for each.
 
+;;; Code:
+
 ;; Todo:
 
 ;; - handle command-line-args-left.
@@ -80,8 +83,6 @@
 ;;   to here.
 ;; - fix up handling of the client's environment (place it in the terminal?).
 
-;;; Code:
-
 (eval-when-compile (require 'cl-lib))
 
 (defgroup server nil
diff --git a/lisp/shell.el b/lisp/shell.el
index 85225b128a..641f274045 100644
--- a/lisp/shell.el
+++ b/lisp/shell.el
@@ -99,6 +99,7 @@
 (require 'pcomplete)
 (eval-when-compile (require 'files-x)) ;with-connection-local-variables
 (require 'subr-x)
+(eval-when-compile (require 'cl-lib))
 
 ;;; Customization and Buffer Variables
 
@@ -307,6 +308,40 @@ for Shell mode only."
                 (const :tag "on" t))
   :group 'shell)
 
+(defcustom shell-fontify-input-enable t
+  "Enable fontification of input in shell buffers.
+This variable only has effect when the shell is started.  Use the
+command `comint-fontify-input-mode' to toggle fontification of
+input."
+  :type 'boolean
+  :group 'shell
+  :safe 'booleanp
+  :version "29.1")
+
+(defcustom shell-indirect-setup-hook nil
+  "Hook run in an indirect buffer for input fontification.
+Input fontification and indentation of a `shell-mode' buffer, if
+enabled, is performed in an indirect buffer, whose indentation
+and syntax highlighting is set up with `sh-mode'.  In addition to
+`comint-indirect-setup-hook', run this hook with the indirect
+buffer as the current buffer after its setup is done.  This can
+be used to further customize fontification and other behavior of
+the indirect buffer."
+  :type 'boolean
+  :group 'shell
+  :safe 'booleanp
+  :version "29.1")
+
+(defcustom shell-highlight-undef-enable nil
+  "Enable highlighting of undefined commands in shell buffers.
+This variable only has effect when the shell is started.  Use the
+command `shell-highlight-undef-mode' to toggle highlighting of
+undefined commands."
+  :type 'boolean
+  :group 'shell
+  :safe 'booleanp
+  :version "29.1")
+
 (defvar shell-dirstack nil
   "List of directories saved by pushd in this buffer's shell.
 Thus, this does not include the shell's current directory.")
@@ -522,6 +557,8 @@ Shell buffers.  It implements `shell-completion-execonly' 
for
 
 (put 'shell-mode 'mode-class 'special)
 
+(defvar sh-shell-file)
+
 (define-derived-mode shell-mode comint-mode "Shell"
   "Major mode for interacting with an inferior shell.
 \\<shell-mode-map>
@@ -585,6 +622,13 @@ from `shell-mode-hook', Emacs will call the `ding' function
 whenever it receives the bell character in output from a
 command."
   :interactive nil
+  :after-hook
+  (unless comint-use-prompt-regexp
+    (if shell-fontify-input-enable
+        (comint-fontify-input-mode))
+    (if shell-highlight-undef-enable
+        (shell-highlight-undef-mode)))
+
   (setq comint-prompt-regexp shell-prompt-pattern)
   (shell-completion-vars)
   (setq-local paragraph-separate "\\'")
@@ -604,6 +648,23 @@ command."
   (setq-local ansi-color-apply-face-function #'shell-apply-ansi-color)
   (shell-reapply-ansi-color)
 
+  (add-hook 'comint-indirect-setup-hook
+            #'shell-indirect-setup-hook 'append t)
+  (setq comint-indirect-setup-function
+        (let ((shell shell--start-prog))
+          (lambda ()
+            (require 'sh-script)
+            (cl-letf
+                (((default-value 'sh-shell-file)
+                  (or shell sh-shell-file))
+                 (inhibit-message t)
+                 (message-log-max nil))
+              (sh-mode)))))
+
+  (setq-local indent-line-function #'comint-indent-input-line-default)
+  (setq-local indent-region-function
+              #'comint-indent-input-region-default)
+
   ;; This is not really correct, since the shell buffer does not really
   ;; edit this directory.  But it is useful in the buffer list and menus.
   (setq list-buffers-directory (expand-file-name default-directory))
@@ -658,6 +719,10 @@ command."
                     ": [[:digit:]]+:[[:digit:]]+;")))
     (comint-read-input-ring t)))
 
+(defun shell-indirect-setup-hook ()
+  "Run `shell-indirect-setup-hook'."
+  (run-hooks 'shell-indirect-setup-hook))
+
 (defun shell-apply-ansi-color (beg end face)
   "Apply FACE as the ansi-color face for the text between BEG and END."
   (when face
@@ -1482,6 +1547,222 @@ Returns t if successful."
     ;; Remove the prompt.
     (replace-regexp-in-string "\n.*\\'" "\n" result)))
 
+;;; Highlight undefined commands
+;;
+;; To highlight non-existent shell commands, customize
+;; `shell-highlight-undef-enable' to t.  To highlight some shell
+;; commands as aliases, add them to `shell-highlight-undef-aliases'.
+
+(defcustom shell-highlight-undef-aliases nil
+  "List of shell commands to highlight as a command alias."
+  :group 'shell
+  :type '(repeat string)
+  :version "29.1")
+
+(defface shell-highlight-undef-defined-face
+  '((t :inherit 'font-lock-function-name-face))
+  "Face used for existing shell commands."
+  :group 'shell
+  :version "29.1")
+
+(defface shell-highlight-undef-undefined-face
+  '((t :inherit 'font-lock-warning-face))
+  "Face used for non-existent shell commands."
+  :group 'shell
+  :version "29.1")
+
+(defface shell-highlight-undef-alias-face
+  '((t :inherit 'font-lock-variable-name-face))
+  "Face used for shell command aliases."
+  :group 'shell
+  :version "29.1")
+
+(defcustom shell-highlight-undef-remote-file-name-inhibit-cache nil
+  "Whether to inhibit cache for fontifying shell commands in remote buffers.
+When fontification of non-existent commands is enabled in a
+remote shell buffer, use a cache to speed up searching for
+executable files on the remote machine.  This options is used to
+control expiry of this cache.  See `remote-file-name-inhibit-cache'
+for description."
+  :group 'faces
+  :type '(choice
+          (const :tag "Do not inhibit file name cache" nil)
+          (const :tag "Do not use file name cache" t)
+          (integer :tag "Do not use file name cache"
+                   :format "Do not use file name cache older than %v seconds"
+                   :value 10))
+  :version "29.1")
+
+(defvar shell--highlight-undef-exec-cache nil
+  "Cache of executable files found in `exec-path'.
+An alist, whose elements are of the form
+\(REMOTE TIME EXECUTABLES), where REMOTE is a string, returned by
+`file-remote-p', TIME is the return value of `float-time', and
+EXECUTABLES is a hash table with keys being the base-names of
+executable files.
+
+Cache expiry is controlled by the user option
+`remote-file-name-inhibit-cache'.")
+
+(defvar shell--highlight-undef-face 'shell-highlight-undef-defined-face)
+
+(defvar shell-highlight-undef-keywords
+  `((,#'shell-highlight-undef-matcher 6 shell--highlight-undef-face)))
+
+(defvar-local shell-highlight-undef-regexp regexp-unmatchable)
+
+(defun shell--highlight-undef-executable-find (command)
+  "Return non-nil if COMMAND is found in `exec-path'.
+Similar to `executable-find', but use cache stored in
+`shell--highlight-undef-exec-cache'."
+  (let ((remote (file-remote-p default-directory))
+        as ret found-in-cache delta-time)
+    (if (null remote)
+        (executable-find command)
+
+      (setq delta-time
+            shell-highlight-undef-remote-file-name-inhibit-cache)
+
+      (pcase (setq as (assoc remote shell--highlight-undef-exec-cache))
+        (`(,_ ,time ,hash)
+         (when (pcase delta-time
+                 ((pred numberp) (<= (float-time) (+ time delta-time)))
+                 ('t nil)
+                 ('nil t))
+           (setq ret (gethash command hash))
+           (setq found-in-cache t)))
+        (_ (setq as (list remote 0 (make-hash-table :test #'equal)))
+           (push as shell--highlight-undef-exec-cache)))
+
+      (if found-in-cache
+          ret
+        ;; Build cache
+        (setcar (cdr as) (float-time))
+        (let ((hash (clrhash (caddr as))))
+          (dolist (dir (exec-path))
+            (pcase-dolist (`(,f . ,attr)
+                           (condition-case nil
+                               (directory-files-and-attributes
+                                (concat remote dir) nil nil 'nosort 'integer)
+                             (file-error nil)))
+              ;; Approximation.  Assume every non-directory file in $PATH is an
+              ;; executable.  Alternatively, we could check
+              ;; `file-executable-p', but doing so for every file in $PATH is
+              ;; slow on remote machines.
+              (unless (eq t (file-attribute-type attr))
+                (puthash f t hash))))
+          (gethash command hash))))))
+
+(defun shell-highlight-undef-matcher (end)
+  "Matcher used to highlight shell commands up to END."
+  (when (re-search-forward shell-highlight-undef-regexp end t)
+    (save-match-data
+      (let ((cmd (match-string 6))
+            (beg (match-beginning 6)))
+        (setq shell--highlight-undef-face
+              (let* ((buf (buffer-base-buffer))
+                     (default-directory
+                      (if buf (buffer-local-value 'default-directory buf)
+                        default-directory)))
+                (cond
+                 ;; Don't fontify command output.  Mostly useful if
+                 ;; `comint-fontify-input-mode' is disabled.
+                 ((text-property-any beg (point) 'field 'output)
+                  nil)
+                 ((member cmd shell-highlight-undef-aliases)
+                  'shell-highlight-undef-alias-face)
+                 ;; Check if it contains a directory separator
+                 ((file-name-directory cmd)
+                  (when (file-name-absolute-p cmd)
+                    (setq cmd (concat
+                               (or (bound-and-true-p comint-file-name-prefix)
+                                   (file-remote-p default-directory))
+                               cmd)))
+                  (if (or (file-executable-p cmd)
+                          (file-directory-p cmd))
+                      'shell-highlight-undef-defined-face
+                    'shell-highlight-undef-undefined-face))
+                 ((shell--highlight-undef-executable-find cmd)
+                  'shell-highlight-undef-defined-face)
+                 (t 'shell-highlight-undef-undefined-face))))))
+    t))
+
+(defvar-local shell--highlight-undef-indirect nil
+  "Non-nil if shell commands are fontified in `comint-indirect-buffer'.")
+
+(declare-function sh-feature "sh-script" (alist &optional function))
+(defvar sh-leading-keywords)
+(defvar sh-other-keywords)
+
+(define-minor-mode shell-highlight-undef-mode
+  "Highlight undefined shell commands and aliases.
+This minor mode is mostly useful in `shell-mode' buffers and
+works better if `comint-fontify-input-mode' is enabled."
+  :init-value nil
+  (if shell--highlight-undef-indirect
+      (progn
+        (remove-hook 'comint-indirect-setup-hook 
shell--highlight-undef-indirect t)
+        (setq shell--highlight-undef-indirect nil)
+        (when-let ((buf (comint-indirect-buffer t)))
+          (with-current-buffer buf
+            (font-lock-remove-keywords nil shell-highlight-undef-keywords))))
+    (font-lock-remove-keywords nil shell-highlight-undef-keywords))
+  (remove-hook 'comint-fontify-input-mode-hook
+               #'shell-highlight-undef-mode-restart t)
+
+  (when shell-highlight-undef-mode
+    (when comint-use-prompt-regexp
+      (setq shell-highlight-undef-mode nil)
+      (error
+       "`shell-highlight-undef-mode' is incompatible with 
`comint-use-prompt-regexp'"))
+
+    (require 'sh-script)
+
+    (let* ((regexp
+            ;; Adapted from `sh-font-lock-keywords-1'
+            (concat
+             "\\("
+             "[;(){}`|&]"
+             (if comint-fontify-input-mode
+                 ;; `comint-fontify-input-mode' already puts
+                 ;; point-min on end of prompt
+                 ""
+               (concat "\\|" comint-prompt-regexp))
+             "\\|^"
+             "\\)"
+             "[ \t]*\\(\\("
+             (regexp-opt (sh-feature sh-leading-keywords) t)
+             "[ \t]+\\)?"
+             (regexp-opt (append (sh-feature sh-leading-keywords)
+                                 (sh-feature sh-other-keywords))
+                         t)
+             "[ \t]+\\)?\\_<\\(\\(?:\\s_\\|\\sw\\|/\\)+\\)\\_>"))
+           (setup
+            (lambda ()
+              (setq shell-highlight-undef-regexp regexp)
+              (font-lock-add-keywords nil shell-highlight-undef-keywords t))))
+      (cond (comint-fontify-input-mode
+             (setq shell--highlight-undef-indirect setup)
+             (if-let ((buf (comint-indirect-buffer t)))
+                 (with-current-buffer buf
+                   (funcall setup))
+               (add-hook 'comint-indirect-setup-hook setup nil t)))
+            (t (funcall setup))))
+
+    (add-hook 'comint-fontify-input-mode-hook
+              #'shell-highlight-undef-mode-restart nil t))
+
+  (font-lock-flush))
+
+(defun shell-highlight-undef-mode-restart ()
+  "If `shell-highlight-undef-mode' is on, restart it.
+`shell-highlight-undef-mode' performs its setup differently
+depending on `comint-fontify-input-mode'.  It's useful to call
+this function when switching `comint-fontify-input-mode' in order
+to make `shell-highlight-undef-mode' redo its setup."
+  (when shell-highlight-undef-mode
+    (shell-highlight-undef-mode 1)))
+
 (provide 'shell)
 
 ;;; shell.el ends here
diff --git a/lisp/simple.el b/lisp/simple.el
index ceb29b1e30..6b73ccb516 100644
--- a/lisp/simple.el
+++ b/lisp/simple.el
@@ -2653,6 +2653,9 @@ function as needed."
 (cl-defmethod function-documentation ((function accessor))
   (oclosure--accessor-docstring function)) ;; FIXME: η-reduce!
 
+(cl-defmethod function-documentation ((f cconv--interactive-helper))
+  (function-documentation (cconv--interactive-helper--fun f)))
+
 ;; This should be in `oclosure.el' but that file is loaded before `cl-generic'.
 (cl-defgeneric oclosure-interactive-form (_function)
   "Return the interactive form of FUNCTION or nil if none.
@@ -2664,6 +2667,9 @@ instead."
   ;; (interactive-form function)
   nil)
 
+(cl-defmethod oclosure-interactive-form ((f cconv--interactive-helper))
+  `(interactive (funcall ',(cconv--interactive-helper--if f))))
+
 (defun command-execute (cmd &optional record-flag keys special)
   ;; BEWARE: Called directly from the C code.
   "Execute CMD as an editor command.
@@ -4563,85 +4569,81 @@ impose the use of a shell (with its need to quote 
arguments)."
                         (set-marker (mark-marker) (point)
                                     (current-buffer)))))
        ;; Output goes in a separate buffer.
-       ;; Preserve the match data in case called from a program.
-        ;; FIXME: It'd be ridiculous for an Elisp function to call
-        ;; shell-command and assume that it won't mess the match-data!
-       (save-match-data
-         (if (string-match "[ \t]*&[ \t]*\\'" command)
-             ;; Command ending with ampersand means asynchronous.
-              (let* ((buffer (get-buffer-create
-                              (or output-buffer 
shell-command-buffer-name-async)))
-                     (bname (buffer-name buffer))
-                     (proc (get-buffer-process buffer))
-                     (directory default-directory))
-               ;; Remove the ampersand.
-               (setq command (substring command 0 (match-beginning 0)))
-               ;; Ask the user what to do with already running process.
-               (when proc
-                 (cond
-                  ((eq async-shell-command-buffer 'confirm-kill-process)
-                   ;; If will kill a process, query first.
-                    (shell-command--same-buffer-confirm "Kill it")
-                   (kill-process proc))
-                  ((eq async-shell-command-buffer 'confirm-new-buffer)
-                   ;; If will create a new buffer, query first.
-                    (shell-command--same-buffer-confirm "Use a new buffer")
-                    (setq buffer (generate-new-buffer bname)))
-                  ((eq async-shell-command-buffer 'new-buffer)
-                   ;; It will create a new buffer.
-                    (setq buffer (generate-new-buffer bname)))
-                  ((eq async-shell-command-buffer 'confirm-rename-buffer)
-                   ;; If will rename the buffer, query first.
-                    (shell-command--same-buffer-confirm "Rename it")
-                   (with-current-buffer buffer
-                     (rename-uniquely))
-                    (setq buffer (get-buffer-create bname)))
-                  ((eq async-shell-command-buffer 'rename-buffer)
-                   ;; It will rename the buffer.
-                   (with-current-buffer buffer
-                     (rename-uniquely))
-                    (setq buffer (get-buffer-create bname)))))
-               (with-current-buffer buffer
-                  (shell-command-save-pos-or-erase)
-                 (setq default-directory directory)
-                  (require 'shell)
-                  (let ((process-environment
-                         (append
-                          (and (natnump async-shell-command-width)
-                               (list
-                                (format "COLUMNS=%d"
-                                        async-shell-command-width)))
-                          (comint-term-environment)
-                          process-environment)))
-                   (setq proc
-                         (start-process-shell-command "Shell" buffer command)))
-                 (setq mode-line-process '(":%s"))
-                  (shell-mode)
-                  (setq-local revert-buffer-function
-                              (lambda (&rest _)
-                                (async-shell-command command buffer)))
-                  (set-process-sentinel proc #'shell-command-sentinel)
-                 ;; Use the comint filter for proper handling of
-                 ;; carriage motion (see comint-inhibit-carriage-motion).
-                  (set-process-filter proc #'comint-output-filter)
-                  (if async-shell-command-display-buffer
-                      ;; Display buffer immediately.
-                      (display-buffer buffer '(nil (allow-no-window . t)))
-                    ;; Defer displaying buffer until first process output.
-                    ;; Use disposable named advice so that the buffer is
-                    ;; displayed at most once per process lifetime.
-                    (let ((nonce (make-symbol "nonce")))
-                      (add-function :before (process-filter proc)
-                                    (lambda (proc _string)
-                                      (let ((buf (process-buffer proc)))
-                                        (when (buffer-live-p buf)
-                                          (remove-function (process-filter 
proc)
-                                                           nonce)
-                                          (display-buffer buf))))
-                                    `((name . ,nonce)))))))
-           ;; Otherwise, command is executed synchronously.
-           (shell-command-on-region (point) (point) command
-                                    output-buffer nil error-buffer)))))))
+       (if (string-match "[ \t]*&[ \t]*\\'" command)
+           ;; Command ending with ampersand means asynchronous.
+            (let* ((buffer (get-buffer-create
+                            (or output-buffer 
shell-command-buffer-name-async)))
+                   (bname (buffer-name buffer))
+                   (proc (get-buffer-process buffer))
+                   (directory default-directory))
+             ;; Remove the ampersand.
+             (setq command (substring command 0 (match-beginning 0)))
+             ;; Ask the user what to do with already running process.
+             (when proc
+               (cond
+                ((eq async-shell-command-buffer 'confirm-kill-process)
+                 ;; If will kill a process, query first.
+                  (shell-command--same-buffer-confirm "Kill it")
+                 (kill-process proc))
+                ((eq async-shell-command-buffer 'confirm-new-buffer)
+                 ;; If will create a new buffer, query first.
+                  (shell-command--same-buffer-confirm "Use a new buffer")
+                  (setq buffer (generate-new-buffer bname)))
+                ((eq async-shell-command-buffer 'new-buffer)
+                 ;; It will create a new buffer.
+                  (setq buffer (generate-new-buffer bname)))
+                ((eq async-shell-command-buffer 'confirm-rename-buffer)
+                 ;; If will rename the buffer, query first.
+                  (shell-command--same-buffer-confirm "Rename it")
+                 (with-current-buffer buffer
+                   (rename-uniquely))
+                  (setq buffer (get-buffer-create bname)))
+                ((eq async-shell-command-buffer 'rename-buffer)
+                 ;; It will rename the buffer.
+                 (with-current-buffer buffer
+                   (rename-uniquely))
+                  (setq buffer (get-buffer-create bname)))))
+             (with-current-buffer buffer
+                (shell-command-save-pos-or-erase)
+               (setq default-directory directory)
+                (require 'shell)
+                (let ((process-environment
+                       (append
+                        (and (natnump async-shell-command-width)
+                             (list
+                              (format "COLUMNS=%d"
+                                      async-shell-command-width)))
+                        (comint-term-environment)
+                        process-environment)))
+                 (setq proc
+                       (start-process-shell-command "Shell" buffer command)))
+               (setq mode-line-process '(":%s"))
+                (shell-mode)
+                (setq-local revert-buffer-function
+                            (lambda (&rest _)
+                              (async-shell-command command buffer)))
+                (set-process-sentinel proc #'shell-command-sentinel)
+               ;; Use the comint filter for proper handling of
+               ;; carriage motion (see comint-inhibit-carriage-motion).
+                (set-process-filter proc #'comint-output-filter)
+                (if async-shell-command-display-buffer
+                    ;; Display buffer immediately.
+                    (display-buffer buffer '(nil (allow-no-window . t)))
+                  ;; Defer displaying buffer until first process output.
+                  ;; Use disposable named advice so that the buffer is
+                  ;; displayed at most once per process lifetime.
+                  (let ((nonce (make-symbol "nonce")))
+                    (add-function :before (process-filter proc)
+                                  (lambda (proc _string)
+                                    (let ((buf (process-buffer proc)))
+                                      (when (buffer-live-p buf)
+                                        (remove-function (process-filter proc)
+                                                         nonce)
+                                        (display-buffer buf))))
+                                  `((name . ,nonce)))))))
+         ;; Otherwise, command is executed synchronously.
+         (shell-command-on-region (point) (point) command
+                                  output-buffer nil error-buffer))))))
 
 (defun shell-command--same-buffer-confirm (action)
   (let ((help-form
@@ -6878,6 +6880,10 @@ point otherwise."
   "Return the end of the region if `use-region-p'."
   (and (use-region-p) (region-end)))
 
+(defun use-region-noncontiguous-p ()
+  "Return non-nil for a non-contiguous region if `use-region-p'."
+  (and (use-region-p) (region-noncontiguous-p)))
+
 (defun use-region-p ()
   "Return t if the region is active and it is appropriate to act on it.
 This is used by commands that act specially on the region under
@@ -6887,6 +6893,11 @@ The return value is t if Transient Mark mode is enabled 
and the
 mark is active; furthermore, if `use-empty-active-region' is nil,
 the region must not be empty.  Otherwise, the return value is nil.
 
+If `use-empty-active-region' is non-nil, there is one further
+caveat: If the user has used `mouse-1' to set point, but used the
+mouse to move point to a different character yet, this function
+returns nil.
+
 For some commands, it may be appropriate to ignore the value of
 `use-empty-active-region'; in that case, use `region-active-p'.
 
@@ -6894,8 +6905,10 @@ Also see the convenience functions 
`use-region-beginning' and
 `use-region-end', which may be handy when writing `interactive'
 specs."
   (and (region-active-p)
-       (or use-empty-active-region (> (region-end) (region-beginning)))
-       t))
+       (or (> (region-end) (region-beginning))
+           (and use-empty-active-region
+                (not (eq (car-safe last-input-event) 'down-mouse-1))
+                (not (mouse-movement-p last-input-event))))))
 
 (defun region-active-p ()
   "Return t if Transient Mark mode is enabled and the mark is active.
@@ -6922,7 +6935,7 @@ see `region-noncontiguous-p' and 
`extract-rectangle-bounds'."
   "Return non-nil if the region contains several pieces.
 An example is a rectangular region handled as a list of
 separate contiguous regions for each line."
-  (cdr (region-bounds)))
+  (let ((bounds (region-bounds))) (and (cdr bounds) bounds)))
 
 (defun redisplay--unhighlight-overlay-function (rol)
   "If ROL is an overlay, call `delete-overlay'."
@@ -7015,7 +7028,7 @@ which is the window that will be redisplayed.  When run, 
the `current-buffer'
 is set to the buffer displayed in that window.")
 
 (define-minor-mode cursor-face-highlight-mode
-  "When enabled, respect the cursor-face property."
+  "When enabled, highlight text that has `cursor-face' property near point."
   :global nil
   (if cursor-face-highlight-mode
       (add-hook 'pre-redisplay-functions
@@ -7973,7 +7986,7 @@ If NOERROR, don't signal an error if we can't move that 
many lines."
 
        ;; Move to the desired column.
         (if (and line-move-visual
-                 (not (or truncate-lines (truncated-partial-width-window-p))))
+                 (not (or truncate-lines truncate-partial-width-windows)))
             ;; Under line-move-visual, goal-column should be
             ;; interpreted in units of the frame's canonical character
             ;; width, which is exactly what vertical-motion does.
@@ -10431,7 +10444,9 @@ and setting it to nil."
     map))
 
 (define-derived-mode messages-buffer-mode special-mode "Messages"
-  "Major mode used in the \"*Messages*\" buffer.")
+  "Major mode used in the \"*Messages*\" buffer."
+  ;; Make it easy to do like "tail -f".
+  (setq-local window-point-insertion-type t))
 
 (defun messages-buffer ()
   "Return the \"*Messages*\" buffer.
diff --git a/lisp/speedbar.el b/lisp/speedbar.el
index e74d6fd80a..515f7d5d75 100644
--- a/lisp/speedbar.el
+++ b/lisp/speedbar.el
@@ -3509,7 +3509,7 @@ Returns the tag list, or t for an error."
     (error t)))
 )
 
-;;; Tag Management -- etags  (old XEmacs compatibility part)
+;;; Tag Management -- etags
 ;;
 (defvar speedbar-fetch-etags-parse-list
   '(;; Note that java has the same parse-group as c
@@ -3552,10 +3552,7 @@ This variable is ignored if `speedbar-use-imenu-flag' is 
t."
 FLAG then becomes a member of etags command line arguments.  If flag
 is \"sort\", then toggle the value of `speedbar-sort-tags'.  If its
 value is \"show\" then toggle the value of
-`speedbar-show-unknown-files'.
-
-  This function is a convenience function for XEmacs menu created by
-Farzin Guilak <farzin@protocol.com>."
+`speedbar-show-unknown-files'."
   (interactive)
   (cond
    ((equal flag "sort")
diff --git a/lisp/startup.el b/lisp/startup.el
index b0fbf7a34c..04de7e42fe 100644
--- a/lisp/startup.el
+++ b/lisp/startup.el
@@ -541,7 +541,7 @@ DIRS are relative."
   (setq comp--compilable t))
 
 (defvar native-comp-eln-load-path)
-(defvar native-comp-deferred-compilation)
+(defvar inhibit-automatic-native-compilation)
 (defvar comp-enable-subr-trampolines)
 
 (defvar startup--original-eln-load-path nil
@@ -578,6 +578,10 @@ the updated value."
 It sets `command-line-processed', processes the command-line,
 reads the initialization files, etc.
 It is the default value of the variable `top-level'."
+  ;; Allow disabling automatic .elc->.eln processing.
+  (setq inhibit-automatic-native-compilation
+        (getenv "EMACS_INHIBIT_AUTOMATIC_NATIVE_COMPILATION"))
+
   (if command-line-processed
       (message internal--top-level-message)
     (setq command-line-processed t)
@@ -596,7 +600,7 @@ It is the default value of the variable `top-level'."
         ;; in this session.  This is necessary if libgccjit is not
         ;; available on MS-Windows, but Emacs was built with
         ;; native-compilation support.
-        (setq native-comp-deferred-compilation nil
+        (setq inhibit-automatic-native-compilation t
               comp-enable-subr-trampolines nil))
 
       ;; Form `native-comp-eln-load-path'.
@@ -718,8 +722,6 @@ It is the default value of the variable `top-level'."
     (let ((dir default-directory))
       (with-current-buffer "*Messages*"
         (messages-buffer-mode)
-        ;; Make it easy to do like "tail -f".
-        (setq-local window-point-insertion-type t)
         ;; Give *Messages* the same default-directory as *scratch*,
         ;; just to keep things predictable.
        (setq default-directory (or dir (expand-file-name "~/")))))
diff --git a/lisp/strokes.el b/lisp/strokes.el
index d7a9539316..0f84588a41 100644
--- a/lisp/strokes.el
+++ b/lisp/strokes.el
@@ -210,9 +210,6 @@ static char * stroke_xpm[] = {
   :link '(emacs-commentary-link "strokes")
   :group 'mouse)
 
-(define-obsolete-variable-alias 'strokes-modeline-string 'strokes-lighter
-  "24.3")
-
 (defcustom strokes-lighter " Strokes"
   "Mode line identifier for Strokes mode."
   :type 'string)
@@ -1362,11 +1359,9 @@ If STROKES-MAP is not given, `strokes-global-map' will 
be used instead."
   "Return t if STROKE1's command name precedes STROKE2's in lexicographic 
order."
   (string-lessp (cdr stroke1) (cdr stroke2)))
 
-(defvar strokes-mode-map
-  (let ((map (make-sparse-keymap)))
-    (define-key map [(shift down-mouse-2)] #'strokes-do-stroke)
-    (define-key map [(meta down-mouse-2)] #'strokes-do-complex-stroke)
-    map))
+(defvar-keymap strokes-mode-map
+  "S-<down-mouse-2>" #'strokes-do-stroke
+  "M-<down-mouse-2>" #'strokes-do-complex-stroke)
 
 ;;;###autoload
 (define-minor-mode strokes-mode
diff --git a/lisp/subr.el b/lisp/subr.el
index 36f5e2fee4..c975c216bb 100644
--- a/lisp/subr.el
+++ b/lisp/subr.el
@@ -311,29 +311,13 @@ Then evaluate RESULT to get return value, default nil.
     (signal 'wrong-type-argument (list 'consp spec)))
   (unless (<= 2 (length spec) 3)
     (signal 'wrong-number-of-arguments (list '(2 . 3) (length spec))))
-  ;; It would be cleaner to create an uninterned symbol,
-  ;; but that uses a lot more space when many functions in many files
-  ;; use dolist.
-  ;; FIXME: This cost disappears in byte-compiled lexical-binding files.
-  (let ((temp '--dolist-tail--))
-    ;; This test does not matter much because both semantics are acceptable,
-    ;; but one is slightly faster with dynamic scoping and the other is
-    ;; slightly faster (and has cleaner semantics) with lexical scoping.
-    (if lexical-binding
-        `(let ((,temp ,(nth 1 spec)))
-           (while ,temp
-             (let ((,(car spec) (car ,temp)))
-               ,@body
-               (setq ,temp (cdr ,temp))))
-           ,@(cdr (cdr spec)))
-      `(let ((,temp ,(nth 1 spec))
-             ,(car spec))
-         (while ,temp
-           (setq ,(car spec) (car ,temp))
+  (let ((tail (make-symbol "tail")))
+    `(let ((,tail ,(nth 1 spec)))
+       (while ,tail
+         (let ((,(car spec) (car ,tail)))
            ,@body
-           (setq ,temp (cdr ,temp)))
-         ,@(if (cdr (cdr spec))
-               `((setq ,(car spec) nil) ,@(cdr (cdr spec))))))))
+           (setq ,tail (cdr ,tail))))
+       ,@(cdr (cdr spec)))))
 
 (defmacro dotimes (spec &rest body)
   "Loop a certain number of times.
@@ -346,33 +330,19 @@ in compilation warnings about unused variables.
 
 \(fn (VAR COUNT [RESULT]) BODY...)"
   (declare (indent 1) (debug dolist))
-  ;; It would be cleaner to create an uninterned symbol,
-  ;; but that uses a lot more space when many functions in many files
-  ;; use dotimes.
-  ;; FIXME: This cost disappears in byte-compiled lexical-binding files.
-  (let ((temp '--dotimes-limit--)
-       (start 0)
-       (end (nth 1 spec)))
-    ;; This test does not matter much because both semantics are acceptable,
-    ;; but one is slightly faster with dynamic scoping and the other has
-    ;; cleaner semantics.
-    (if lexical-binding
-        (let ((counter '--dotimes-counter--))
-          `(let ((,temp ,end)
-                 (,counter ,start))
-             (while (< ,counter ,temp)
-               (let ((,(car spec) ,counter))
-                 ,@body)
-               (setq ,counter (1+ ,counter)))
-             ,@(if (cddr spec)
-                   ;; FIXME: This let often leads to "unused var" warnings.
-                   `((let ((,(car spec) ,counter)) ,@(cddr spec))))))
-      `(let ((,temp ,end)
-             (,(car spec) ,start))
-         (while (< ,(car spec) ,temp)
-           ,@body
-           (setq ,(car spec) (1+ ,(car spec))))
-         ,@(cdr (cdr spec))))))
+  (let ((var (nth 0 spec))
+        (end (nth 1 spec))
+        (upper-bound (make-symbol "upper-bound"))
+        (counter (make-symbol "counter")))
+    `(let ((,upper-bound ,end)
+           (,counter 0))
+       (while (< ,counter ,upper-bound)
+         (let ((,var ,counter))
+           ,@body)
+         (setq ,counter (1+ ,counter)))
+       ,@(if (cddr spec)
+             ;; FIXME: This let often leads to "unused var" warnings.
+             `((let ((,var ,counter)) ,@(cddr spec)))))))
 
 (defmacro declare (&rest _specs)
   "Do not evaluate any arguments, and return nil.
@@ -1592,6 +1562,21 @@ in the current Emacs session, then this function may 
return nil."
   ;; is this really correct? maybe remove mouse-movement?
   (memq (event-basic-type object) '(mouse-1 mouse-2 mouse-3 mouse-movement)))
 
+(defun event--posn-at-point ()
+  ;; Use `window-point' for the case when the current buffer
+  ;; is temporarily switched to some other buffer (bug#50256)
+  (let* ((pos (window-point))
+         (posn (posn-at-point pos)))
+    (if (null posn) ;; `pos' is "out of sight".
+        (list (selected-window) pos '(0 . 0) 0)
+      ;; If `pos' is inside a chunk of text hidden by an `invisible'
+      ;; or `display' property, `posn-at-point' returns the position
+      ;; that *is* visible, whereas `event--posn-at-point' is used
+      ;; when we have a keyboard event, whose position is `point' even
+      ;; if that position is invisible.
+      (setf (nth 5 posn) pos)
+      posn)))
+
 (defun event-start (event)
   "Return the starting position of EVENT.
 EVENT should be a mouse click, drag, or key press event.  If
@@ -1618,10 +1603,7 @@ nil or (STRING . POSITION)'.
 
 For more information, see Info node `(elisp)Click Events'."
   (or (and (consp event) (nth 1 event))
-      ;; Use `window-point' for the case when the current buffer
-      ;; is temporarily switched to some other buffer (bug#50256)
-      (posn-at-point (window-point))
-      (list (selected-window) (window-point) '(0 . 0) 0)))
+      (event--posn-at-point)))
 
 (defun event-end (event)
   "Return the ending position of EVENT.
@@ -1629,10 +1611,7 @@ EVENT should be a click, drag, or key press event.
 
 See `event-start' for a description of the value returned."
   (or (and (consp event) (nth (if (consp (nth 2 event)) 2 1) event))
-      ;; Use `window-point' for the case when the current buffer
-      ;; is temporarily switched to some other buffer (bug#50256)
-      (posn-at-point (window-point))
-      (list (selected-window) (window-point) '(0 . 0) 0)))
+      (event--posn-at-point)))
 
 (defsubst event-click-count (event)
   "Return the multi-click count of EVENT, a click or drag event.
@@ -1824,8 +1803,6 @@ be a list of the form returned by `event-start' and 
`event-end'."
 
 ;;;; Obsolescent names for functions.
 
-(make-obsolete 'buffer-has-markers-at nil "24.3")
-
 (make-obsolete 'invocation-directory "use the variable of the same name."
                "27.1")
 (make-obsolete 'invocation-name "use the variable of the same name." "27.1")
@@ -1891,6 +1868,17 @@ be a list of the form returned by `event-start' and 
`event-end'."
 ;; in warnings when using `values' in let-bindings.
 ;;(make-obsolete-variable 'values "no longer used" "28.1")
 
+(defvar max-specpdl-size 2500
+  "Former limit on specbindings, now without effect.
+This variable used to limit the size of the specpdl stack which,
+among other things, holds dynamic variable bindings and `unwind-protect'
+activations.  To prevent runaway recursion, use `max-lisp-eval-depth'
+instead; it will indirectly limit the specpdl stack size as well.")
+(make-obsolete-variable 'max-specpdl-size nil "29.1")
+
+(make-obsolete-variable 'native-comp-deferred-compilation
+                        'inhibit-automatic-native-compilation "29.1")
+
 
 ;;;; Alternate names for functions - these are not being phased out.
 
@@ -1913,8 +1901,10 @@ be a list of the form returned by `event-start' and 
`event-end'."
 (defalias 'mkdir #'make-directory)
 
 ;; These were the XEmacs names, now obsolete:
-(define-obsolete-function-alias 'point-at-eol #'line-end-position "29.1")
-(define-obsolete-function-alias 'point-at-bol #'line-beginning-position "29.1")
+(defalias 'point-at-eol #'line-end-position)
+(make-obsolete 'point-at-eol "use `line-end-position' or `pos-eol' instead." 
"29.1")
+(defalias 'point-at-bol #'line-beginning-position)
+(make-obsolete 'point-at-bol "use `line-beginning-position' or `pos-bol' 
instead." "29.1")
 (define-obsolete-function-alias 'user-original-login-name #'user-login-name 
"28.1")
 
 ;; These are in obsolete/autoload.el, but are commonly used by
@@ -2527,7 +2517,20 @@ The variable list SPEC is the same as in `if-let'."
   (declare (indent 1) (debug if-let))
   (list 'if-let spec (macroexp-progn body)))
 
+(defmacro while-let (spec &rest body)
+  "Bind variables according to SPEC and conditionally evaluate BODY.
+Evaluate each binding in turn, stopping if a binding value is nil.
+If all bindings are non-nil, eval BODY and repeat.
 
+The variable list SPEC is the same as in `if-let'."
+  (declare (indent 1) (debug if-let))
+  (let ((done (gensym "done")))
+    `(catch ',done
+       (while t
+         (if-let* ,spec
+             (progn
+               ,@body)
+           (throw ',done nil))))))
 
 ;; PUBLIC: find if the current mode derives from another.
 
@@ -3544,11 +3547,12 @@ like) while `y-or-n-p' is running)."
                            (if (or (zerop l) (eq ?\s (aref prompt (1- l))))
                                "" " ")
                            (if dialog ""
-                              (if help-form
-                                  (format "(y, n or %s) "
-                                         (key-description
-                                           (vector help-char)))
-                                "(y or n) "))))))
+                              (substitute-command-keys
+                               (if help-form
+                                   (format "(\\`y', \\`n' or \\`%s') "
+                                           (key-description
+                                            (vector help-char)))
+                                 "(\\`y' or \\`n') ")))))))
         ;; Preserve the actual command that eventually called
         ;; `y-or-n-p' (otherwise `repeat' will be repeating
         ;; `exit-minibuffer').
@@ -3786,10 +3790,6 @@ This finishes the change group by reverting all of its 
changes."
 
 ;;;; Display-related functions.
 
-;; For compatibility.
-(define-obsolete-function-alias 'redraw-modeline
-  #'force-mode-line-update "24.3")
-
 (defun momentary-string-display (string pos &optional exit-char message)
   "Momentarily display STRING in the buffer at POS.
 Display remains until next event is input.
@@ -4060,6 +4060,13 @@ system's shell."
 Otherwise, return nil."
   (or (stringp object) (null object)))
 
+(defun list-of-strings-p (object)
+  "Return t if OBJECT is nil or a list of strings."
+  (declare (pure t) (side-effect-free error-free))
+  (while (and (consp object) (stringp (car object)))
+    (setq object (cdr object)))
+  (null object))
+
 (defun booleanp (object)
   "Return t if OBJECT is one of the two canonical boolean values: t or nil.
 Otherwise, return nil."
@@ -4253,15 +4260,17 @@ Comparisons and replacements are done with fixed case."
         (error "End after end of buffer"))
     (setq end (point-max)))
   (save-excursion
-    (let ((matches 0)
-          (case-fold-search nil))
-      (goto-char start)
-      (while (search-forward string end t)
-        (delete-region (match-beginning 0) (match-end 0))
-        (insert replacement)
-        (setq matches (1+ matches)))
-      (and (not (zerop matches))
-           matches))))
+    (goto-char start)
+    (save-restriction
+      (narrow-to-region start end)
+      (let ((matches 0)
+            (case-fold-search nil))
+        (while (search-forward string nil t)
+          (delete-region (match-beginning 0) (match-end 0))
+          (insert replacement)
+          (setq matches (1+ matches)))
+        (and (not (zerop matches))
+             matches)))))
 
 (defun replace-regexp-in-region (regexp replacement &optional start end)
   "Replace REGEXP with REPLACEMENT in the region from START to END.
@@ -4288,14 +4297,16 @@ REPLACEMENT can use the following special elements:
         (error "End after end of buffer"))
     (setq end (point-max)))
   (save-excursion
-    (let ((matches 0)
-          (case-fold-search nil))
-      (goto-char start)
-      (while (re-search-forward regexp end t)
-        (replace-match replacement t)
-        (setq matches (1+ matches)))
-      (and (not (zerop matches))
-           matches))))
+    (goto-char start)
+    (save-restriction
+      (narrow-to-region start end)
+      (let ((matches 0)
+            (case-fold-search nil))
+          (while (re-search-forward regexp nil t)
+          (replace-match replacement t)
+          (setq matches (1+ matches)))
+        (and (not (zerop matches))
+             matches)))))
 
 (defun yank-handle-font-lock-face-property (face start end)
   "If `font-lock-defaults' is nil, apply FACE as a `face' property.
@@ -4853,16 +4864,26 @@ the function `undo--wrap-and-run-primitive-undo'."
       (let ((undo--combining-change-calls t))
        (if (not inhibit-modification-hooks)
            (run-hook-with-args 'before-change-functions beg end))
-       (let (;; (inhibit-modification-hooks t)
-              (before-change-functions
-               ;; Ugly Hack: if the body uses syntax-ppss/syntax-propertize
-               ;; (e.g. via a regexp-search or sexp-movement triggering
-               ;; on-the-fly syntax-propertize), make sure that this gets
-               ;; properly refreshed after subsequent changes.
-               (if (memq #'syntax-ppss-flush-cache before-change-functions)
-                   '(syntax-ppss-flush-cache)))
-              after-change-functions)
-         (setq result (funcall body)))
+       (let ((bcf before-change-functions)
+             (acf after-change-functions)
+             (local-bcf (local-variable-p 'before-change-functions))
+             (local-acf (local-variable-p 'after-change-functions)))
+         (unwind-protect
+              ;; FIXME: WIBNI we could just use `inhibit-modification-hooks'?
+              (progn
+                ;; Ugly Hack: if the body uses syntax-ppss/syntax-propertize
+                ;; (e.g. via a regexp-search or sexp-movement triggering
+                ;; on-the-fly syntax-propertize), make sure that this gets
+                ;; properly refreshed after subsequent changes.
+               (setq-local before-change-functions
+                            (if (memq #'syntax-ppss-flush-cache bcf)
+                                '(syntax-ppss-flush-cache)))
+                (setq-local after-change-functions nil)
+               (setq result (funcall body)))
+           (if local-bcf (setq before-change-functions bcf)
+             (kill-local-variable 'before-change-functions))
+           (if local-acf (setq after-change-functions acf)
+             (kill-local-variable 'after-change-functions))))
         (when (not (eq buffer-undo-list t))
           (let ((ap-elt
                 (list 'apply
@@ -4984,10 +5005,6 @@ If `default-directory' is already an existing directory, 
it's not changed."
 
 ;;; Matching and match data.
 
-;; We use save-match-data-internal as the local variable because
-;; that works ok in practice (people should not use that variable elsewhere).
-;; We used to use an uninterned symbol; the compiler handles that properly
-;; now, but it generates slower code.
 (defmacro save-match-data (&rest body)
   "Execute the BODY forms, restoring the global value of the match data.
 The value returned is the value of the last form in BODY.
@@ -4999,13 +5016,12 @@ rather than your caller's match data."
   ;; because that makes a bootstrapping problem
   ;; if you need to recompile all the Lisp files using interpreted code.
   (declare (indent 0) (debug t))
-  (list 'let
-       '((save-match-data-internal (match-data)))
-       (list 'unwind-protect
-             (cons 'progn body)
-             ;; It is safe to free (evaporate) markers immediately here,
-             ;; as Lisp programs should not copy from save-match-data-internal.
-             '(set-match-data save-match-data-internal 'evaporate))))
+  (let ((saved-match-data (make-symbol "saved-match-data")))
+    (list 'let
+         (list (list saved-match-data '(match-data)))
+         (list 'unwind-protect
+               (cons 'progn body)
+               (list 'set-match-data saved-match-data t)))))
 
 (defun match-string (num &optional string)
   "Return the string of text matched by the previous search or regexp 
operation.
@@ -5243,6 +5259,8 @@ Modifies the match data; use `save-match-data' if 
necessary."
 
     (nreverse list)))
 
+(defalias 'string-split #'split-string)
+
 (defun combine-and-quote-strings (strings &optional separator)
   "Concatenate the STRINGS, adding the SEPARATOR (default \" \").
 This tries to quote the strings to avoid ambiguity such that
@@ -6990,32 +7008,32 @@ CONDITION is either:
         (lambda (conditions)
           (catch 'match
             (dolist (condition conditions)
-              (when (cond
-                     ((eq condition t))
-                     ((stringp condition)
-                      (string-match-p condition (buffer-name buffer)))
-                     ((functionp condition)
-                      (if (eq 1 (cdr (func-arity condition)))
-                          (funcall condition buffer)
-                        (funcall condition buffer arg)))
-                     ((eq (car-safe condition) 'major-mode)
-                      (eq
-                       (buffer-local-value 'major-mode buffer)
-                       (cdr condition)))
-                     ((eq (car-safe condition) 'derived-mode)
-                      (provided-mode-derived-p
-                       (buffer-local-value 'major-mode buffer)
-                       (cdr condition)))
-                     ((eq (car-safe condition) 'not)
-                      (not (funcall match (cdr condition))))
-                     ((eq (car-safe condition) 'or)
-                      (funcall match (cdr condition)))
-                     ((eq (car-safe condition) 'and)
-                      (catch 'fail
-                        (dolist (c (cdr conditions))
-                          (unless (funcall match c)
-                            (throw 'fail nil)))
-                        t)))
+              (when (pcase condition
+                      ('t t)
+                      ((pred stringp)
+                       (string-match-p condition (buffer-name buffer)))
+                      ((pred functionp)
+                       (if (eq 1 (cdr (func-arity condition)))
+                           (funcall condition buffer)
+                         (funcall condition buffer arg)))
+                      (`(major-mode . ,mode)
+                       (eq
+                        (buffer-local-value 'major-mode buffer)
+                        mode))
+                      (`(derived-mode . ,mode)
+                       (provided-mode-derived-p
+                        (buffer-local-value 'major-mode buffer)
+                        mode))
+                      (`(not . ,cond)
+                       (not (funcall match cond)))
+                      (`(or . ,args)
+                       (funcall match args))
+                      (`(and . ,args)
+                       (catch 'fail
+                         (dolist (c args)
+                           (unless (funcall match (list c))
+                             (throw 'fail nil)))
+                         t)))
                 (throw 'match t)))))))
     (funcall match (list condition))))
 
diff --git a/lisp/t-mouse.el b/lisp/t-mouse.el
index cdfc30c879..7a4e7f330e 100644
--- a/lisp/t-mouse.el
+++ b/lisp/t-mouse.el
@@ -62,6 +62,9 @@
     (gpm-mouse-stop))
   (set-terminal-parameter nil 'gpm-mouse-active nil))
 
+(defun gpm-mouse-tty-setup ()
+  (if gpm-mouse-mode (gpm-mouse-enable) (gpm-mouse-disable)))
+
 ;;;###autoload
 (define-minor-mode gpm-mouse-mode
   "Toggle mouse support in GNU/Linux consoles (GPM Mouse mode).
@@ -80,7 +83,9 @@ GPM.  This is due to limitations in GPM and the Linux kernel."
                         (terminal-parameter terminal 'gpm-mouse-active))))
       ;; Simulate selecting a terminal by selecting one of its frames ;-(
       (with-selected-frame (car (frames-on-display-list terminal))
-        (if gpm-mouse-mode (gpm-mouse-enable) (gpm-mouse-disable))))))
+        (gpm-mouse-tty-setup))))
+  (when gpm-mouse-mode
+    (add-hook 'tty-setup-hook #'gpm-mouse-tty-setup)))
 
 (provide 't-mouse)
 
diff --git a/lisp/tab-bar.el b/lisp/tab-bar.el
index cf5ae09a24..abefd996a8 100644
--- a/lisp/tab-bar.el
+++ b/lisp/tab-bar.el
@@ -2411,6 +2411,7 @@ When `switch-to-buffer-obey-display-actions' is non-nil,
 (keymap-set tab-prefix-map "M"   #'tab-move-to)
 (keymap-set tab-prefix-map "G"   #'tab-group)
 (keymap-set tab-prefix-map "r"   #'tab-rename)
+(keymap-set tab-prefix-map "^ f"  #'tab-detach)
 (keymap-set tab-prefix-map "RET" #'tab-switch)
 (keymap-set tab-prefix-map "b"   #'switch-to-buffer-other-tab)
 (keymap-set tab-prefix-map "f"   #'find-file-other-tab)
diff --git a/lisp/tab-line.el b/lisp/tab-line.el
index 3e3b4c9559..94e8f29a95 100644
--- a/lisp/tab-line.el
+++ b/lisp/tab-line.el
@@ -135,45 +135,35 @@ function `tab-line-tab-face-group'."
   :group 'tab-line-faces)
 
 
-(defvar tab-line-tab-map
-  (let ((map (make-sparse-keymap)))
-    (define-key map [tab-line down-mouse-1] 'tab-line-select-tab)
-    (define-key map [tab-line mouse-2] 'tab-line-close-tab)
-    (define-key map [tab-line down-mouse-3] 'tab-line-tab-context-menu)
-    (define-key map "\C-m" 'tab-line-select-tab)
-    map)
-  "Local keymap for `tab-line-mode' window tabs.")
-
-(defvar tab-line-add-map
-  (let ((map (make-sparse-keymap)))
-    (define-key map [tab-line down-mouse-1] 'tab-line-new-tab)
-    (define-key map [tab-line down-mouse-2] 'tab-line-new-tab)
-    (define-key map "\C-m" 'tab-line-new-tab)
-    map)
-  "Local keymap to add `tab-line-mode' window tabs.")
-
-(defvar tab-line-tab-close-map
-  (let ((map (make-sparse-keymap)))
-    (define-key map [tab-line mouse-1] 'tab-line-close-tab)
-    (define-key map [tab-line mouse-2] 'tab-line-close-tab)
-    map)
-  "Local keymap to close `tab-line-mode' window tabs.")
-
-(defvar tab-line-left-map
-  (let ((map (make-sparse-keymap)))
-    (define-key map [tab-line down-mouse-1] 'tab-line-hscroll-left)
-    (define-key map [tab-line down-mouse-2] 'tab-line-hscroll-left)
-    (define-key map "\C-m" 'tab-line-new-tab)
-    map)
-  "Local keymap to scroll `tab-line-mode' window tabs to the left.")
-
-(defvar tab-line-right-map
-  (let ((map (make-sparse-keymap)))
-    (define-key map [tab-line down-mouse-1] 'tab-line-hscroll-right)
-    (define-key map [tab-line down-mouse-2] 'tab-line-hscroll-right)
-    (define-key map "\C-m" 'tab-line-new-tab)
-    map)
-  "Local keymap to scroll `tab-line-mode' window tabs to the right.")
+(defvar-keymap tab-line-tab-map
+  :doc "Local keymap for `tab-line-mode' window tabs."
+  "<tab-line> <down-mouse-1>" #'tab-line-select-tab
+  "<tab-line> <mouse-2>"      #'tab-line-close-tab
+  "<tab-line> <down-mouse-3>" #'tab-line-tab-context-menu
+  "RET" #'tab-line-select-tab)
+
+(defvar-keymap tab-line-add-map
+  :doc "Local keymap to add `tab-line-mode' window tabs."
+  "<tab-line> <down-mouse-1>" #'tab-line-new-tab
+  "<tab-line> <down-mouse-2>" #'tab-line-new-tab
+  "RET" #'tab-line-new-tab)
+
+(defvar-keymap tab-line-tab-close-map
+  :doc "Local keymap to close `tab-line-mode' window tabs."
+  "<tab-line> <mouse-1>" #'tab-line-close-tab
+  "<tab-line> <mouse-2>" #'tab-line-close-tab)
+
+(defvar-keymap tab-line-left-map
+  :doc "Local keymap to scroll `tab-line-mode' window tabs to the left."
+  "<tab-line> <down-mouse-1>" #'tab-line-hscroll-left
+  "<tab-line> <down-mouse-2>" #'tab-line-hscroll-left
+  "RET" #'tab-line-new-tab)
+
+(defvar-keymap tab-line-right-map
+  :doc "Local keymap to scroll `tab-line-mode' window tabs to the right."
+  "<tab-line> <down-mouse-1>" #'tab-line-hscroll-right
+  "<tab-line> <down-mouse-2>" #'tab-line-hscroll-right
+  "RET" #'tab-line-new-tab)
 
 
 (defcustom tab-line-new-tab-choice t
diff --git a/lisp/tar-mode.el b/lisp/tar-mode.el
index 20ad6e1e46..49d84319c5 100644
--- a/lisp/tar-mode.el
+++ b/lisp/tar-mode.el
@@ -169,7 +169,7 @@ This information is useful, but it takes screen space away 
from file names."
 
 (defun tar-swap-data ()
   "Swap buffer contents between current buffer and `tar-data-buffer'.
-Preserve the modified states of the buffers and set `buffer-swapped-with'."
+Preserve the modified states of the buffers and set `tar-data-swapped'."
   (let ((data-buffer-modified-p (buffer-modified-p tar-data-buffer))
        (current-buffer-modified-p (buffer-modified-p)))
     (buffer-swap-text tar-data-buffer)
diff --git a/lisp/term.el b/lisp/term.el
index 797fb18074..755c220270 100644
--- a/lisp/term.el
+++ b/lisp/term.el
@@ -755,25 +755,8 @@ Buffer local variable.")
    term-color-bright-cyan
    term-color-bright-white])
 
-(defcustom term-default-fg-color nil
-  "If non-nil, default color for foreground in Term mode."
-  :group 'term
-  :type '(choice (const nil) (string :tag "color")))
-(make-obsolete-variable 'term-default-fg-color "use the face `term' instead."
-                        "24.3")
-
-(defcustom term-default-bg-color nil
-  "If non-nil, default color for foreground in Term mode."
-  :group 'term
-  :type '(choice (const nil) (string :tag "color")))
-(make-obsolete-variable 'term-default-bg-color "use the face `term' instead."
-                        "24.3")
-
 (defface term
-  `((t
-     :foreground ,term-default-fg-color
-     :background ,term-default-bg-color
-     :inherit default))
+  `((t :inherit default))
   "Default face to use in Term mode."
   :group 'term)
 
diff --git a/lisp/term/fbterm.el b/lisp/term/fbterm.el
new file mode 100644
index 0000000000..ad7150c1a1
--- /dev/null
+++ b/lisp/term/fbterm.el
@@ -0,0 +1,27 @@
+;;; fbterm.el  -*- lexical-binding:t -*-
+
+;; Copyright (C) 2022 Free Software Foundation, Inc.
+
+;; Note that, in some versions of fbterm, the TERM environment
+;; variable is set to "linux".  When that's the case, the code below
+;; will not be executed, and only 8 colors will be available.  It is
+;; therefore necessary, with these versions of fbterm, to set that
+;; environment variable to "fbterm" to enable its 256 color mode
+;; extension.  See also the node "Emacs in a Linux console" of the
+;; Emacs FAQ.
+
+(require 'term/xterm)
+
+(defun terminal-init-fbterm ()
+  "Terminal initialization function for fbterm."
+
+  ;; fbterm can't display underlines, even though its terminfo data
+  ;; says it can.
+  (tty-no-underline)
+
+  ;; fbterm supports xterm's 256 color mode extension.
+  (xterm-register-default-colors xterm-standard-colors))
+
+(provide 'term/fbterm)
+
+;;; fbterm.el ends here
diff --git a/lisp/term/haiku-win.el b/lisp/term/haiku-win.el
index a16169d477..24942d96c1 100644
--- a/lisp/term/haiku-win.el
+++ b/lisp/term/haiku-win.el
@@ -598,6 +598,45 @@ MODIFIERS is the internal modifier mask of the wheel 
movement."
     ;; the Deskbar will not, so kill ourself here.
     (unless cancel-shutdown (kill-emacs))))
 
+;;;; Wallpaper support.
+
+
+(declare-function haiku-write-node-attribute "haikuselect.c")
+(declare-function haiku-send-message "haikuselect.c")
+
+(defun haiku-set-wallpaper (file)
+  "Make FILE the wallpaper.
+Set the desktop background to the image FILE, on all workspaces,
+with an offset of 0, 0."
+  (let ((encoded-file (encode-coding-string
+                       (expand-file-name file)
+                       (or file-name-coding-system
+                           default-file-name-coding-system))))
+    ;; Write the necessary information to the desktop directory.
+    (haiku-write-node-attribute "/boot/home/Desktop"
+                                "be:bgndimginfo"
+                                (list '(type . 0)
+                                      '("be:bgndimginfoerasetext" bool t)
+                                      (list "be:bgndimginfopath" 'string
+                                            encoded-file)
+                                      '("be:bgndimginfoworkspaces" long
+                                        ;; This is a mask of all the
+                                        ;; workspaces the background
+                                        ;; image will be applied to.  It
+                                        ;; is treated as an unsigned
+                                        ;; value by the Tracker, despite
+                                        ;; the type being signed.
+                                        -1)
+                                      ;; Don't apply an offset
+                                      '("be:bgndimginfooffset" point (0 . 0))
+                                      ;; Don't stretch or crop or anything
+                                      '("be:bgndimginfomode" long 0)
+                                      ;; Don't apply a set
+                                      '("be:bgndimginfoset" long 0)))
+    ;; Tell the tracker to redisplay the wallpaper.
+    (haiku-send-message "application/x-vnd.Be-TRAK"
+                        (list (cons 'type (haiku-numeric-enum Tbgr))))))
+
 
 ;;;; Cursors.
 
diff --git a/lisp/term/linux.el b/lisp/term/linux.el
index ab5a6d8698..f24af3f134 100644
--- a/lisp/term/linux.el
+++ b/lisp/term/linux.el
@@ -2,8 +2,6 @@
 
 ;; The Linux console handles Latin-1 by default.
 
-(declare-function gpm-mouse-enable "t-mouse" ())
-
 (defun terminal-init-linux ()
   "Terminal initialization function for linux."
   (unless (terminal-coding-system)
@@ -15,15 +13,11 @@
   ;; Compositions confuse cursor movement.
   (setq-default auto-composition-mode "linux")
 
-  (ignore-errors (when gpm-mouse-mode (require 't-mouse) (gpm-mouse-enable)))
-
-  ;; Don't translate ESC TAB to backtab as directed
-  ;; by ncurses-6.3.
+  ;; Don't translate ESC TAB to backtab as directed by ncurses-6.3.
   (define-key input-decode-map "\e\t" nil)
   
   ;; Make Latin-1 input characters work, too.
-  ;; Meta will continue to work, because the kernel
-  ;; turns that into Escape.
+  ;; Meta will continue to work, because the kernel turns that into Escape.
 
   ;; The arg only matters in that it is not t or nil.
   (set-input-meta-mode 'iso-latin-1))
diff --git a/lisp/term/pgtk-win.el b/lisp/term/pgtk-win.el
index b93e259d82..20f1573916 100644
--- a/lisp/term/pgtk-win.el
+++ b/lisp/term/pgtk-win.el
@@ -371,6 +371,7 @@ This uses `icon-map-list' to map icon file names to stock 
icon names."
   "Return the device class of NAME.
 Users should not call this function; see `device-class' instead."
   (cond
+   ((not name) nil)
    ((string-match-p "XTEST" name) 'test)
    ((string= "Virtual core pointer" name) 'core-pointer)
    ((string= "Virtual core keyboard" name) 'core-keyboard)
diff --git a/lisp/term/x-win.el b/lisp/term/x-win.el
index 38266baa96..57c6b785e7 100644
--- a/lisp/term/x-win.el
+++ b/lisp/term/x-win.el
@@ -1573,45 +1573,68 @@ frames on all displays."
 (defun x-device-class (name)
   "Return the device class of NAME.
 Users should not call this function; see `device-class' instead."
-  (let ((downcased-name (downcase name)))
-    (cond
-     ((string-match-p "XTEST" name) 'test)
-     ((string= "Virtual core pointer" name) 'core-pointer)
-     ((string= "Virtual core keyboard" name) 'core-keyboard)
-     ((string-match-p "eraser" downcased-name) 'eraser)
-     ((string-match-p " pad" downcased-name) 'pad)
-     ((or (or (string-match-p "wacom" downcased-name)
-              (string-match-p "pen" downcased-name))
-          (string-match-p "stylus" downcased-name))
-      'pen)
-     ((or (string-prefix-p "xwayland-touch:" name)
-          (string-match-p "touchscreen" downcased-name))
-      'touchscreen)
-     ((or (string-match-p "trackpoint" downcased-name)
-          (string-match-p "stick" downcased-name))
-      'trackpoint)
-     ((or (string-match-p "mouse" downcased-name)
-          (string-match-p "optical" downcased-name)
-          (string-match-p "pointer" downcased-name))
-      'mouse)
-     ((string-match-p "cursor" downcased-name) 'puck)
-     ((or (string-match-p "keyboard" downcased-name)
-          ;; One of my cheap keyboards is really named this...
-          (string= name "USB USB Keykoard"))
-      'keyboard)
-     ((string-match-p "button" downcased-name) 'power-button)
-     ((string-match-p "touchpad" downcased-name) 'touchpad)
-     ((or (string-match-p "midi" downcased-name)
-          (string-match-p "piano" downcased-name))
-      'piano)
-     ((or (string-match-p "wskbd" downcased-name) ; NetBSD/OpenBSD
-          (and (string-match-p "/dev" downcased-name)
-               (string-match-p "kbd" downcased-name)))
-      'keyboard))))
+  (and name
+       (let ((downcased-name (downcase name)))
+         (cond
+          ((string-match-p "XTEST" name) 'test)
+          ((string= "Virtual core pointer" name) 'core-pointer)
+          ((string= "Virtual core keyboard" name) 'core-keyboard)
+          ((string-match-p "eraser" downcased-name) 'eraser)
+          ((string-match-p " pad" downcased-name) 'pad)
+          ((or (or (string-match-p "wacom" downcased-name)
+                   (string-match-p "pen" downcased-name))
+               (string-match-p "stylus" downcased-name))
+           'pen)
+          ((or (string-prefix-p "xwayland-touch:" name)
+               (string-match-p "touchscreen" downcased-name))
+           'touchscreen)
+          ((or (string-match-p "trackpoint" downcased-name)
+               (string-match-p "stick" downcased-name))
+           'trackpoint)
+          ((or (string-match-p "mouse" downcased-name)
+               (string-match-p "optical" downcased-name)
+               (string-match-p "pointer" downcased-name))
+           'mouse)
+          ((string-match-p "cursor" downcased-name) 'puck)
+          ((or (string-match-p "keyboard" downcased-name)
+               ;; One of my cheap keyboards is really named this...
+               (string= name "USB USB Keykoard"))
+           'keyboard)
+          ((string-match-p "button" downcased-name) 'power-button)
+          ((string-match-p "touchpad" downcased-name) 'touchpad)
+          ((or (string-match-p "midi" downcased-name)
+               (string-match-p "piano" downcased-name))
+           'piano)
+          ((or (string-match-p "wskbd" downcased-name) ; NetBSD/OpenBSD
+               (and (string-match-p "/dev" downcased-name)
+                    (string-match-p "kbd" downcased-name)))
+           'keyboard)))))
 
 (setq x-dnd-movement-function #'x-dnd-movement)
 (setq x-dnd-unsupported-drop-function #'x-dnd-handle-unsupported-drop)
 
+(defvar x-input-coding-function)
+
+(defun x-get-input-coding-system (x-locale)
+  "Return a coding system for the locale X-LOCALE.
+Return a coding system that is able to decode text sent with the
+X input method locale X-LOCALE, or nil if no coding system was
+found."
+  (if (equal x-locale "C")
+      ;; Treat the C locale specially, as it means "ascii" under X.
+      'ascii
+    (let ((locale (downcase x-locale)))
+      (or (locale-name-match locale locale-preferred-coding-systems)
+         (when locale
+           (if (string-match "\\.\\([^@]+\\)" locale)
+               (locale-charset-to-coding-system
+                (match-string 1 locale))))
+          (let ((language-name
+                 (locale-name-match locale locale-language-names)))
+            (and (consp language-name) (cdr language-name)))))))
+
+(setq x-input-coding-function #'x-get-input-coding-system)
+
 (provide 'x-win)
 (provide 'term/x-win)
 
diff --git a/lisp/textmodes/artist.el b/lisp/textmodes/artist.el
index 2cf9ded04b..76675328da 100644
--- a/lisp/textmodes/artist.el
+++ b/lisp/textmodes/artist.el
@@ -5341,8 +5341,6 @@ The event, EV, is the mouse event."
   (require 'reporter)
   (if (y-or-n-p "Do you want to submit a bug report on Artist? ")
       (let ((vars '(window-system
-                   window-system-version
-                   ;;
                    artist-rubber-banding
                    artist-interface-with-rect
                    artist-aspect-ratio
diff --git a/lisp/textmodes/emacs-authors-mode.el 
b/lisp/textmodes/emacs-authors-mode.el
index 866822c103..3eba8e0e45 100644
--- a/lisp/textmodes/emacs-authors-mode.el
+++ b/lisp/textmodes/emacs-authors-mode.el
@@ -130,7 +130,20 @@ Provides some basic font locking and not much else."
               '(emacs-authors-mode-font-lock-keywords nil nil ((?_ . "w"))))
   (setq font-lock-multiline nil)
   (setq imenu-generic-expression emacs-authors-imenu-generic-expression)
-  (emacs-etc--hide-local-variables))
+  (emacs-etc--hide-local-variables)
+  (setq-local outline-regexp (rx (+ (not (any ":\n"))) ": "
+                                 (or "changed" "co-wrote" "wrote") " ")
+              outline-minor-mode-cycle t
+              outline-level
+              (lambda ()
+                (if (looking-at (rx bol
+                                    (or (or "  "
+                                            (seq "and " (or "co-wrote"
+                                                            "changed")))
+                                        eol)))
+                    2
+                  1)))
+  (outline-minor-mode))
 
 (define-obsolete-face-alias 'etc-authors-default 'emacs-authors-default "29.1")
 (define-obsolete-face-alias 'etc-authors-author 'emacs-authors-author "29.1")
diff --git a/lisp/textmodes/emacs-news-mode.el 
b/lisp/textmodes/emacs-news-mode.el
index 022e17c934..d9decae4df 100644
--- a/lisp/textmodes/emacs-news-mode.el
+++ b/lisp/textmodes/emacs-news-mode.el
@@ -73,11 +73,14 @@
 
 (defun emacs-news--mode-common ()
   (setq-local font-lock-defaults '(emacs-news-mode-font-lock-keywords t))
-  (setq-local outline-regexp "\\(:? +\\)?\\(\\*+\\) "
+  ;; This `outline-regexp' matches leading spaces inserted
+  ;; by the current implementation of `outline-minor-mode-use-buttons'.
+  (setq-local outline-regexp "\\(?: +\\)?\\(\\*+\\) "
+              outline-level (lambda () (length (match-string 1)))
               outline-minor-mode-cycle t
-              outline-level (lambda () (length (match-string 2)))
               outline-minor-mode-highlight 'append)
   (outline-minor-mode)
+  (setq-local imenu-generic-expression outline-imenu-generic-expression)
   (emacs-etc--hide-local-variables))
 
 ;;;###autoload
@@ -274,6 +277,17 @@ documentation marks on the previous line."
     (forward-line -1))
   (open-line n))
 
+(defun emacs-news-delete-temporary-markers ()
+  "Delete any temporary markers.
+This is used when preparing a new release of Emacs."
+  (interactive nil emacs-news-mode)
+  (goto-char (point-min))
+  (re-search-forward "^Temporary note:$")
+  (forward-line -1)
+  (delete-region (point) (save-excursion (forward-paragraph) (point)))
+  (while (re-search-forward (rx bol (or "+++" "---") eol) nil t)
+    (delete-line)))
+
 (provide 'emacs-news-mode)
 
 ;;; emacs-news-mode.el ends here
diff --git a/lisp/textmodes/flyspell.el b/lisp/textmodes/flyspell.el
index a893bc7b9c..774e7ac737 100644
--- a/lisp/textmodes/flyspell.el
+++ b/lisp/textmodes/flyspell.el
@@ -425,11 +425,9 @@ like <img alt=\"Some thing.\">."
 ;;*---------------------------------------------------------------------*/
 ;;*    The minor mode declaration.                                      */
 ;;*---------------------------------------------------------------------*/
-(defvar flyspell-mouse-map
-  (let ((map (make-sparse-keymap)))
-    (define-key map [mouse-2] 'flyspell-correct-word)
-    map)
-  "Keymap for Flyspell to put on erroneous words.")
+(defvar-keymap flyspell-mouse-map
+  :doc "Keymap for Flyspell to put on erroneous words."
+  "<mouse-2>" #'flyspell-correct-word)
 
 (defvar flyspell-mode-map
   (let ((map (make-sparse-keymap)))
diff --git a/lisp/textmodes/ispell.el b/lisp/textmodes/ispell.el
index 8e63368809..b3fb326cf9 100644
--- a/lisp/textmodes/ispell.el
+++ b/lisp/textmodes/ispell.el
@@ -262,12 +262,14 @@ This must be an absolute file name."
   "Non-nil means use `look' rather than `grep'.
 Default is based on whether `look' seems to be available."
   :type 'boolean)
+(make-obsolete-variable 'ispell-look-p nil "29.1")
 
 (defcustom ispell-have-new-look nil
   "Non-nil means use the `-r' option (regexp) when running `look'."
   :type 'boolean)
+(make-obsolete-variable 'ispell-have-new-look nil "29.1")
 
-(defcustom ispell-look-options (if ispell-have-new-look "-dfr" "-df")
+(defcustom ispell-look-options "-df"
   "String of command options for `ispell-look-command'."
   :type 'string)
 
@@ -738,7 +740,8 @@ Otherwise returns the library directory name, if that is 
defined."
   "Execute the forms in BODY with a reasonable `default-directory'."
   (declare (indent 0) (debug t))
   `(let ((default-directory default-directory))
-     (unless (file-accessible-directory-p default-directory)
+     (unless (and (not (file-remote-p default-directory))
+                  (file-accessible-directory-p default-directory))
        (setq default-directory (expand-file-name "~/")))
      ,@body))
 
@@ -2519,8 +2522,10 @@ if defined."
 
   (let* ((process-connection-type ispell-use-ptys-p)
         (wild-p (string-search "*" word))
-        (look-p (and ispell-look-p     ; Only use look for an exact match.
-                     (or ispell-have-new-look (not wild-p))))
+        (look-p (and ispell-look-command
+                      (file-exists-p ispell-look-command)
+                      ;; Only use look for an exact match.
+                     (not wild-p)))
         (prog (if look-p ispell-look-command ispell-grep-command))
         (args (if look-p ispell-look-options ispell-grep-options))
         status results loc)
diff --git a/lisp/textmodes/less-css-mode.el b/lisp/textmodes/less-css-mode.el
index a0462756b0..5d17b390f4 100644
--- a/lisp/textmodes/less-css-mode.el
+++ b/lisp/textmodes/less-css-mode.el
@@ -209,10 +209,8 @@ directory by default."
     (modify-syntax-entry ?. "'" st)
     st))
 
-(defvar less-css-mode-map
-  (let ((map (make-sparse-keymap)))
-    (define-key map "\C-c\C-c" #'less-css-compile)
-    map))
+(defvar-keymap less-css-mode-map
+  "C-c C-c" #'less-css-compile)
 
 ;;;###autoload (add-to-list 'auto-mode-alist '("\\.less\\'" . less-css-mode))
 ;;;###autoload
diff --git a/lisp/textmodes/page-ext.el b/lisp/textmodes/page-ext.el
index 6b71f26e4f..05c02308e5 100644
--- a/lisp/textmodes/page-ext.el
+++ b/lisp/textmodes/page-ext.el
@@ -1,7 +1,6 @@
 ;;; page-ext.el --- extended page handling commands  -*- lexical-binding:t -*-
 
-;; Copyright (C) 1990-1991, 1993-1994, 2001-2022 Free Software
-;; Foundation, Inc.
+;; Copyright (C) 1990-2022 Free Software Foundation, Inc.
 
 ;; Author: Robert J. Chassell <bob@gnu.org>
 ;; (according to ack.texi)
@@ -25,20 +24,20 @@
 ;;; Commentary:
 
 ;; You may use these commands to handle an address list or other
-;; small data base.
+;; small database.
 
 
 ;;; Summary
 
 ;; The current page commands are:
 
-;;     forward-page         C-x ]
-;;     backward-page        C-x [
-;;     narrow-to-page       C-x p
-;;     count-lines-page     C-x l
-;;     mark-page            C-x C-p  (change this to C-x C-p C-m)
-;;     sort-pages           not bound
-;;     what-page            not bound
+;;     `forward-page'         C-x ]
+;;     `backward-page'        C-x [
+;;     `narrow-to-page'       C-x p
+;;     `count-lines-page'     C-x l
+;;     `mark-page'            C-x C-p  (change this to C-x C-p C-m)
+;;     `sort-pages'           not bound
+;;     `what-page'            not bound
 
 ;; The new page handling commands all use `C-x C-p' as a prefix.  This
 ;; means that the key binding for `mark-page' must be changed.
@@ -47,15 +46,15 @@
 
 ;; New page handling commands:
 
-;;     pages-next-page                  C-x C-p C-n
-;;     pages-previous-page              C-x C-p C-p
-;;     pages-search                     C-x C-p C-s
-;;     pages-add-new-page               C-x C-p C-a
-;;     pages-sort-buffer                C-x C-p s
-;;     pages-set-delimiter              C-x C-p C-l
-;;     pages-directory                  C-x C-p C-d
-;;     pages-directory-for-addresses    C-x C-p d
-;;        pages-directory-goto          C-c C-c
+;;     `pages-next-page'                  C-x C-p C-n
+;;     `pages-previous-page'              C-x C-p C-p
+;;     `pages-search'                     C-x C-p C-s
+;;     `pages-add-new-page'               C-x C-p C-a
+;;     `pages-sort-buffer'                C-x C-p s
+;;     `pages-set-delimiter'              C-x C-p C-l
+;;     `pages-directory'                  C-x C-p C-d
+;;     `pages-directory-for-addresses'    C-x C-p d
+;;        `pages-directory-goto'          C-c C-c
 
 
 ;;; Using the page commands
@@ -103,8 +102,8 @@
 ;;
 ;;   2. The first line of text in each entry is the `heading line'; it
 ;;      will appear in the pages-directory-buffer which is constructed
-;;      using the `C-x C-p C-d' (pages-directory) command or the `C-x
-;;      C-p d' (pages-directory-for-addresses) command.
+;;      using the `C-x C-p C-d' (`pages-directory') command or the
+;;      `C-x C-p d' (`pages-directory-for-addresses') command.
 ;;
 ;;      The heading line may be on the same line as the page-delimiter
 ;;      or it may follow after.  It is the first non-blank line on the
@@ -163,17 +162,18 @@
 ;; `pages-previous-page', `pages-add-new-page', `mark-page', and `pages-search'
 ;; commands.
 
-;; You may use either the `C-x C-p d' (pages-directory-for-addresses)
-;; or the `C-x C-p C-d' (pages-directory) command to construct and
+;; You may use either the `C-x C-p d' (`pages-directory-for-addresses')
+;; or the `C-x C-p C-d' (`pages-directory') command to construct and
 ;; display a directory of all the heading lines.
 
 ;; In the directory, you may position the cursor over a heading line
-;; and type `C-c C-c' (pages-directory-goto) to go to the entry to
+;; and type `C-c C-c' (`pages-directory-goto') to go to the entry to
 ;; which it refers in the pages buffer.
 
-;; You can type `C-c C-p C-a' (pages-add-new-page) to add a new entry in the
-;; pages buffer or address file.  This is the same command you use to
-;; add a new entry when you are in the pages buffer or address file.
+;; You can type `C-c C-p C-a' (`pages-add-new-page') to add a new
+;; entry in the pages buffer or address file.  This is the same
+;; command you use to add a new entry when you are in the pages buffer
+;; or address file.
 
 ;; If you wish, you may create several different directories,
 ;; one for each different buffer.
diff --git a/lisp/textmodes/paragraphs.el b/lisp/textmodes/paragraphs.el
index cd726ad477..c500dc014f 100644
--- a/lisp/textmodes/paragraphs.el
+++ b/lisp/textmodes/paragraphs.el
@@ -514,9 +514,9 @@ Second and third arg START and END specify the region to 
operate on.
 If optional argument NO-QUERY is non-nil, make changes without asking
 for confirmation.  You can use `repunctuate-sentences-filter' to add
 filters to skip occurrences of spaces that don't need to be replaced."
-  (interactive (list nil
-                     (if (use-region-p) (region-beginning))
-                     (if (use-region-p) (region-end))))
+  (declare (interactive-args (start (use-region-beginning))
+                             (end (use-region-end))))
+  (interactive (list nil (use-region-beginning) (use-region-end)))
   (let ((regexp "\\([]\"')]?\\)\\([.?!]\\)\\([]\"')]?\\) +")
         (to-string "\\1\\2\\3  "))
     (if no-query
diff --git a/lisp/textmodes/picture.el b/lisp/textmodes/picture.el
index e8c1e6b14f..ab211fdd7b 100644
--- a/lisp/textmodes/picture.el
+++ b/lisp/textmodes/picture.el
@@ -1,6 +1,6 @@
 ;;; picture.el --- "Picture mode" -- editing using quarter-plane screen model 
-*- lexical-binding: t -*-
 
-;; Copyright (C) 1985, 1994, 2001-2022 Free Software Foundation, Inc.
+;; Copyright (C) 1985-2022 Free Software Foundation, Inc.
 
 ;; Author: K. Shane Hartman
 ;; Maintainer: emacs-devel@gnu.org
@@ -23,9 +23,9 @@
 
 ;;; Commentary:
 
-;; This code provides the picture-mode commands documented in the Emacs
+;; This code provides the `picture-mode' commands documented in the Emacs
 ;; manual.  The screen is treated as a semi-infinite quarter-plane with
-;; support for rectangle operations and `etch-a-sketch' character
+;; support for rectangle operations and "etch-a-sketch" character
 ;; insertion in any of eight directions.
 
 ;;; Code:
@@ -96,7 +96,7 @@ If scan reaches end of buffer, stop there without error."
 
 (defun picture-forward-column (arg &optional interactive)
   "Move cursor right, making whitespace if necessary.
-With argument, move that many columns."
+With prefix argument ARG, move that many columns."
   (interactive "^p\nd")
   (let (deactivate-mark)
     (picture-update-desired-column interactive)
@@ -110,14 +110,14 @@ With argument, move that many columns."
 
 (defun picture-backward-column (arg &optional interactive)
   "Move cursor left, making whitespace if necessary.
-With argument, move that many columns."
+With prefix argument ARG, move that many columns."
   (interactive "^p\nd")
   (picture-update-desired-column interactive)
   (picture-forward-column (- arg)))
 
 (defun picture-move-down (arg)
   "Move vertically down, making whitespace if necessary.
-With argument, move that many lines."
+With prefix argument ARG, move that many lines."
   (interactive "^p")
   (let (deactivate-mark)
     (picture-update-desired-column nil)
@@ -134,7 +134,7 @@ With argument, move that many lines."
 
 (defun picture-move-up (arg)
   "Move vertically up, making whitespace if necessary.
-With argument, move that many lines."
+With prefix argument ARG, move that many lines."
   (interactive "^p")
   (picture-update-desired-column nil)
   (picture-move-down (- arg)))
@@ -161,36 +161,36 @@ With argument, move that many lines."
 
 (defun picture-movement-nw (&optional arg)
   "Move up and left after self-inserting character in Picture mode.
-With prefix argument, move up and two-column left."
+With prefix argument ARG, move up and two-column left."
   (interactive "P")
   (picture-set-motion -1 (if arg -2 -1)))
 
 (defun picture-movement-ne (&optional arg)
   "Move up and right after self-inserting character in Picture mode.
-With prefix argument, move up and two-column right."
+With prefix argument ARG, move up and two-column right."
   (interactive "P")
   (picture-set-motion -1 (if arg 2 1)))
 
 (defun picture-movement-sw (&optional arg)
   "Move down and left after self-inserting character in Picture mode.
-With prefix argument, move down and two-column left."
+With prefix argument ARG, move down and two-column left."
   (interactive "P")
   (picture-set-motion 1 (if arg -2 -1)))
 
 (defun picture-movement-se (&optional arg)
   "Move down and right after self-inserting character in Picture mode.
-With prefix argument, move down and two-column right."
+With prefix argument ARG, move down and two-column right."
   (interactive "P")
   (picture-set-motion 1 (if arg 2 1)))
 
-(defun picture-set-motion (vert horiz)
+(defun picture-set-motion (vertical horizontal)
   "Set VERTICAL and HORIZONTAL increments for movement in Picture mode.
 The mode line is updated to reflect the current direction."
-  (setq picture-vertical-step vert
-       picture-horizontal-step horiz)
+  (setq picture-vertical-step vertical
+        picture-horizontal-step horizontal)
   (setq mode-name
        (format "Picture:%s"
-               (nth (+ 2 (% horiz 3) (* 5 (1+ (% vert 2))))
+                (nth (+ 2 (% horizontal 3) (* 5 (1+ (% vertical 2))))
                     '(wnw nw up ne ene Left left none right Right
                           wsw sw down se ese))))
   (force-mode-line-update)
@@ -305,9 +305,9 @@ Use \"\\[command-apropos] picture-movement\" to see those 
commands."
 
 (defun picture-clear-line (arg)
   "Clear out rest of line; if at end of line, advance to next line.
-Cleared-out line text goes into the kill ring, as do newlines that are
-advanced over.  With argument, clear out (and save in kill ring) that
-many lines."
+Cleared-out line text goes into the kill ring, as do newlines
+that are advanced over.  With prefix argument ARG, clear out (and
+save in kill ring) that many lines."
   (interactive "P")
   (if arg
       (progn
@@ -320,7 +320,8 @@ many lines."
 
 (defun picture-newline (arg)
   "Move to the beginning of the following line.
-With argument, moves that many lines (up, if negative argument);
+With prefix argument ARG, move that many lines (up, if negative
+argument);
 always moves to the beginning of a line."
   (interactive "^p")
   (let ((start (point))
@@ -466,8 +467,11 @@ If no such character is found, move to beginning of line."
 
 (defun picture-tab (&optional arg)
   "Tab transparently (just move point) to next tab stop.
-With prefix arg, overwrite the traversed text with spaces.  The tab stop
-list can be changed by \\[picture-set-tab-stops] and \\[edit-tab-stops].
+With prefix argument ARG, overwrite the traversed text with
+spaces.  The tab stop list can be changed by \
+\\<picture-mode-map>\\[picture-set-tab-stops] and
+\\[edit-tab-stops].
+
 See also documentation for variable `picture-tab-chars'."
   (interactive "^P")
   (let* ((opoint (point)))
@@ -515,10 +519,11 @@ Interactively, reads the register using 
`register-read-with-preview'."
 
 (defun picture-yank-rectangle (&optional insertp)
   "Overlay rectangle saved by \\[picture-clear-rectangle].
-The rectangle is positioned with upper left corner at point, overwriting
-existing text.  With prefix argument, the rectangle is inserted instead,
-shifting existing text.  Leaves mark at one corner of rectangle and
-point at the other (diagonally opposed) corner."
+The rectangle is positioned with upper left corner at point,
+overwriting existing text.  With prefix argument INSERTP, the
+rectangle is inserted instead, shifting existing text.  Leave
+mark at one corner of rectangle and point at the
+other (diagonally opposed) corner."
   (interactive "P")
   (if (not (consp picture-killed-rectangle))
       (error "No rectangle saved")
@@ -536,10 +541,11 @@ regardless of where you click."
 
 (defun picture-yank-rectangle-from-register (register &optional insertp)
   "Overlay rectangle saved in REGISTER.
-The rectangle is positioned with upper left corner at point, overwriting
-existing text.  With prefix argument, the rectangle is
-inserted instead, shifting existing text.  Leaves mark at one corner
-of rectangle and point at the other (diagonally opposed) corner.
+The rectangle is positioned with upper left corner at point,
+overwriting existing text.  With prefix argument INSERTP, the
+rectangle is inserted instead, shifting existing text.  Leave
+mark at one corner of rectangle and point at the
+other (diagonally opposed) corner.
 
 Interactively, reads the register using `register-read-with-preview'."
   (interactive (list (register-read-with-preview "Rectangle from register: ")
@@ -552,7 +558,7 @@ Interactively, reads the register using 
`register-read-with-preview'."
 (defun picture-insert-rectangle (rectangle &optional insertp)
   "Overlay RECTANGLE with upper left corner at point.
 Optional argument INSERTP, if non-nil causes RECTANGLE to be inserted.
-Leaves the region surrounding the rectangle."
+Leave the region surrounding the rectangle."
   (let ((indent-tabs-mode nil))
     (if (not insertp)
        (save-excursion
@@ -570,7 +576,7 @@ Leaves the region surrounding the rectangle."
      (if (= (current-column) 0) 1 0)))
 
 (defun picture-draw-rectangle (start end)
-  "Draw a rectangle around region."
+  "Draw a rectangle around region from START to END."
   (interactive "*r")                    ; start will be less than end
   (let* ((sl     (picture-current-line))
          (sc     (current-column))
@@ -615,61 +621,57 @@ Leaves the region surrounding the rectangle."
 
 (defalias 'picture-delete-char 'delete-char)
 
-(defvar picture-mode-map
-  (let ((map (make-keymap)))
-    (define-key map [remap self-insert-command] 'picture-self-insert)
-    (define-key map [remap completion-separator-self-insert-command]
-                         'picture-self-insert)
-    (define-key map [remap completion-separator-self-insert-autofilling]
-                         'picture-self-insert)
-    (define-key map [remap forward-char] 'picture-forward-column)
-    (define-key map [remap right-char] 'picture-forward-column)
-    (define-key map [remap backward-char] 'picture-backward-column)
-    (define-key map [remap left-char] 'picture-backward-column)
-    (define-key map [remap delete-char] 'picture-clear-column)
-      ;; There are two possibilities for what is normally on DEL.
-    (define-key map [remap backward-delete-char-untabify]
-      'picture-backward-clear-column)
-    (define-key map [remap delete-backward-char] 
'picture-backward-clear-column)
-    (define-key map [remap kill-line] 'picture-clear-line)
-    (define-key map [remap open-line] 'picture-open-line)
-    (define-key map [remap newline] 'picture-newline)
-    (define-key map [remap newline-and-indent] 'picture-duplicate-line)
-    (define-key map [remap next-line] 'picture-move-down)
-    (define-key map [remap previous-line] 'picture-move-up)
-    (define-key map [remap move-beginning-of-line] 'picture-beginning-of-line)
-    (define-key map [remap move-end-of-line] 'picture-end-of-line)
-    (define-key map [remap mouse-set-point] 'picture-mouse-set-point)
-    (define-key map "\C-c\C-d" 'picture-delete-char)
-    (define-key map "\t" 'picture-tab)
-    (define-key map "\e\t" 'picture-tab-search)
-    (define-key map "\C-c\t" 'picture-set-tab-stops)
-    (define-key map "\C-c\C-k" 'picture-clear-rectangle)
-    (define-key map "\C-c\C-w" 'picture-clear-rectangle-to-register)
-    (define-key map "\C-c\C-y" 'picture-yank-rectangle)
-    (define-key map "\C-c\C-x" 'picture-yank-rectangle-from-register)
-    (define-key map "\C-c\C-r" 'picture-draw-rectangle)
-    (define-key map "\C-c\C-c" 'picture-mode-exit)
-    (define-key map "\C-c\C-f" 'picture-motion)
-    (define-key map "\C-c\C-b" 'picture-motion-reverse)
-    (define-key map "\C-c<" 'picture-movement-left)
-    (define-key map "\C-c>" 'picture-movement-right)
-    (define-key map "\C-c^" 'picture-movement-up)
-    (define-key map "\C-c." 'picture-movement-down)
-    (define-key map "\C-c`" 'picture-movement-nw)
-    (define-key map "\C-c'" 'picture-movement-ne)
-    (define-key map "\C-c/" 'picture-movement-sw)
-    (define-key map "\C-c\\" 'picture-movement-se)
-    (define-key map [(control ?c) left]  'picture-movement-left)
-    (define-key map [(control ?c) right] 'picture-movement-right)
-    (define-key map [(control ?c) up]    'picture-movement-up)
-    (define-key map [(control ?c) down]  'picture-movement-down)
-    (define-key map [(control ?c) home]  'picture-movement-nw)
-    (define-key map [(control ?c) prior] 'picture-movement-ne)
-    (define-key map [(control ?c) end]   'picture-movement-sw)
-    (define-key map [(control ?c) next]  'picture-movement-se)
-    map)
-  "Keymap used in `picture-mode'.")
+(defvar-keymap picture-mode-map
+  :doc "Keymap used in `picture-mode'."
+  :full t
+  "<remap> <self-insert-command>"           #'picture-self-insert
+  "<remap> <completion-separator-self-insert-command>" #'picture-self-insert
+  "<remap> <completion-separator-self-insert-autofilling>" 
#'picture-self-insert
+  "<remap> <forward-char>"                  #'picture-forward-column
+  "<remap> <right-char>"                    #'picture-forward-column
+  "<remap> <backward-char>"                 #'picture-backward-column
+  "<remap> <left-char>"                     #'picture-backward-column
+  "<remap> <delete-char>"                   #'picture-clear-column
+  ;; There are two possibilities for what is normally on DEL.
+  "<remap> <backward-delete-char-untabify>" #'picture-backward-clear-column
+  "<remap> <delete-backward-char>"          #'picture-backward-clear-column
+  "<remap> <kill-line>"                     #'picture-clear-line
+  "<remap> <open-line>"                     #'picture-open-line
+  "<remap> <newline>"                       #'picture-newline
+  "<remap> <newline-and-indent>"            #'picture-duplicate-line
+  "<remap> <next-line>"                     #'picture-move-down
+  "<remap> <previous-line>"                 #'picture-move-up
+  "<remap> <move-beginning-of-line>"        #'picture-beginning-of-line
+  "<remap> <move-end-of-line>"              #'picture-end-of-line
+  "<remap> <mouse-set-point>"               #'picture-mouse-set-point
+  "C-c C-d"     #'picture-delete-char
+  "TAB"         #'picture-tab
+  "M-TAB"       #'picture-tab-search
+  "C-c TAB"     #'picture-set-tab-stops
+  "C-c C-k"     #'picture-clear-rectangle
+  "C-c C-w"     #'picture-clear-rectangle-to-register
+  "C-c C-y"     #'picture-yank-rectangle
+  "C-c C-x"     #'picture-yank-rectangle-from-register
+  "C-c C-r"     #'picture-draw-rectangle
+  "C-c C-c"     #'picture-mode-exit
+  "C-c C-f"     #'picture-motion
+  "C-c C-b"     #'picture-motion-reverse
+  "C-c <"       #'picture-movement-left
+  "C-c >"       #'picture-movement-right
+  "C-c ^"       #'picture-movement-up
+  "C-c ."       #'picture-movement-down
+  "C-c `"       #'picture-movement-nw
+  "C-c '"       #'picture-movement-ne
+  "C-c /"       #'picture-movement-sw
+  "C-c \\"      #'picture-movement-se
+  "C-c <left>"  #'picture-movement-left
+  "C-c <right>" #'picture-movement-right
+  "C-c <up>"    #'picture-movement-up
+  "C-c <down>"  #'picture-movement-down
+  "C-c <home>"  #'picture-movement-nw
+  "C-c <prior>" #'picture-movement-ne
+  "C-c <end>"   #'picture-movement-sw
+  "C-c <next>"  #'picture-movement-se)
 
 (defcustom picture-mode-hook nil
   "If non-nil, its value is called on entry to Picture mode.
diff --git a/lisp/textmodes/reftex-global.el b/lisp/textmodes/reftex-global.el
index 062cea9c50..9f308646fc 100644
--- a/lisp/textmodes/reftex-global.el
+++ b/lisp/textmodes/reftex-global.el
@@ -276,7 +276,18 @@ one with the `xr' package."
   ;; to ignore the problematic string.
   ;; If TEST is nil, it is ignored without query.
   ;; Return the number of replacements.
-  (let ((n 0) file label match-data buf macro pos cell)
+  (let ((n 0)
+        (opt-re (concat "\\(?:{[^}{]*"
+                        "\\(?:{[^}{]*"
+                        "\\(?:{[^}{]*}[^}{]*\\)*"
+                        "}[^}{]*\\)*"
+                        "}[^][]*\\)*"))
+        (man-re (concat "\\(?:{[^}{]*"
+                        "\\(?:{[^}{]*"
+                        "\\(?:{[^}{]*}[^}{]*\\)*"
+                        "}[^}{]*\\)*"
+                        "}[^}{]*\\)*"))
+        file label match-data buf macro pos cell)
     (while (setq file (pop files))
       (setq buf (reftex-get-file-buffer-force file))
       (unless buf
@@ -301,7 +312,29 @@ one with the `xr' package."
                              (looking-at "\\\\ref[a-zA-Z]*[^a-zA-Z]")
                              (looking-at (format
                                           reftex-find-label-regexp-format
-                                          (regexp-quote label)))))
+                                          (regexp-quote label)))
+                             ;; In case the label-keyval is inside an
+                             ;; optional argument to \begin{env}
+                             (looking-at (concat
+                                          "\\\\begin[[:space:]]*{[^}]+}"
+                                          "[[:space:]]*"
+                                          "\\[[^][]*"
+                                          opt-re
+                                          (format
+                                           reftex-find-label-regexp-format
+                                           (regexp-quote label))
+                                          "[^]]*\\]"))
+                             ;; In case the label-keyval is inside the
+                             ;; first mandatory argument to \begin{env}
+                             (looking-at (concat
+                                          "\\\\begin[[:space:]]*{[^}]+}"
+                                          "[[:space:]]*"
+                                          "{[^}{]*"
+                                          man-re
+                                          (format
+                                           reftex-find-label-regexp-format
+                                           (regexp-quote label))
+                                          "[^}]*}"))))
                 ;; OK, we should replace it.
                 (set-match-data match-data)
                 (cond
diff --git a/lisp/textmodes/remember.el b/lisp/textmodes/remember.el
index c7a9f20ea2..f8c7af2500 100644
--- a/lisp/textmodes/remember.el
+++ b/lisp/textmodes/remember.el
@@ -548,13 +548,11 @@ If this is nil, then `diary-file' will be used instead."
 
 ;;; Internal Functions:
 
-(defvar remember-mode-map
-  (let ((map (make-sparse-keymap)))
-    (define-key map "\C-x\C-s" #'remember-finalize)
-    (define-key map "\C-c\C-c" #'remember-finalize)
-    (define-key map "\C-c\C-k" #'remember-destroy)
-    map)
-  "Keymap used in `remember-mode'.")
+(defvar-keymap remember-mode-map
+  :doc "Keymap used in `remember-mode'."
+  "C-x C-s" #'remember-finalize
+  "C-c C-c" #'remember-finalize
+  "C-c C-k" #'remember-destroy)
 
 (define-derived-mode remember-mode text-mode "Remember"
   "Major mode for output from \\[remember].
@@ -596,11 +594,9 @@ If this is nil, use `initial-major-mode'."
 
 
 
-(defvar remember-notes-mode-map
-  (let ((map (make-sparse-keymap)))
-    (define-key map "\C-c\C-c" #'remember-notes-save-and-bury-buffer)
-    map)
-  "Keymap used in `remember-notes-mode'.")
+(defvar-keymap remember-notes-mode-map
+  :doc "Keymap used in `remember-notes-mode'."
+  "C-c C-c" #'remember-notes-save-and-bury-buffer)
 
 (define-minor-mode remember-notes-mode
   "Minor mode for the `remember-notes' buffer.
diff --git a/lisp/textmodes/rst.el b/lisp/textmodes/rst.el
index c0d4dc68af..7fe46b9628 100644
--- a/lisp/textmodes/rst.el
+++ b/lisp/textmodes/rst.el
@@ -3634,19 +3634,6 @@ Region is from BEG to END.  With WITH-EMPTY prefix empty 
lines too."
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
-(dolist (var '(rst-level-face-max rst-level-face-base-color
-                                 rst-level-face-base-light
-                                 rst-level-face-format-light
-                                 rst-level-face-step-light
-                                 rst-level-1-face
-                                 rst-level-2-face
-                                 rst-level-3-face
-                                 rst-level-4-face
-                                 rst-level-5-face
-                                 rst-level-6-face))
-  (make-obsolete-variable var "customize the faces `rst-level-*' instead."
-                         "24.3"))
-
 ;; Define faces for the first 6 levels. More levels are possible, however.
 (defface rst-level-1 '((((background light)) (:background "grey85"))
                       (((background dark)) (:background "grey15")))
diff --git a/lisp/textmodes/tex-mode.el b/lisp/textmodes/tex-mode.el
index e6c0f8c28c..6dc2865dd9 100644
--- a/lisp/textmodes/tex-mode.el
+++ b/lisp/textmodes/tex-mode.el
@@ -980,24 +980,23 @@ Inherits `shell-mode-map' with a few additions.")
                                  (save-excursion
                                    (beginning-of-line)
                                    (search-forward "%" search-end t))))))
-      (when (and slash (not comment))
-       (setq mode
-             (if (looking-at
-                  (concat
-                   (regexp-opt '("documentstyle" "documentclass"
-                                 "begin" "subsection" "section"
-                                 "part" "chapter" "newcommand"
-                                 "renewcommand" "RequirePackage")
-                               'words)
-                   "\\|NeedsTeXFormat{LaTeX"))
-                 (if (and (looking-at
-                           
"document\\(style\\|class\\)\\(\\[.*\\]\\)?{slides}")
-                          ;; SliTeX is almost never used any more nowadays.
-                          (tex-executable-exists-p slitex-run-command))
-                     #'slitex-mode
-                   #'latex-mode)
-               #'plain-tex-mode))))
-    mode))
+      (if (not (and slash (not comment)))
+         mode
+       (if (looking-at
+            (concat
+             (regexp-opt '("documentstyle" "documentclass"
+                           "begin" "subsection" "section"
+                           "part" "chapter" "newcommand"
+                           "renewcommand" "RequirePackage")
+                         'words)
+             "\\|NeedsTeXFormat{LaTeX"))
+           (if (and (looking-at
+                     "document\\(style\\|class\\)\\(\\[.*\\]\\)?{slides}")
+                    ;; SliTeX is almost never used any more nowadays.
+                    (tex-executable-exists-p slitex-run-command))
+               #'slitex-mode
+             #'latex-mode)
+         #'plain-tex-mode)))))
 
 ;; `tex-mode' plays two roles: it's the parent of several sub-modes
 ;; but it's also the function that chooses between those submodes.
@@ -1014,7 +1013,10 @@ such as if there are no commands in the file, the value 
of `tex-default-mode'
 says which mode to use."
   (tex-common-initialization))
 
-(advice-add 'tex-mode :around #'tex--redirect-to-submode)
+(advice-add 'tex-mode :around #'tex--redirect-to-submode
+            ;; Give it lower precedence than normal advice, so
+            ;; AUCTeX's advice takes precedence over it.
+            '((depth . 50)))
 (defvar tex-mode--recursing nil)
 (defun tex--redirect-to-submode (orig-fun)
   "Redirect to one of the submodes when called directly."
@@ -1026,26 +1028,21 @@ says which mode to use."
                  ;; We're called from one of the children already.
                  orig-fun
                (setq tex-mode--recursing t)
-               (tex--guess-mode)))))
+               (let ((mode (tex--guess-mode)))
+                 ;; `tex--guess-mode' really tries to guess the *type* of file,
+                 ;; so we still need to consult `major-mode-remap-alist'
+                 ;; to see which mode to use for that type.
+                 (alist-get mode major-mode-remap-alist mode))))))
 
 ;; The following three autoloaded aliases appear to conflict with
-;; AUCTeX.  However, even though AUCTeX uses the mixed case variants
-;; for all mode relevant variables and hooks, the invocation function
-;; and setting of `major-mode' themselves need to be lowercase for
-;; AUCTeX to provide a fully functional user-level replacement.  So
-;; these aliases should remain as they are, in particular since AUCTeX
-;; users are likely to use them.
-;; Note from Stef: I don't understand the above explanation, the only
-;; justification I can find to keep those confusing aliases is for those
-;; users who may have files annotated with -*- LaTeX -*- (e.g. because they
-;; received them from someone using AUCTeX).
-
-;;;###autoload
-(defalias 'TeX-mode #'tex-mode)
-;;;###autoload
-(defalias 'plain-TeX-mode #'plain-tex-mode)
-;;;###autoload
-(defalias 'LaTeX-mode #'latex-mode)
+;; AUCTeX.  We keep those confusing aliases for those users who may
+;; have files annotated with -*- LaTeX -*- (e.g. because they received
+;; them from someone using AUCTeX).
+;; FIXME: Turn them into autoloads so that AUCTeX can override them
+;; with it's own autoloads?  Or maybe rely on `major-mode-remap-alist'?
+;;;###autoload (defalias 'TeX-mode #'tex-mode)
+;;;###autoload (defalias 'plain-TeX-mode #'plain-tex-mode)
+;;;###autoload (defalias 'LaTeX-mode #'latex-mode)
 
 ;;;###autoload
 (define-derived-mode plain-tex-mode tex-mode "TeX"
@@ -1596,10 +1593,6 @@ Puts point on a blank line between them."
 ;;;; LaTeX completion.
 
 (defvar latex-complete-bibtex-cache nil)
-
-(define-obsolete-function-alias 'latex-string-prefix-p
-  #'string-prefix-p "24.3")
-
 (defvar bibtex-reference-key)
 (declare-function reftex-get-bibfile-list "reftex-cite.el" ())
 
@@ -2174,8 +2167,6 @@ IN can be either a string (with the same % escapes in it) 
indicating
 OUT describes the output file and is either a %-escaped string
   or nil to indicate that there is no output file.")
 
-(define-obsolete-function-alias 'tex-string-prefix-p #'string-prefix-p "24.3")
-
 (defun tex-guess-main-file (&optional all)
   "Find a likely `tex-main-file'.
 Looks for hints in other buffers in the same directory or in
diff --git a/lisp/time.el b/lisp/time.el
index e7066cae7a..247d715ab6 100644
--- a/lisp/time.el
+++ b/lisp/time.el
@@ -528,7 +528,15 @@ If the value is t instead of an alist, use the value of
 
 (defvar-keymap world-clock-mode-map
   "n" #'next-line
-  "p" #'previous-line)
+  "p" #'previous-line
+  "w" #'world-clock-copy-time-as-kill)
+
+(defun world-clock-copy-time-as-kill ()
+  "Copy current line into the kill ring."
+  (interactive nil world-clock-mode)
+  (when-let ((str (buffer-substring-no-properties (pos-bol) (pos-eol))))
+    (kill-new str)
+    (message str)))
 
 (define-derived-mode world-clock-mode special-mode "World clock"
   "Major mode for buffer that displays times in various time zones.
diff --git a/lisp/url/url-gw.el b/lisp/url/url-gw.el
index c4a41f56b3..e4d1ca72a0 100644
--- a/lisp/url/url-gw.el
+++ b/lisp/url/url-gw.el
@@ -28,8 +28,6 @@
 (require 'url-vars)
 (require 'url-parse)
 
-;; Fixme: support SSH explicitly or via a url-gateway-rlogin-program?
-
 (autoload 'socks-open-network-stream "socks")
 
 (defgroup url-gateway nil
@@ -51,17 +49,20 @@
   "What hostname to actually rlog into before doing a telnet."
   :type '(choice (const nil) string)
   :group 'url-gateway)
+(make-obsolete-variable 'url-gateway-rlogin-host nil "29.1")
 
 (defcustom url-gateway-rlogin-user-name nil
   "Username to log into the remote machine with when using rlogin."
   :type '(choice (const nil) string)
   :group 'url-gateway)
+(make-obsolete-variable 'url-gateway-rlogin-user-name nil "29.1")
 
 (defcustom url-gateway-rlogin-parameters '("telnet" "-8")
   "Parameters to `url-open-rlogin'.
 This list will be used as the parameter list given to rsh."
   :type '(repeat string)
   :group 'url-gateway)
+(make-obsolete-variable 'url-gateway-rlogin-parameters nil "29.1")
 
 (defcustom url-gateway-telnet-host nil
   "What hostname to actually login to before doing a telnet."
@@ -141,6 +142,7 @@ linked Emacs under SunOS 4.x."
 ;; Stolen from red gnus nntp.el
 (defun url-open-rlogin (name buffer host service)
   "Open a connection using rsh."
+  (declare (obsolete nil "29.1"))
   (if (not (stringp service))
       (setq service (int-to-string service)))
   (let ((proc (if url-gateway-rlogin-user-name
@@ -205,6 +207,9 @@ linked Emacs under SunOS 4.x."
        (delete-region (point) (point-max)))
       proc)))
 
+(defvar url-gw-rlogin-obsolete-warned-once nil)
+(make-obsolete-variable url-gw-rlogin-obsolete-warned-once nil "29.1")
+
 ;;;###autoload
 (defun url-open-stream (name buffer host service &optional gateway-method)
   "Open a stream to HOST, possibly via a gateway.
@@ -255,7 +260,11 @@ overriding the value of `url-gateway-method'."
                         ('telnet
                          (url-open-telnet name buffer host service))
                         ('rlogin
-                         (url-open-rlogin name buffer host service))
+                          (unless url-gw-rlogin-obsolete-warned-once
+                            (lwarn 'url :error "Setting `url-gateway-method' 
to `rlogin' is obsolete")
+                            (setq url-gw-rlogin-obsolete-warned-once t))
+                          (with-suppressed-warnings ((obsolete 
url-open-rlogin))
+                            (url-open-rlogin name buffer host service)))
                         (_
                          (error "Bad setting of url-gateway-method: %s"
                                 url-gateway-method))))))
diff --git a/lisp/url/url-handlers.el b/lisp/url/url-handlers.el
index 74f77cd238..cb115fceb2 100644
--- a/lisp/url/url-handlers.el
+++ b/lisp/url/url-handlers.el
@@ -302,11 +302,13 @@ accessible."
     filename))
 (put 'file-local-copy 'url-file-handlers #'url-file-local-copy)
 
-(defun url-insert (buffer &optional beg end)
+(defun url-insert (buffer &optional beg end inhibit-decode)
   "Insert the body of a URL object.
 BUFFER should be a complete URL buffer as returned by `url-retrieve'.
 If the headers specify a coding-system (and current buffer is multibyte),
-it is applied to the body before it is inserted.
+it is applied to the body before it is inserted.  If INHIBIT-DECODE is
+non-nil, don't do any coding system decoding even in multibyte buffers.
+
 Returns a list of the form (SIZE CHARSET), where SIZE is the size in bytes
 of the inserted text and CHARSET is the charset that was specified in the
 header, or nil if none was found.
@@ -318,7 +320,8 @@ They count bytes from the beginning of the body."
                      (buffer-substring (+ (point-min) beg)
                                        (if end (+ (point-min) end) 
(point-max)))
                   (buffer-string))))
-         (charset (if enable-multibyte-characters
+         (charset (if (and enable-multibyte-characters
+                           (not inhibit-decode))
                       (mail-content-type-get (mm-handle-type handle)
                                              'charset))))
     (mm-destroy-parts handle)
@@ -362,6 +365,16 @@ if it had been inserted from a file named URL."
     (url-insert-buffer-contents buffer url visit beg end replace)))
 (put 'insert-file-contents 'url-file-handlers #'url-insert-file-contents)
 
+;;;###autoload
+(defun url-insert-file-contents-literally (url)
+  "Insert the data retrieved from URL literally in the current buffer."
+  (let ((buffer (url-retrieve-synchronously url)))
+    (unless buffer
+      (signal 'file-error (list url "No Data")))
+    (url-insert buffer nil nil t)
+    (kill-buffer buffer)
+    nil))
+
 (defun url-file-name-completion (url _directory &optional _predicate)
   ;; Even if it's not implemented, it's not an error to ask for completion,
   ;; in case it's available (bug#14806).
diff --git a/lisp/url/url-misc.el b/lisp/url/url-misc.el
index 479f64c3e0..0c1f79a0c5 100644
--- a/lisp/url/url-misc.el
+++ b/lisp/url/url-misc.el
@@ -47,6 +47,9 @@
       (error "Malformed url: %s" (url-recreate-url url)))
     nil))
 
+(defvar url-misc-rlogin-obsolete-warned-once nil)
+(make-obsolete-variable url-misc-rlogin-obsolete-warned-once nil "29.1")
+
 (defun url-do-terminal-emulator (type server port user)
   (switch-to-buffer
    (apply
@@ -58,6 +61,9 @@
          (t (error "Unknown terminal emulator required: %s" type)))
     nil
     (cond ((eq type 'rlogin)
+           (unless url-misc-rlogin-obsolete-warned-once
+             (lwarn 'url :error "Method `rlogin' is obsolete")
+             (setq url-misc-rlogin-obsolete-warned-once t))
           (if user (list server "-l" user) (list server)))
          ((eq type 'telnet)
           (if port (list server port) (list server)))
@@ -74,7 +80,7 @@
   nil)
 
 ;;;###autoload
-(defalias 'url-rlogin 'url-generic-emulator-loader)
+(define-obsolete-function-alias 'url-rlogin #'url-generic-emulator-loader 
"29.1")
 ;;;###autoload
 (defalias 'url-telnet 'url-generic-emulator-loader)
 ;;;###autoload
diff --git a/lisp/url/url-parse.el b/lisp/url/url-parse.el
index 24b064773b..91f47d0325 100644
--- a/lisp/url/url-parse.el
+++ b/lisp/url/url-parse.el
@@ -96,17 +96,6 @@ If the specified port number is the default, return nil."
            (or file "/")
            (if frag (concat "#" frag)))))
 
-(defun url-recreate-url-attributes (urlobj)
-  "Recreate the attributes of an URL string from the parsed URLOBJ."
-  (declare (obsolete nil "24.3"))
-  (when (url-attributes urlobj)
-    (concat ";"
-           (mapconcat (lambda (x)
-                         (if (cdr x)
-                             (concat (car x) "=" (cdr x))
-                           (car x)))
-                       (url-attributes urlobj) ";"))))
-
 ;;;###autoload
 (defun url-generic-parse-url (url)
   "Return an URL-struct of the parts of URL.
diff --git a/lisp/url/url-vars.el b/lisp/url/url-vars.el
index 859a5c75ed..4cdca05554 100644
--- a/lisp/url/url-vars.el
+++ b/lisp/url/url-vars.el
@@ -350,13 +350,11 @@ Should be a symbol specifying how to get a connection 
from the local machine.
 
 Currently supported methods:
 `telnet': Run telnet in a subprocess to connect;
-`rlogin': Rlogin to another machine to connect;
 `socks': Connect through a socks server;
 `tls': Connect with TLS;
 `ssl': Connect with SSL (deprecated, use `tls' instead);
 `native': Connect directly."
   :type '(radio (const :tag "Telnet to gateway host" :value telnet)
-               (const :tag "Rlogin to gateway host" :value rlogin)
                (const :tag "Use SOCKS proxy" :value socks)
                (const :tag "Use SSL/TLS for all connections" :value tls)
                (const :tag "Use SSL for all connections (obsolete)" :value ssl)
diff --git a/lisp/url/url.el b/lisp/url/url.el
index d08ff04eda..b4ece5faeb 100644
--- a/lisp/url/url.el
+++ b/lisp/url/url.el
@@ -280,7 +280,9 @@ how long to wait for a response before giving up."
               ;; Querying over consumer internet in the US takes 100
               ;; ms, so split the difference.
               (accept-process-output nil 0.05)))
-        (unless (eq data-buffer proc-buffer)
+        ;; Kill the process buffer on redirects.
+        (when (and data-buffer
+                   (not (eq data-buffer proc-buffer)))
           (let (kill-buffer-query-functions)
             (kill-buffer proc-buffer)))))
     data-buffer))
diff --git a/lisp/vc/add-log.el b/lisp/vc/add-log.el
index d710578fff..e3c0e2ca06 100644
--- a/lisp/vc/add-log.el
+++ b/lisp/vc/add-log.el
@@ -208,8 +208,6 @@ a case simply use the directory containing the changed 
file."
   '((t (:inherit font-lock-comment-face)))
   "Face for highlighting acknowledgments."
   :version "21.1")
-(define-obsolete-face-alias 'change-log-acknowledgement
-  'change-log-acknowledgment "24.3")
 
 (defconst change-log-file-names-re "^\\( +\\|\t\\)\\* \\([^ ,:([\n]+\\)")
 (defconst change-log-start-entry-re "^\\sw.........[0-9:+ ]*")
@@ -808,7 +806,7 @@ if it were to exist."
 
 (defun add-log-find-changelog-buffer (changelog-file-name)
   "Find a ChangeLog buffer for CHANGELOG-FILE-NAME.
-Respect `add-log-use-pseudo-changelog', which see."
+Respect `add-log--pseudo-changelog-buffer-name', which see."
   (if (or (file-exists-p changelog-file-name)
           (not add-log-dont-create-changelog-file))
       (find-file-noselect changelog-file-name)
diff --git a/lisp/vc/diff-mode.el b/lisp/vc/diff-mode.el
index 6b30de3cb3..a9591c9d82 100644
--- a/lisp/vc/diff-mode.el
+++ b/lisp/vc/diff-mode.el
@@ -279,20 +279,21 @@ and hunk-based syntax highlighting otherwise as a 
fallback."
   :doc "Keymap for `diff-minor-mode'.  See also `diff-mode-shared-map'."
   (key-description diff-minor-mode-prefix) diff-mode-shared-map)
 
-(define-minor-mode diff-auto-refine-mode
-  "Toggle automatic diff hunk finer highlighting (Diff Auto Refine mode).
+(with-suppressed-warnings ((obsolete diff-auto-refine-mode))
+  (define-minor-mode diff-auto-refine-mode
+    "Toggle automatic diff hunk finer highlighting (Diff Auto Refine mode).
 
 Diff Auto Refine mode is a buffer-local minor mode used with
 `diff-mode'.  When enabled, Emacs automatically highlights
 changes in detail as the user visits hunks.  When transitioning
 from disabled to enabled, it tries to refine the current hunk, as
 well."
-  :group 'diff-mode :init-value nil :lighter nil ;; " Auto-Refine"
-  (if diff-auto-refine-mode
-      (progn
-        (customize-set-variable 'diff-refine 'navigation)
-        (condition-case-unless-debug nil (diff-refine-hunk) (error nil)))
-    (customize-set-variable 'diff-refine nil)))
+    :group 'diff-mode :init-value nil :lighter nil ;; " Auto-Refine"
+    (if diff-auto-refine-mode
+        (progn
+          (customize-set-variable 'diff-refine 'navigation)
+          (condition-case-unless-debug nil (diff-refine-hunk) (error nil)))
+      (customize-set-variable 'diff-refine nil))))
 (make-obsolete 'diff-auto-refine-mode "set `diff-refine' instead." "27.1")
 (make-obsolete-variable 'diff-auto-refine-mode
                         "set `diff-refine' instead." "27.1")
@@ -2336,10 +2337,22 @@ Call FUN with two args (BEG and END) for each hunk."
   (let ((inhibit-read-only t))
     (undo arg)))
 
+;;;###autoload
+(defcustom diff-add-log-use-relative-names nil
+  "Use relative file names when generating ChangeLog skeletons.
+The files will be relative to the root directory of the VC
+repository.  This option affects the behavior of
+`diff-add-log-current-defuns'."
+  :type 'boolean
+  :safe #'booleanp
+  :version "29.1")
+
 (defun diff-add-log-current-defuns ()
   "Return an alist of defun names for the current diff.
 The elements of the alist are of the form (FILE . (DEFUN...)),
-where DEFUN... is a list of function names found in FILE."
+where DEFUN... is a list of function names found in FILE.  If
+`diff-add-log-use-relative-names' is non-nil, file names in the alist
+are relative to the root directory of the VC repository."
   (save-excursion
     (goto-char (point-min))
     (let* ((defuns nil)
@@ -2373,7 +2386,12 @@ where DEFUN... is a list of function names found in 
FILE."
           ;; hunks (e.g., "diff --git ..." etc).
           (re-search-forward diff-hunk-header-re nil t)
         (setq hunk-end (save-excursion (diff-end-of-hunk)))
-        (pcase-let* ((filename (substring-no-properties (diff-find-file-name)))
+        (pcase-let* ((filename (substring-no-properties
+                                (if diff-add-log-use-relative-names
+                                    (file-relative-name
+                                     (diff-find-file-name)
+                                     (vc-root-dir))
+                                  (diff-find-file-name))))
                      (=lines 0)
                      (+lines 0)
                      (-lines 0)
@@ -2928,6 +2946,15 @@ hunk text is not found in the source file."
         (forward-line 1)))
     (nreverse props)))
 
+;;;###autoload
+(defun diff-vc-deduce-fileset ()
+  (let ((backend (vc-responsible-backend default-directory))
+        files)
+    (save-excursion
+      (goto-char (point-min))
+      (while (progn (diff-file-next) (not (eobp)))
+        (push (diff-find-file-name nil t) files)))
+    (list backend (nreverse files) nil nil 'patch)))
 
 (defun diff--filter-substring (str)
   (when diff-font-lock-prettify
diff --git a/lisp/vc/ediff-wind.el b/lisp/vc/ediff-wind.el
index d45e13ea72..bd2e9f1977 100644
--- a/lisp/vc/ediff-wind.el
+++ b/lisp/vc/ediff-wind.el
@@ -36,14 +36,6 @@
   :group 'ediff
   :group 'frames)
 
-
-;; Determine which window setup function to use based on current window system.
-(defun ediff-choose-window-setup-function-automatically ()
-  (declare (obsolete ediff-setup-windows-default "24.3"))
-  (if (display-graphic-p)
-      #'ediff-setup-windows-multiframe
-    #'ediff-setup-windows-plain))
-
 (defcustom ediff-window-setup-function #'ediff-setup-windows-default
   "Function called to set up windows.
 Ediff provides a choice of three functions:
diff --git a/lisp/vc/log-edit.el b/lisp/vc/log-edit.el
index e958673fea..4624ada417 100644
--- a/lisp/vc/log-edit.el
+++ b/lisp/vc/log-edit.el
@@ -325,6 +325,11 @@ automatically."
 (defface log-edit-summary '((t :inherit font-lock-function-name-face))
   "Face for the summary in `log-edit-mode' buffers.")
 
+(defface log-edit-headers-separator
+  '((t :height 0.1 :inverse-video t :extend t))
+  "Face for the separator line in `log-edit-mode' buffers."
+  :version "29.1")
+
 (defface log-edit-header '((t :inherit font-lock-keyword-face))
   "Face for the headers in `log-edit-mode' buffers.")
 
@@ -393,7 +398,7 @@ The first subexpression is the actual text of the field.")
          nil lax))
      ("^\n"
       (progn (goto-char (match-end 0)) (1+ (match-end 0))) nil
-      (0 '(face (:height 0.1 :inverse-video t :extend t)
+      (0 '(face log-edit-headers-separator
            display-line-numbers-disable t rear-nonsticky t))))
     (log-edit--match-first-line (0 'log-edit-summary))))
 
@@ -664,6 +669,19 @@ comment history, see `log-edit-comment-ring', and hides 
`log-edit-files-buf'."
       (indent-rigidly (point) (point-max)
                      (- log-edit-common-indent common)))))
 
+(defvar vc-patch-string)
+
+(autoload 'vc-diff-patch-string "vc")
+(defun log-edit-diff-patch ()
+  (vc-diff-patch-string vc-patch-string))
+
+(defvar vc-log-fileset)
+
+(defun log-edit-diff-fileset ()
+  "Display diffs for the files to be committed."
+  (interactive)
+  (vc-diff nil nil (list log-edit-vc-backend vc-log-fileset)))
+
 (defun log-edit-show-diff ()
   "Show the diff for the files to be committed."
   (interactive)
diff --git a/lisp/vc/pcvs-util.el b/lisp/vc/pcvs-util.el
index 89f8d26880..ddc3ea6e81 100644
--- a/lisp/vc/pcvs-util.el
+++ b/lisp/vc/pcvs-util.el
@@ -164,8 +164,6 @@ arguments.  If ARGS is not a list, no argument will be 
passed."
                          (if oneline (line-end-position) (point-max))))
     (file-error nil)))
 
-(define-obsolete-function-alias 'cvs-string-prefix-p #'string-prefix-p "24.3")
-
 ;;;;
 ;;;; file names
 ;;;;
diff --git a/lisp/vc/vc-bzr.el b/lisp/vc/vc-bzr.el
index f6b17d4ce0..bce7996712 100644
--- a/lisp/vc/vc-bzr.el
+++ b/lisp/vc/vc-bzr.el
@@ -339,7 +339,7 @@ in the repository root directory of FILE."
   "Value of `compilation-error-regexp-alist' in *vc-bzr* buffers.")
 
 ;; To be called via vc-pull from vc.el, which requires vc-dispatcher.
-(declare-function vc-exec-after "vc-dispatcher" (code))
+(declare-function vc-exec-after "vc-dispatcher" (code &optional success))
 (declare-function vc-set-async-update "vc-dispatcher" (process-buffer))
 (declare-function vc-compilation-mode "vc-dispatcher" (backend))
 
diff --git a/lisp/vc/vc-cvs.el b/lisp/vc/vc-cvs.el
index 52cc42791f..2dd3d416ac 100644
--- a/lisp/vc/vc-cvs.el
+++ b/lisp/vc/vc-cvs.el
@@ -545,7 +545,7 @@ Will fail unless you have administrative privileges on the 
repo."
 ;;;
 
 ;; Follows vc-cvs-command, which uses vc-do-command from vc-dispatcher.
-(declare-function vc-exec-after "vc-dispatcher" (code))
+(declare-function vc-exec-after "vc-dispatcher" (code &optional success))
 
 (defun vc-cvs-print-log (files buffer &optional _shortlog _start-revision 
limit)
   "Print commit log associated with FILES into specified BUFFER.
diff --git a/lisp/vc/vc-dir.el b/lisp/vc/vc-dir.el
index 068a66b25b..037de415e6 100644
--- a/lisp/vc/vc-dir.el
+++ b/lisp/vc/vc-dir.el
@@ -266,7 +266,7 @@ See `run-hooks'."
                  :enable (vc-find-backend-function vc-dir-backend 'push)
                  :help "Push the current branch's changes"))
     (define-key map [update]
-      '(menu-item "Update to Latest Version" vc-update
+      '(menu-item "Update to Latest Version" vc-pull
                  :help "Update the current fileset's files to their tip 
revisions"))
     (define-key map [revert]
       '(menu-item "Revert to Base Version" vc-revert
@@ -306,8 +306,8 @@ See `run-hooks'."
     (define-key map "=" #'vc-diff)        ;; C-x v =
     (define-key map "D" #'vc-root-diff)           ;; C-x v D
     (define-key map "i" #'vc-register)    ;; C-x v i
-    (define-key map "+" #'vc-update)      ;; C-x v +
-    ;; I'd prefer some kind of symmetry with vc-update:
+    (define-key map "+" #'vc-pull)        ;; C-x v +
+    ;; I'd prefer some kind of symmetry with vc-pull:
     (define-key map "P" #'vc-push)        ;; C-x v P
     (define-key map "l" #'vc-print-log)           ;; C-x v l
     (define-key map "L" #'vc-print-root-log) ;; C-x v L
@@ -356,10 +356,10 @@ See `run-hooks'."
     (define-key map "G" #'vc-dir-ignore)
 
     (let ((branch-map (make-sparse-keymap)))
-      (define-key map "B" branch-map)
-      (define-key branch-map "c" #'vc-create-tag)
+      (define-key map "b" branch-map)
+      (define-key branch-map "c" #'vc-create-branch)
       (define-key branch-map "l" #'vc-print-branch-log)
-      (define-key branch-map "s" #'vc-retrieve-tag))
+      (define-key branch-map "s" #'vc-switch-branch))
 
     (let ((mark-map (make-sparse-keymap)))
       (define-key map "*" mark-map)
diff --git a/lisp/vc/vc-dispatcher.el b/lisp/vc/vc-dispatcher.el
index e2a490092b..dc3ed52650 100644
--- a/lisp/vc/vc-dispatcher.el
+++ b/lisp/vc/vc-dispatcher.el
@@ -109,6 +109,8 @@
 ;; TODO:
 ;; - log buffers need font-locking.
 
+(eval-when-compile (require 'cl-lib))
+
 ;; General customization
 
 (defcustom vc-logentry-check-hook nil
@@ -194,7 +196,7 @@ Another is that undo information is not kept."
 
 (defvar vc-sentinel-movepoint)          ;Dynamically scoped.
 
-(defun vc--process-sentinel (p code)
+(defun vc--process-sentinel (p code &optional success)
   (let ((buf (process-buffer p)))
     ;; Impatient users sometime kill "slow" buffers; check liveness
     ;; to avoid "error in process sentinel: Selecting deleted buffer".
@@ -215,7 +217,7 @@ Another is that undo information is not kept."
             ;; each sentinel read&set process-mark, but since `cmd' needs
             ;; to work both for async and sync processes, this would be
             ;; difficult to achieve.
-            (vc-exec-after code)
+            (vc-exec-after code success)
             (move-marker m (point)))
           ;; But sometimes the sentinels really want to move point.
           (when vc-sentinel-movepoint
@@ -232,11 +234,14 @@ Another is that undo information is not kept."
                                 'help-echo
                                 "A command is in progress in this buffer"))))
 
-(defun vc-exec-after (code)
+(defun vc-exec-after (code &optional success)
   "Eval CODE when the current buffer's process is done.
 If the current buffer has no process, just evaluate CODE.
 Else, add CODE to the process' sentinel.
-CODE should be a function of no arguments."
+CODE should be a function of no arguments.
+
+If SUCCESS, it should be a process object.  Only run CODE if the
+SUCCESS process has a zero exit code."
   (let ((proc (get-buffer-process (current-buffer))))
     (cond
      ;; If there's no background process, just execute the code.
@@ -247,13 +252,15 @@ CODE should be a function of no arguments."
      ((or (null proc) (eq (process-status proc) 'exit))
       ;; Make sure we've read the process's output before going further.
       (when proc (accept-process-output proc))
-      (if (functionp code) (funcall code) (eval code t)))
+      (when (or (not success)
+                (zerop (process-exit-status success)))
+        (if (functionp code) (funcall code) (eval code t))))
      ;; If a process is running, add CODE to the sentinel
      ((eq (process-status proc) 'run)
       (vc-set-mode-line-busy-indicator)
       (letrec ((fun (lambda (p _msg)
                       (remove-function (process-sentinel p) fun)
-                      (vc--process-sentinel p code))))
+                      (vc--process-sentinel p code success))))
         (add-function :after (process-sentinel proc) fun)))
      (t (error "Unexpected process state"))))
   nil)
@@ -262,6 +269,13 @@ CODE should be a function of no arguments."
   (declare (indent 0) (debug (def-body)))
   `(vc-exec-after (lambda () ,@body)))
 
+(defvar vc-filter-command-function #'list
+  "Function called to transform VC commands before execution.
+The function is called inside the buffer in which the command
+will be run and is passed the COMMAND, FILE-OR-LIST and FLAGS
+arguments to `vc-do-command'.  It should return a list of three
+elements, the new values for these arguments.")
+
 (defvar vc-post-command-functions nil
   "Hook run at the end of `vc-do-command'.
 Each function is called inside the buffer in which the command was run
@@ -282,6 +296,23 @@ the man pages for \"torsocks\" for more details about Tor."
   :version "27.1"
   :group 'vc)
 
+(defun vc-user-edit-command (command file-or-list flags)
+  "Prompt the user to edit VC command COMMAND and FLAGS.
+Intended to be used as the value of `vc-filter-command-function'."
+  (let* ((files-separator-p (string= "--" (car (last flags))))
+         (edited (split-string-and-unquote
+                  (read-shell-command
+                   (format "Edit VC command & arguments%s: "
+                           (if file-or-list
+                               " (files list to be appended)"
+                             ""))
+                   (combine-and-quote-strings
+                    (cons command (remq nil (if files-separator-p
+                                                (butlast flags)
+                                              flags))))))))
+    (list (car edited) file-or-list
+          (nconc (cdr edited) (and files-separator-p '("--"))))))
+
 ;;;###autoload
 (defun vc-do-command (buffer okstatus command file-or-list &rest flags)
   "Execute a slave command, notifying user and checking for errors.
@@ -296,117 +327,142 @@ FILE-OR-LIST is the name of a working file; it may be a 
list of
 files or be nil (to execute commands that don't expect a file
 name or set of files).  If an optional list of FLAGS is present,
 that is inserted into the command line before the filename.
+
 Return the return value of the slave command in the synchronous
 case, and the process object in the asynchronous case."
-  ;; FIXME: file-relative-name can return a bogus result because
-  ;; it doesn't look at the actual file-system to see if symlinks
-  ;; come into play.
-  (let* ((files
-         (mapcar (lambda (f) (file-relative-name (expand-file-name f)))
-                 (if (listp file-or-list) file-or-list (list file-or-list))))
-        ;; Keep entire commands in *Messages* but avoid resizing the
-        ;; echo area.  Messages in this function are formatted in
-        ;; a such way that the important parts are at the beginning,
-        ;; due to potential truncation of long messages.
-        (message-truncate-lines t)
-        (full-command
-         (concat (if vc-tor "torsocks " "")
-                  (if (string= (substring command -1) "\n")
-                     (substring command 0 -1)
-                   command)
-                 " " (vc-delistify flags)
-                 " " (vc-delistify files)))
-        (vc-inhibit-message
-         (or (eq vc-command-messages 'log)
-             (eq (selected-window) (active-minibuffer-window)))))
+  (let (;; Keep entire commands in *Messages* but avoid resizing the
+       ;; echo area.  Messages in this function are formatted in
+       ;; a such way that the important parts are at the beginning,
+       ;; due to potential truncation of long messages.
+       (message-truncate-lines t)
+        (vc-inhibit-message
+        (or (eq vc-command-messages 'log)
+            (eq (selected-window) (active-minibuffer-window)))))
     (save-current-buffer
       (unless (or (eq buffer t)
                  (and (stringp buffer)
                       (string= (buffer-name) buffer))
                  (eq buffer (current-buffer)))
-       (vc-setup-buffer buffer))
-      ;; If there's some previous async process still running, just kill it.
-      (let ((squeezed (remq nil flags))
-           (inhibit-read-only t)
-           (status 0))
-       (when files
-         (setq squeezed (nconc squeezed files)))
-       (let (;; Since some functions need to parse the output
-             ;; from external commands, set LC_MESSAGES to C.
-             (process-environment (cons "LC_MESSAGES=C" process-environment))
-             (w32-quote-process-args t))
-         (if (eq okstatus 'async)
-             ;; Run asynchronously.
-             (let ((proc
-                    (let ((process-connection-type nil))
-                      (apply #'start-file-process command (current-buffer)
-                              command squeezed))))
-               (when vc-command-messages
-                 (let ((inhibit-message vc-inhibit-message))
-                   (message "Running in background: %s" full-command)))
-                ;; Get rid of the default message insertion, in case we don't
-                ;; set a sentinel explicitly.
-               (set-process-sentinel proc #'ignore)
-               (set-process-filter proc #'vc-process-filter)
-               (setq status proc)
-               (when vc-command-messages
-                 (vc-run-delayed
-                   (let ((message-truncate-lines t)
-                         (inhibit-message vc-inhibit-message))
-                     (message "Done in background: %s" full-command)))))
-           ;; Run synchronously
-           (when vc-command-messages
-             (let ((inhibit-message vc-inhibit-message))
-               (message "Running in foreground: %s" full-command)))
-           (let ((buffer-undo-list t))
-             (setq status (apply #'process-file command nil t nil squeezed)))
-           (when (and (not (eq t okstatus))
-                      (or (not (integerp status))
-                          (and okstatus (< okstatus status))))
-              (unless (eq ?\s (aref (buffer-name (current-buffer)) 0))
-                (pop-to-buffer (current-buffer))
-                (goto-char (point-min))
-                (shrink-window-if-larger-than-buffer))
-             (error "Failed (%s): %s"
-                    (if (integerp status) (format "status %d" status) status)
-                    full-command))
-           (when vc-command-messages
-             (let ((inhibit-message vc-inhibit-message))
-               (message "Done (status=%d): %s" status full-command)))))
-       (vc-run-delayed
-         (run-hook-with-args 'vc-post-command-functions
-                             command file-or-list flags))
-       status))))
+        (vc-setup-buffer buffer))
+      (cl-destructuring-bind (command file-or-list flags)
+          (funcall vc-filter-command-function command file-or-list flags)
+        (when vc-tor
+          (push command flags)
+          (setq command "torsocks"))
+        (let* (;; FIXME: file-relative-name can return a bogus result
+               ;; because it doesn't look at the actual file-system to
+               ;; see if symlinks come into play.
+               (files
+               (mapcar (lambda (f)
+                          (file-relative-name (expand-file-name f)))
+                       (if (listp file-or-list)
+                            file-or-list
+                          (list file-or-list))))
+              (full-command
+               (concat (if (string= (substring command -1) "\n")
+                           (substring command 0 -1)
+                         command)
+                       " " (vc-delistify flags)
+                       " " (vc-delistify files)))
+               (squeezed (remq nil flags))
+              (inhibit-read-only t)
+              (status 0))
+          ;; If there's some previous async process still running,
+          ;; just kill it.
+          (when files
+           (setq squeezed (nconc squeezed files)))
+         (let (;; Since some functions need to parse the output
+               ;; from external commands, set LC_MESSAGES to C.
+               (process-environment
+                 (cons "LC_MESSAGES=C" process-environment))
+               (w32-quote-process-args t))
+           (if (eq okstatus 'async)
+               ;; Run asynchronously.
+               (let ((proc
+                      (let ((process-connection-type nil))
+                        (apply #'start-file-process command
+                                (current-buffer) command squeezed))))
+                 (when vc-command-messages
+                   (let ((inhibit-message vc-inhibit-message))
+                     (message "Running in background: %s"
+                               full-command)))
+                  ;; Get rid of the default message insertion, in case
+                  ;; we don't set a sentinel explicitly.
+                 (set-process-sentinel proc #'ignore)
+                 (set-process-filter proc #'vc-process-filter)
+                 (setq status proc)
+                 (when vc-command-messages
+                   (vc-run-delayed
+                     (let ((message-truncate-lines t)
+                           (inhibit-message vc-inhibit-message))
+                       (message "Done in background: %s"
+                                 full-command)))))
+             ;; Run synchronously
+             (when vc-command-messages
+               (let ((inhibit-message vc-inhibit-message))
+                 (message "Running in foreground: %s" full-command)))
+             (let ((buffer-undo-list t))
+               (setq status (apply #'process-file
+                                    command nil t nil squeezed)))
+             (when (and (not (eq t okstatus))
+                        (or (not (integerp status))
+                            (and okstatus (< okstatus status))))
+                (unless (eq ?\s (aref (buffer-name (current-buffer)) 0))
+                  (pop-to-buffer (current-buffer))
+                  (goto-char (point-min))
+                  (shrink-window-if-larger-than-buffer))
+               (error "Failed (%s): %s"
+                      (if (integerp status)
+                           (format "status %d" status)
+                         status)
+                      full-command))
+             (when vc-command-messages
+               (let ((inhibit-message vc-inhibit-message))
+                 (message "Done (status=%d): %s"
+                           status full-command)))))
+         (vc-run-delayed
+           (run-hook-with-args 'vc-post-command-functions
+                               command file-or-list flags))
+         status)))))
+
+(defvar vc--inhibit-async-window nil)
 
 (defun vc-do-async-command (buffer root command &rest args)
   "Run COMMAND asynchronously with ARGS, displaying the result.
 Send the output to BUFFER, which should be a buffer or the name
 of a buffer, which is created.
 ROOT should be the directory in which the command should be run.
+The process object is returned.
 Display the buffer in some window, but don't select it."
-  (let* ((dir default-directory)
-        (inhibit-read-only t)
-        window new-window-start)
+  (let ((dir default-directory)
+       (inhibit-read-only t)
+        new-window-start proc)
     (setq buffer (get-buffer-create buffer))
     (if (get-buffer-process buffer)
        (error "Another VC action on %s is running" root))
     (with-current-buffer buffer
       (setq default-directory root)
-      (goto-char (point-max))
-      (unless (eq (point) (point-min))
-       (insert "\n"))
-      (setq new-window-start (point))
-      (insert "Running \"" command)
-      (dolist (arg args)
-       (insert " " arg))
-      (insert "\"...\n")
-      ;; Run in the original working directory.
-      (let ((default-directory dir))
-       (apply #'vc-do-command t 'async command nil args)))
-    (setq window (display-buffer buffer))
-    (if window
-       (set-window-start window new-window-start))
-    buffer))
+      (let* (;; Run in the original working directory.
+             (default-directory dir)
+             (orig-fun vc-filter-command-function)
+             (vc-filter-command-function
+              (lambda (&rest args)
+                (cl-destructuring-bind (&whole args cmd _ flags)
+                    (apply orig-fun args)
+                  (goto-char (point-max))
+                  (unless (eq (point) (point-min))
+                   (insert "\n"))
+                  (setq new-window-start (point))
+                  (insert "Running \"" cmd)
+                  (dolist (flag flags)
+                   (insert " " flag))
+                  (insert "\"...\n")
+                  args))))
+       (setq proc (apply #'vc-do-command t 'async command nil args))))
+    (unless vc--inhibit-async-window
+      (when-let ((window (display-buffer buffer)))
+        (set-window-start window new-window-start)))
+    proc))
 
 (defvar compilation-error-regexp-alist)
 
@@ -624,6 +680,8 @@ NOT-URGENT means it is ok to continue if the user says not 
to save."
 
 (declare-function log-edit-empty-buffer-p "log-edit" ())
 
+(defvar vc-patch-string)
+
 (defun vc-log-edit (fileset mode backend)
   "Set up `log-edit' for use on FILE."
   (setq default-directory
@@ -653,15 +711,17 @@ NOT-URGENT means it is ok to continue if the user says 
not to save."
                       (mapcar
                        (lambda (file) (file-relative-name file root))
                        fileset))))
-             (log-edit-diff-function . vc-diff)
+             (log-edit-diff-function
+               . ,(if vc-patch-string 'log-edit-diff-patch 
'log-edit-diff-fileset))
              (log-edit-vc-backend . ,backend)
-             (vc-log-fileset . ,fileset))
+             (vc-log-fileset . ,fileset)
+             (vc-patch-string . ,vc-patch-string))
            nil
            mode)
   (set-buffer-modified-p nil)
   (setq buffer-file-name nil))
 
-(defun vc-start-logentry (files comment initial-contents msg logbuf mode 
action &optional after-hook backend)
+(defun vc-start-logentry (files comment initial-contents msg logbuf mode 
action &optional after-hook backend patch-string)
   "Accept a comment for an operation on FILES.
 If COMMENT is nil, pop up a LOGBUF buffer, emit MSG, and set the
 action on close to ACTION.  If COMMENT is a string and
@@ -673,7 +733,8 @@ empty comment.  Remember the file's buffer in 
`vc-parent-buffer'
 \(current one if no file).  Puts the log-entry buffer in major mode
 MODE, defaulting to `log-edit-mode' if MODE is nil.
 AFTER-HOOK specifies the local value for `vc-log-after-operation-hook'.
-BACKEND, if non-nil, specifies a VC backend for the Log Edit buffer."
+BACKEND, if non-nil, specifies a VC backend for the Log Edit buffer.
+PATCH-STRING is a patch to check in."
   (let ((parent
          (if (vc-dispatcher-browsing)
              ;; If we are called from a directory browser, the parent buffer is
@@ -688,6 +749,8 @@ BACKEND, if non-nil, specifies a VC backend for the Log 
Edit buffer."
     (setq-local vc-parent-buffer parent)
     (setq-local vc-parent-buffer-name
                 (concat " from " (buffer-name vc-parent-buffer)))
+    (when patch-string
+      (setq-local vc-patch-string patch-string))
     (vc-log-edit files mode backend)
     (make-local-variable 'vc-log-after-operation-hook)
     (when after-hook
@@ -697,7 +760,11 @@ BACKEND, if non-nil, specifies a VC backend for the Log 
Edit buffer."
       (erase-buffer)
       (when (stringp comment) (insert comment)))
     (if (or (not comment) initial-contents)
-       (message "%s  Type C-c C-c when done" msg)
+        (message (substitute-command-keys
+                  (if (eq major-mode 'log-edit-mode)
+                      "%s  Type \\[log-edit-done] when done"
+                    "%s  Type \\`C-c C-c' when done"))
+                 msg)
       (vc-finish-logentry (eq comment t)))))
 
 ;; vc-finish-logentry is typically called from a log-edit buffer (see
@@ -753,7 +820,8 @@ the buffer contents as a comment."
 (defun vc-dispatcher-browsing ()
   "Are we in a directory browser buffer?"
   (or (derived-mode-p 'vc-dir-mode)
-      (derived-mode-p 'dired-mode)))
+      (derived-mode-p 'dired-mode)
+      (derived-mode-p 'diff-mode)))
 
 ;; These are unused.
 ;; (defun vc-dispatcher-in-fileset-p (fileset)
diff --git a/lisp/vc/vc-git.el b/lisp/vc/vc-git.el
index 46a486a46c..4a2a42ad2a 100644
--- a/lisp/vc/vc-git.el
+++ b/lisp/vc/vc-git.el
@@ -53,7 +53,8 @@
 ;; - responsible-p (file)                          OK
 ;; - receive-file (file rev)                       NOT NEEDED
 ;; - unregister (file)                             OK
-;; * checkin (files rev comment)                   OK
+;; * checkin (files comment rev)                   OK
+;; - checkin-patch (patch-string comment)          OK
 ;; * find-revision (file rev buffer)               OK
 ;; * checkout (file &optional rev)                 OK
 ;; * revert (file &optional contents-done)         OK
@@ -81,7 +82,7 @@
 ;; - annotate-time ()                              OK
 ;; - annotate-current-time ()                      NOT NEEDED
 ;; - annotate-extract-revision-at-line ()          OK
-;; TAG SYSTEM
+;; TAG/BRANCH SYSTEM
 ;; - create-tag (dir name branchp)                 OK
 ;; - retrieve-tag (dir name update)                OK
 ;; MISCELLANEOUS
@@ -127,6 +128,12 @@ If nil, use the value of `vc-annotate-switches'.  If t, 
use no switches."
                 (repeat :tag "Argument List" :value ("") string))
   :version "25.1")
 
+;; Check if local value of `vc-git-annotate-switches' is safe.
+;; Currently only "-w" (ignore whitespace) is considered safe, but
+;; this list might be extended in the future (probably most options
+;; are perfectly safe.)
+;;;###autoload(put 'vc-git-annotate-switches 'safe-local-variable (lambda 
(switches) (equal switches "-w")))
+
 (defcustom vc-git-log-switches nil
   "String or list of strings specifying switches for Git log under VC."
   :type '(choice (const :tag "None" nil)
@@ -617,7 +624,7 @@ or an empty string if none."
 
 ;; Follows vc-git-command (or vc-do-async-command), which uses vc-do-command
 ;; from vc-dispatcher.
-(declare-function vc-exec-after "vc-dispatcher" (code))
+(declare-function vc-exec-after "vc-dispatcher" (code &optional success))
 ;; Follows vc-exec-after.
 (declare-function vc-set-async-update "vc-dispatcher" (process-buffer))
 
@@ -857,6 +864,47 @@ The car of the list is the current branch."
 
 ;;; STATE-CHANGING FUNCTIONS
 
+(defcustom vc-git-log-edit-summary-target-len nil
+  "Target length for Git commit summary lines.
+If a number, characters in Summary: lines beyond this length are
+displayed in the `vc-git-log-edit-summary-target-warning' face.
+A value of any other type means no highlighting.
+
+By setting this to an integer around 50, you can improve the
+compatibility of your commit messages with Git commands that
+print the summary line in width-constrained contexts.  However,
+many commit summaries will need to exceed this length.
+
+See also `vc-git-log-edit-summary-max-len'."
+  :type '(choice (const :tag "No target" nil)
+                 (natnum :tag "Target length"))
+  :safe (lambda (x) (or (not x) (natnump x))))
+
+(defface vc-git-log-edit-summary-target-warning
+  '((t :inherit warning))
+  "Face for Git commit summary lines beyond the target length.
+See `vc-git-log-edit-summary-target-len'.")
+
+(defcustom vc-git-log-edit-summary-max-len 68
+  "Maximum length for Git commit summary lines.
+If a number, characters in summary lines beyond this length are
+displayed in the `vc-git-log-edit-summary-max-warning' face.
+A value of any other type means no highlighting.
+
+It is good practice to avoid writing summary lines longer than
+this because otherwise the summary line will be truncated in many
+contexts in which Git commands display summary lines.
+
+See also `vc-git-log-edit-summary-target-len'."
+  :type '(choice (const :tag "No target" nil)
+                 (natnum :tag "Target length"))
+  :safe (lambda (x) (or (not x) (natnump x))))
+
+(defface vc-git-log-edit-summary-max-warning
+  '((t :inherit error))
+  "Face for Git commit summary lines beyond the maximum length.
+See `vc-git-log-edit-summary-max-len'.")
+
 (defun vc-git-create-repo ()
   "Create a new Git repository."
   (vc-git-command nil 0 nil "init"))
@@ -910,9 +958,38 @@ If toggling on, also insert its message into the buffer."
   "C-c C-n" #'vc-git-log-edit-toggle-no-verify
   "C-c C-e" #'vc-git-log-edit-toggle-amend)
 
+(defun vc-git--log-edit-summary-check (limit)
+  (and (re-search-forward "^Summary: " limit t)
+       (when-let ((regex
+                   (cond ((and (natnump vc-git-log-edit-summary-max-len)
+                               (natnump vc-git-log-edit-summary-target-len))
+                          (format ".\\{,%d\\}\\(.\\{,%d\\}\\)\\(.*\\)"
+                                  vc-git-log-edit-summary-target-len
+                                  (- vc-git-log-edit-summary-max-len
+                                     vc-git-log-edit-summary-target-len)))
+                         ((natnump vc-git-log-edit-summary-max-len)
+                          (format ".\\{,%d\\}\\(?2:.*\\)"
+                                  vc-git-log-edit-summary-max-len))
+                         ((natnump vc-git-log-edit-summary-target-len)
+                          (format ".\\{,%d\\}\\(.*\\)"
+                                  vc-git-log-edit-summary-target-len)))))
+         (re-search-forward regex limit t))))
+
 (define-derived-mode vc-git-log-edit-mode log-edit-mode "Log-Edit/git"
   "Major mode for editing Git log messages.
-It is based on `log-edit-mode', and has Git-specific extensions.")
+It is based on `log-edit-mode', and has Git-specific extensions."
+  (setq-local
+   log-edit-font-lock-keywords
+   (append log-edit-font-lock-keywords
+           '((vc-git--log-edit-summary-check
+             (1 'vc-git-log-edit-summary-target-warning prepend t)
+              (2 'vc-git-log-edit-summary-max-warning prepend t))))))
+
+(defvar vc-git-patch-string nil)
+
+(defun vc-git-checkin-patch (patch-string comment)
+  (let ((vc-git-patch-string patch-string))
+    (vc-git-checkin nil comment)))
 
 (defun vc-git-checkin (files comment &optional _rev)
   (let* ((file1 (or (car files) default-directory))
@@ -936,12 +1013,45 @@ It is based on `log-edit-mode', and has Git-specific 
extensions.")
           (if (eq system-type 'windows-nt)
               (let ((default-directory (file-name-directory file1)))
                 (make-nearby-temp-file "git-msg")))))
+    (when vc-git-patch-string
+      (unless (zerop (vc-git-command nil t nil "diff" "--cached" "--quiet"))
+        ;; Check that all staged changes also exist in the patch.
+        ;; This is needed to allow adding/removing files that are
+        ;; currently staged to the index.  So remove the whole file diff
+        ;; from the patch because commit will take it from the index.
+        (with-temp-buffer
+          (vc-git-command (current-buffer) t nil "diff" "--cached")
+          (goto-char (point-min))
+          (let ((pos (point)) file-diff file-beg)
+            (while (not (eobp))
+              (forward-line 1) ; skip current "diff --git" line
+              (search-forward "diff --git" nil 'move)
+              (move-beginning-of-line 1)
+              (setq file-diff (buffer-substring pos (point)))
+              (if (and (setq file-beg (string-search
+                                       file-diff vc-git-patch-string))
+                       ;; Check that file diff ends with an empty string
+                       ;; or the beginning of the next file diff.
+                       (string-match-p "\\`\\'\\|\\`diff --git"
+                                       (substring
+                                        vc-git-patch-string
+                                        (+ file-beg (length file-diff)))))
+                  (setq vc-git-patch-string
+                        (string-replace file-diff "" vc-git-patch-string))
+                (user-error "Index not empty"))
+              (setq pos (point))))))
+      (let ((patch-file (make-temp-file "git-patch")))
+        (with-temp-file patch-file
+          (insert vc-git-patch-string))
+        (unwind-protect
+            (vc-git-command nil 0 patch-file "apply" "--cached")
+          (delete-file patch-file))))
     (cl-flet ((boolean-arg-fn
                (argument)
                (lambda (value) (when (equal value "yes") (list argument)))))
       ;; When operating on the whole tree, better pass "-a" than ".", since "."
       ;; fails when we're committing a merge.
-      (apply #'vc-git-command nil 0 (if only files)
+      (apply #'vc-git-command nil 0 (if (and only (not vc-git-patch-string)) 
files)
              (nconc (if msg-file (list "commit" "-F"
                                        (file-local-name msg-file))
                       (list "commit" "-m"))
@@ -959,7 +1069,8 @@ It is based on `log-edit-mode', and has Git-specific 
extensions.")
                           (write-region (car args) nil msg-file))
                         (setq args (cdr args)))
                       args)
-                   (if only (list "--only" "--") '("-a")))))
+                    (unless vc-git-patch-string
+                      (if only (list "--only" "--") '("-a"))))))
     (if (and msg-file (file-exists-p msg-file)) (delete-file msg-file))))
 
 (defun vc-git-find-revision (file rev buffer)
@@ -1006,31 +1117,37 @@ It is based on `log-edit-mode', and has Git-specific 
extensions.")
 (defun vc-git--pushpull (command prompt extra-args)
   "Run COMMAND (a string; either push or pull) on the current Git branch.
 If PROMPT is non-nil, prompt for the Git command to run."
+  (require 'vc-dispatcher)
   (let* ((root (vc-git-root default-directory))
         (buffer (format "*vc-git : %s*" (expand-file-name root)))
-        (git-program vc-git-program)
-        args)
-    ;; If necessary, prompt for the exact command.
-    ;; TODO if pushing, prompt if no default push location - cf bzr.
-    (when prompt
-      (setq args (split-string
-                 (read-shell-command
-                   (format "Git %s command: " command)
-                   (format "%s %s" git-program command)
-                   'vc-git-history)
-                 " " t))
-      (setq git-program (car  args)
-           command     (cadr args)
-           args        (cddr args)))
-    (setq args (nconc args extra-args))
-    (require 'vc-dispatcher)
-    (apply #'vc-do-async-command buffer root git-program command args)
+         (git-program vc-git-program)
+         ;; TODO if pushing, prompt if no default push location - cf bzr.
+         (vc-filter-command-function
+          (if prompt
+              (lambda (&rest args)
+                (cl-destructuring-bind (&whole args git _ flags)
+                    (apply #'vc-user-edit-command args)
+                  (setq git-program git
+                        command (car flags)
+                        extra-args (cdr flags))
+                  args))
+            vc-filter-command-function))
+         (proc (apply #'vc-do-async-command
+                      buffer root git-program command extra-args)))
+    ;; "git pull" includes progress output that uses ^M to move point
+    ;; to the beginning of the line.  Just translate these to newlines
+    ;; (but don't do anything with the CRLF sequence).
+    (add-function :around (process-filter proc)
+                  (lambda (filter process string)
+                    (funcall filter process
+                             (replace-regexp-in-string "\r\\(\\'\\|[^\n]\\)"
+                                                       "\n\\1" string))))
     (with-current-buffer buffer
       (vc-run-delayed
         (vc-compilation-mode 'git)
         (setq-local compile-command
                     (concat git-program " " command " "
-                            (mapconcat #'identity args " ")))
+                            (mapconcat #'identity extra-args " ")))
         (setq-local compilation-directory root)
         ;; Either set `compilation-buffer-name-function' locally to nil
         ;; or use `compilation-arguments' to set `name-function'.
@@ -1039,7 +1156,8 @@ If PROMPT is non-nil, prompt for the Git command to run."
                     (list compile-command nil
                           (lambda (_name-of-mode) buffer)
                           nil))))
-    (vc-set-async-update buffer)))
+    (vc-set-async-update buffer)
+    proc))
 
 (defun vc-git-pull (prompt)
   "Pull changes into the current Git branch.
@@ -1053,6 +1171,25 @@ Normally, this runs \"git push\".  If PROMPT is non-nil, 
prompt
 for the Git command to run."
   (vc-git--pushpull "push" prompt nil))
 
+(defun vc-git-pull-and-push (prompt)
+  "Pull changes into the current Git branch, and then push.
+The push will only be performed if the pull was successful.
+
+Normally, this runs \"git pull\".  If PROMPT is non-nil, prompt
+for the Git command to run."
+  (let ((proc (vc-git--pushpull "pull" prompt '("--stat"))))
+    (when (process-buffer proc)
+      (with-current-buffer (process-buffer proc)
+        (if (and (eq (process-status proc) 'exit)
+                 (zerop (process-exit-status proc)))
+            (let ((vc--inhibit-async-window t))
+              (vc-git-push nil))
+          (vc-exec-after
+           (lambda ()
+             (let ((vc--inhibit-async-window t))
+               (vc-git-push nil)))
+           proc))))))
+
 (defun vc-git-merge-branch ()
   "Merge changes into the current Git branch.
 This prompts for a branch to merge from."
@@ -1209,7 +1346,12 @@ If LIMIT is a revision string, use it as an 
end-revision."
 
 (defun vc-git-log-incoming (buffer remote-location)
   (vc-setup-buffer buffer)
-  (vc-git-command nil 0 nil "fetch")
+  (vc-git-command nil 0 nil "fetch"
+                  (unless (string= remote-location "")
+                    ;; `remote-location' is in format "repository/branch",
+                    ;; so remove everything except a repository name.
+                    (replace-regexp-in-string
+                     "/.*" "" remote-location)))
   (vc-git-command
    buffer 'async nil
    "log"
@@ -1482,13 +1624,25 @@ This requires git 1.8.4 or later, for the \"-L\" option 
of \"git log\"."
                    (expand-file-name fname (vc-git-root default-directory))))
          revision)))))
 
-;;; TAG SYSTEM
+;;; TAG/BRANCH SYSTEM
+
+(declare-function vc-read-revision "vc"
+                  (prompt &optional files backend default initial-input))
 
 (defun vc-git-create-tag (dir name branchp)
-  (let ((default-directory dir))
-    (and (vc-git-command nil 0 nil "update-index" "--refresh")
+  (let ((default-directory dir)
+        (start-point (when branchp (vc-read-revision
+                                    (format-prompt "Start point"
+                                                   (car (vc-git-branches)))
+                                    (list dir) 'Git))))
+    (and (or (zerop (vc-git-command nil t nil "update-index" "--refresh"))
+             (y-or-n-p "Modified files exist.  Proceed? ")
+             (user-error (format "Can't create %s with modified files"
+                                 (if branchp "branch" "tag"))))
          (if branchp
-             (vc-git-command nil 0 nil "checkout" "-b" name)
+             (vc-git-command nil 0 nil "checkout" "-b" name
+                             (when (and start-point (not (eq start-point "")))
+                               start-point))
            (vc-git-command nil 0 nil "tag" name)))))
 
 (defun vc-git-retrieve-tag (dir name _update)
diff --git a/lisp/vc/vc-hg.el b/lisp/vc/vc-hg.el
index f4a44df3c2..eed9592378 100644
--- a/lisp/vc/vc-hg.el
+++ b/lisp/vc/vc-hg.el
@@ -1345,7 +1345,7 @@ REV is the revision to check out into WORKFILE."
 
 ;; Follows vc-hg-command (or vc-do-async-command), which uses vc-do-command
 ;; from vc-dispatcher.
-(declare-function vc-exec-after "vc-dispatcher" (code))
+(declare-function vc-exec-after "vc-dispatcher" (code &optional success))
 ;; Follows vc-exec-after.
 (declare-function vc-set-async-update "vc-dispatcher" (process-buffer))
 
diff --git a/lisp/vc/vc-hooks.el b/lisp/vc/vc-hooks.el
index 1f0eeb7e18..6ad26cfe67 100644
--- a/lisp/vc/vc-hooks.el
+++ b/lisp/vc/vc-hooks.el
@@ -857,6 +857,9 @@ In the latter case, VC mode is deactivated for this buffer."
 ;; (autoload 'vc-prefix-map "vc" nil nil 'keymap)
 (defvar-keymap vc-prefix-map
   "a"   #'vc-update-change-log
+  "b c" #'vc-create-branch
+  "b l" #'vc-print-branch-log
+  "b s" #'vc-switch-branch
   "d"   #'vc-dir
   "g"   #'vc-annotate
   "G"   #'vc-ignore
@@ -879,13 +882,11 @@ In the latter case, VC mode is deactivated for this 
buffer."
   "="   #'vc-diff
   "D"   #'vc-root-diff
   "~"   #'vc-revision-other-window
-  "x"   #'vc-delete-file)
+  "x"   #'vc-delete-file
+  "!"   #'vc-edit-next-command)
 (fset 'vc-prefix-map vc-prefix-map)
 (define-key ctl-x-map "v" 'vc-prefix-map)
 
-(with-suppressed-warnings ((obsolete vc-switch-backend))
-  (keymap-set vc-prefix-map "b" #'vc-switch-backend))
-
 (defvar vc-menu-map
   (let ((map (make-sparse-keymap "Version Control")))
     ;;(define-key map [show-files]
diff --git a/lisp/vc/vc-svn.el b/lisp/vc/vc-svn.el
index 08b53a7169..9c2bdf6674 100644
--- a/lisp/vc/vc-svn.el
+++ b/lisp/vc/vc-svn.el
@@ -207,7 +207,7 @@ switches."
 
 ;; dir-status-files called from vc-dir, which loads vc,
 ;; which loads vc-dispatcher.
-(declare-function vc-exec-after "vc-dispatcher" (code))
+(declare-function vc-exec-after "vc-dispatcher" (code &optional success))
 
 (autoload 'vc-expand-dirs "vc")
 
diff --git a/lisp/vc/vc.el b/lisp/vc/vc.el
index 85a96a29fa..787dd51d07 100644
--- a/lisp/vc/vc.el
+++ b/lisp/vc/vc.el
@@ -247,6 +247,11 @@
 ;;   revision argument is only supported with some older VCSes, like
 ;;   RCS and CVS, and is otherwise silently ignored.
 ;;
+;; - checkin-patch (patch-string comment)
+;;
+;;   Commit a single patch PATCH-STRING to this backend, bypassing
+;;   the changes in filesets.  COMMENT is used as a check-in comment.
+;;
 ;; * find-revision (file rev buffer)
 ;;
 ;;   Fetch revision REV of file FILE and put it into BUFFER.
@@ -444,7 +449,7 @@
 ;;
 ;;   Return the common ancestor between REV1 and REV2 revisions.
 
-;; TAG SYSTEM
+;; TAG/BRANCH SYSTEM
 ;;
 ;; - create-tag (dir name branchp)
 ;;
@@ -459,8 +464,9 @@
 ;; - retrieve-tag (dir name update)
 ;;
 ;;   Retrieve the version tagged by NAME of all registered files at or below 
DIR.
+;;   If NAME is a branch name, switch to that branch.
 ;;   If UPDATE is non-nil, then update buffers of any files in the
-;;   tag that are currently visited.  The default implementation
+;;   tag/branch that are currently visited.  The default implementation
 ;;   does a sanity check whether there aren't any uncommitted changes at
 ;;   or below DIR, and then performs a tree walk, using the `checkout'
 ;;   function to retrieve the corresponding revisions.
@@ -659,8 +665,6 @@
 ;;   display the branch name in the mode-line.  Replace
 ;;   vc-cvs-sticky-tag with that.
 ;;
-;; - Add a primitives for switching to a branch (creating it if required.
-;;
 ;; - Add the ability to list tags and branches.
 ;;
 ;;;; Unify two different versions of the amend capability
@@ -804,12 +808,12 @@ not specific to any particular backend."
 (defcustom vc-annotate-switches nil
   "A string or list of strings specifying switches for annotate under VC.
 When running annotate under a given BACKEND, VC uses the first
-non-nil value of `vc-BACKEND-annotate-switches', `vc-annotate-switches',
-and `annotate-switches', in that order.  Since nil means to check the
-next variable in the sequence, either of the first two may use
-the value t to mean no switches at all.  `vc-annotate-switches'
-should contain switches that are specific to version control, but
-not specific to any particular backend.
+non-nil value of `vc-BACKEND-annotate-switches' and
+`vc-annotate-switches', in that order.  Since nil means to check
+the next variable in the sequence, setting the first to the value
+t means no switches at all.  `vc-annotate-switches' should
+contain switches that are specific to version control, but not
+specific to any particular backend.
 
 As very few switches (if any) are used across different VC tools,
 please consider using the specific `vc-BACKEND-annotate-switches'
@@ -1010,7 +1014,11 @@ responsible for the given file."
                           (lambda (backend)
                             (when-let ((dir (vc-call-backend
                                              backend 'responsible-p file)))
-                              (cons backend dir)))
+                              ;; We run DIR through `expand-file-name'
+                              ;; so that abbreviated directories, such
+                              ;; as "~/", wouldn't look "less specific"
+                              ;; due to their artificially shorter length.
+                              (cons backend (expand-file-name dir))))
                           vc-handled-backends))))
         ;; Just a single response (or none); use it.
         (if (< (length dirs) 2)
@@ -1045,7 +1053,8 @@ Within directories, only files already under version 
control are noticed."
        ((derived-mode-p 'log-edit-mode) log-edit-vc-backend)
        ((derived-mode-p 'diff-mode)     diff-vc-backend)
         ;; Maybe we could even use comint-mode rather than shell-mode?
-       ((derived-mode-p 'dired-mode 'shell-mode 'compilation-mode)
+       ((derived-mode-p
+          'dired-mode 'shell-mode 'eshell-mode 'compilation-mode)
         (ignore-errors (vc-responsible-backend default-directory)))
        (vc-mode (vc-backend buffer-file-name))))
 
@@ -1102,6 +1111,8 @@ BEWARE: this function may change the current buffer."
       (vc-dir-deduce-fileset state-model-only-files))
      ((derived-mode-p 'dired-mode)
       (dired-vc-deduce-fileset state-model-only-files not-state-changing))
+     ((derived-mode-p 'diff-mode)
+      (diff-vc-deduce-fileset))
      ((setq backend (vc-backend buffer-file-name))
       (if state-model-only-files
        (list backend (list buffer-file-name)
@@ -1114,7 +1125,8 @@ BEWARE: this function may change the current buffer."
            (or (buffer-file-name vc-parent-buffer)
                                (with-current-buffer vc-parent-buffer
                                  (or (derived-mode-p 'vc-dir-mode)
-                                     (derived-mode-p 'dired-mode)))))
+                                     (derived-mode-p 'dired-mode)
+                                     (derived-mode-p 'diff-mode)))))
       (progn                  ;FIXME: Why not `with-current-buffer'? --Stef.
        (set-buffer vc-parent-buffer)
        (vc-deduce-fileset-1 not-state-changing allow-unregistered 
state-model-only-files)))
@@ -1230,6 +1242,8 @@ with, using the most specific one."
       (error "Fileset files are missing, so cannot be operated on"))
      ((eq state 'ignored)
       (error "Fileset files are ignored by the version-control system"))
+     ((eq model 'patch)
+      (vc-checkin files backend nil nil nil (buffer-string)))
      ((or (null state) (eq state 'unregistered))
       (cond (verbose
              (let ((backend (vc-read-backend "Backend to register to: ")))
@@ -1613,15 +1627,18 @@ Type \\[vc-next-action] to check in changes.")
      (format "I stole the lock on %s, " file-description)
      (current-time-string)
      ".\n")
-    (message "Please explain why you stole the lock.  Type C-c C-c when 
done.")))
+    (message
+     (substitute-command-keys
+      "Please explain why you stole the lock.  Type \\`C-c C-c' when done"))))
 
-(defun vc-checkin (files backend &optional comment initial-contents rev)
+(defun vc-checkin (files backend &optional comment initial-contents rev 
patch-string)
   "Check in FILES. COMMENT is a comment string; if omitted, a
 buffer is popped up to accept a comment.  If INITIAL-CONTENTS is
 non-nil, then COMMENT is used as the initial contents of the log
 entry buffer.
 The optional argument REV may be a string specifying the new revision
 level (only supported for some older VCSes, like RCS and CVS).
+The optional argument PATCH-STRING is a string to check in as a patch.
 
 Runs the normal hooks `vc-before-checkin-hook' and `vc-checkin-hook'."
   (run-hooks 'vc-before-checkin-hook)
@@ -1643,7 +1660,9 @@ Runs the normal hooks `vc-before-checkin-hook' and 
`vc-checkin-hook'."
        ;; vc-checkin-switches, but 'the' local buffer is
        ;; not a well-defined concept for filesets.
        (progn
-         (vc-call-backend backend 'checkin files comment rev)
+         (if patch-string
+             (vc-call-backend backend 'checkin-patch patch-string comment)
+           (vc-call-backend backend 'checkin files comment rev))
          (mapc #'vc-delete-automatic-version-backups files))
        `((vc-state . up-to-date)
          (vc-checkout-time . ,(file-attribute-modification-time
@@ -1651,7 +1670,8 @@ Runs the normal hooks `vc-before-checkin-hook' and 
`vc-checkin-hook'."
          (vc-working-revision . nil)))
      (message "Checking in %s...done" (vc-delistify files)))
    'vc-checkin-hook
-   backend))
+   backend
+   patch-string))
 
 ;;; Additional entry points for examining version histories
 
@@ -1779,6 +1799,26 @@ objects, and finally killing buffer ORIGINAL."
 (defvar vc-diff-added-files nil
   "If non-nil, diff added files by comparing them to /dev/null.")
 
+(defvar vc-patch-string nil)
+
+(defun vc-diff-patch-string (patch-string)
+  "Report diffs to be committed from the patch.
+Like `vc-diff-internal' but uses PATCH-STRING to display
+in the output buffer."
+  (let ((buffer "*vc-diff*"))
+    (vc-setup-buffer buffer)
+    (let ((buffer-undo-list t)
+          (inhibit-read-only t))
+      (insert patch-string))
+    (setq buffer-read-only t)
+    (diff-mode)
+    (setq-local diff-vc-backend (vc-responsible-backend default-directory))
+    (setq-local revert-buffer-function
+                (lambda (_ _) (vc-diff-patch-string patch-string)))
+    (setq-local vc-patch-string patch-string)
+    (pop-to-buffer (current-buffer))
+    (vc-run-delayed (vc-diff-finish (current-buffer) nil))))
+
 (defun vc-diff-internal (async vc-fileset rev1 rev2 &optional verbose buffer)
   "Report diffs between two revisions of a fileset.
 Output goes to the buffer BUFFER, which defaults to *vc-diff*.
@@ -1877,8 +1917,11 @@ Return t if the buffer had changes, nil otherwise."
       (setq files (cadr vc-fileset))
       (setq backend (car vc-fileset))))
    ((null backend) (setq backend (vc-backend (car files)))))
-  (let ((completion-table
-         (vc-call-backend backend 'revision-completion-table files)))
+  ;; Override any `vc-filter-command-function' value, as user probably
+  ;; doesn't want to edit the command to get the completions.
+  (let* ((vc-filter-command-function #'list)
+         (completion-table
+          (vc-call-backend backend 'revision-completion-table files)))
     (if completion-table
         (completing-read prompt completion-table
                          nil nil initial-input 'vc-revision-history default)
@@ -1968,19 +2011,20 @@ state of each file in the fileset."
     (when buffer-file-name (vc-buffer-sync not-urgent))))
 
 ;;;###autoload
-(defun vc-diff (&optional historic not-urgent)
+(defun vc-diff (&optional historic not-urgent fileset)
   "Display diffs between file revisions.
 Normally this compares the currently selected fileset with their
 working revisions.  With a prefix argument HISTORIC, it reads two revision
 designators specifying which revisions to compare.
 
 The optional argument NOT-URGENT non-nil means it is ok to say no to
-saving the buffer."
+saving the buffer.  The optional argument FILESET can override the
+deduced fileset."
   (interactive (list current-prefix-arg t))
   (if historic
       (call-interactively 'vc-version-diff)
     (vc-maybe-buffer-sync not-urgent)
-    (let ((fileset (vc-deduce-fileset t)))
+    (let ((fileset (or fileset (vc-deduce-fileset t))))
       (vc-buffer-sync-fileset fileset not-urgent)
       (vc-diff-internal t fileset nil nil
                        (called-interactively-p 'interactive)))))
@@ -2397,7 +2441,23 @@ checked out in that new branch."
   (message "Making %s... done" (if branchp "branch" "tag")))
 
 ;;;###autoload
-(defun vc-retrieve-tag (dir name)
+(defun vc-create-branch (dir name)
+  "Descending recursively from DIR, make a branch called NAME.
+After a new branch is made, the files are checked out in that new branch.
+Uses `vc-create-tag' with the non-nil arg `branchp'."
+  (interactive
+   (let ((granularity
+          (vc-call-backend (vc-responsible-backend default-directory)
+                           'revision-granularity)))
+     (list
+      (if (eq granularity 'repository)
+          default-directory
+        (read-directory-name "Directory: " default-directory default-directory 
t))
+      (read-string "New branch name: " nil 'vc-revision-history))))
+  (vc-create-tag dir name t))
+
+;;;###autoload
+(defun vc-retrieve-tag (dir name &optional branchp)
   "For each file in or below DIR, retrieve their tagged version NAME.
 NAME can name a branch, in which case this command will switch to the
 named branch in the directory DIR.
@@ -2407,6 +2467,8 @@ If NAME is empty, it refers to the latest revisions of 
the current branch.
 If locking is used for the files in DIR, then there must not be any
 locked files at or below DIR (but if NAME is empty, locked files are
 allowed and simply skipped).
+If the prefix argument BRANCHP is given, switch the branch
+and check out the files in that branch.
 This function runs the hook `vc-retrieve-tag-hook' when finished."
   (interactive
    (let* ((granularity
@@ -2422,15 +2484,21 @@ This function runs the hook `vc-retrieve-tag-hook' when 
finished."
              (read-directory-name "Directory: " default-directory nil t))))
      (list
       dir
-      (vc-read-revision (format-prompt "Tag name to retrieve" "latest 
revisions")
+      (vc-read-revision (format-prompt
+                         (if current-prefix-arg
+                             "Switch to branch"
+                           "Tag name to retrieve")
+                         "latest revisions")
                         (list dir)
-                        (vc-responsible-backend dir)))))
+                        (vc-responsible-backend dir))
+      current-prefix-arg)))
   (let* ((backend (vc-responsible-backend dir))
          (update (when (vc-call-backend backend 'update-on-retrieve-tag)
                    (yes-or-no-p "Update any affected buffers? ")))
         (msg (if (or (not name) (string= name ""))
                  (format "Updating %s... " (abbreviate-file-name dir))
-               (format "Retrieving tag %s into %s... "
+               (format "Retrieving %s %s into %s... "
+                        (if branchp "branch" "tag")
                        name (abbreviate-file-name dir)))))
     (message "%s" msg)
     (vc-call-backend backend 'retrieve-tag dir name update)
@@ -2438,6 +2506,25 @@ This function runs the hook `vc-retrieve-tag-hook' when 
finished."
     (run-hooks 'vc-retrieve-tag-hook)
     (message "%s" (concat msg "done"))))
 
+;;;###autoload
+(defun vc-switch-branch (dir name)
+  "Switch to the branch NAME in the directory DIR.
+If NAME is empty, it refers to the latest revisions of the current branch.
+Uses `vc-retrieve-tag' with the non-nil arg `branchp'."
+  (interactive
+   (let* ((granularity
+           (vc-call-backend (vc-responsible-backend default-directory)
+                            'revision-granularity))
+          (dir
+           (if (eq granularity 'repository)
+               (expand-file-name (vc-root-dir))
+             (read-directory-name "Directory: " default-directory nil t))))
+     (list
+      dir
+      (vc-read-revision (format-prompt "Switch to branch" "latest revisions")
+                        (list dir)
+                        (vc-responsible-backend dir)))))
+  (vc-retrieve-tag dir name t))
 
 ;; Miscellaneous other entry points
 
@@ -2663,8 +2750,10 @@ with its diffs (if the underlying VCS supports that)."
 (defun vc-print-branch-log (branch)
   "Show the change log for BRANCH root in a window."
   (interactive
-   (list
-    (vc-read-revision "Branch to log: ")))
+   (let* ((backend (vc-responsible-backend default-directory))
+          (rootdir (vc-call-backend backend 'root default-directory)))
+     (list
+      (vc-read-revision "Branch to log: " (list rootdir) backend))))
   (when (equal branch "")
     (error "No branch specified"))
   (let* ((backend (vc-responsible-backend default-directory))
@@ -2676,10 +2765,11 @@ with its diffs (if the underlying VCS supports that)."
 ;;;###autoload
 (defun vc-log-incoming (&optional remote-location)
   "Show log of changes that will be received with pull from REMOTE-LOCATION.
-When called interactively with a prefix argument, prompt for REMOTE-LOCATION."
+When called interactively with a prefix argument, prompt for REMOTE-LOCATION.
+In some version control systems REMOTE-LOCATION can be a remote branch name."
   (interactive
    (when current-prefix-arg
-     (list (read-string "Remote location (empty for default): "))))
+     (list (read-string "Remote location/branch (empty for default): "))))
   (let ((backend (vc-deduce-backend)))
     (unless backend
       (error "Buffer is not version controlled"))
@@ -2689,10 +2779,11 @@ When called interactively with a prefix argument, 
prompt for REMOTE-LOCATION."
 ;;;###autoload
 (defun vc-log-outgoing (&optional remote-location)
   "Show log of changes that will be sent with a push operation to 
REMOTE-LOCATION.
-When called interactively with a prefix argument, prompt for REMOTE-LOCATION."
+When called interactively with a prefix argument, prompt for REMOTE-LOCATION.
+In some version control systems REMOTE-LOCATION can be a remote branch name."
   (interactive
    (when current-prefix-arg
-     (list (read-string "Remote location (empty for default): "))))
+     (list (read-string "Remote location/branch (empty for default): "))))
   (let ((backend (vc-deduce-backend)))
     (unless backend
       (error "Buffer is not version controlled"))
@@ -2879,6 +2970,28 @@ It also signals an error in a Bazaar bound branch."
         (vc-call-backend backend 'push arg)
       (user-error "VC push is unsupported for `%s'" backend))))
 
+;;;###autoload
+(defun vc-pull-and-push (&optional arg)
+  "First pull, and then push the current branch.
+The push will only be performed if the pull operation was successful.
+
+You must be visiting a version controlled file, or in a `vc-dir' buffer.
+
+On a distributed version control system, this runs a \"pull\"
+operation on the current branch, prompting for the precise
+command if required.  Optional prefix ARG non-nil forces a prompt
+for the VCS command to run.  If this is successful, a \"push\"
+operation will then be done.
+
+On a non-distributed version control system, this signals an error.
+It also signals an error in a Bazaar bound branch."
+  (interactive "P")
+  (let* ((vc-fileset (vc-deduce-fileset t))
+        (backend (car vc-fileset)))
+    (if (vc-find-backend-function backend 'pull-and-push)
+        (vc-call-backend backend 'pull-and-push arg)
+      (user-error "VC pull-and-push is unsupported for `%s'" backend))))
+
 (defun vc-version-backup-file (file &optional rev)
   "Return name of backup file for revision REV of FILE.
 If version backups should be used for FILE, and there exists
@@ -3124,6 +3237,33 @@ log entries should be gathered."
   (vc-call-backend (vc-responsible-backend default-directory)
                    'update-changelog args))
 
+(defvar vc-filter-command-function)
+
+;;;###autoload
+(defun vc-edit-next-command ()
+  "Request editing the next VC shell command before execution.
+This is a prefix command.  It affects only a VC command executed
+immediately after this one."
+  (interactive)
+  (letrec ((minibuffer-depth (minibuffer-depth))
+           (command this-command)
+           (keys (key-description (this-command-keys)))
+           (old vc-filter-command-function)
+           (echofun (lambda () keys))
+           (postfun
+            (lambda ()
+              (unless (or (eq this-command command)
+                          (> (minibuffer-depth) minibuffer-depth))
+                (remove-hook 'post-command-hook postfun)
+                (remove-hook 'prefix-command-echo-keystrokes-functions
+                             echofun)
+                (setq vc-filter-command-function old)))))
+    (add-hook 'post-command-hook postfun)
+    (add-hook 'prefix-command-echo-keystrokes-functions echofun)
+    (setq vc-filter-command-function
+          (lambda (&rest args)
+            (apply #'vc-user-edit-command (apply old args))))))
+
 (defun vc-default-responsible-p (_backend _file)
   "Indicate whether BACKEND is responsible for FILE.
 The default is to return nil always."
@@ -3239,8 +3379,6 @@ to provide the `find-revision' operation instead."
 
 
 ;; These things should probably be generally available
-(define-obsolete-function-alias 'vc-string-prefix-p 'string-prefix-p "24.3")
-
 (defun vc-file-tree-walk (dirname func &rest args)
   "Walk recursively through DIRNAME.
 Invoke FUNC f ARGS on each VC-managed file f underneath it."
diff --git a/lisp/wdired.el b/lisp/wdired.el
index 33e0b96f0f..6904bac4d0 100644
--- a/lisp/wdired.el
+++ b/lisp/wdired.el
@@ -1024,7 +1024,8 @@ Like original function but it skips read-only words."
         (setq filename (wdired-get-filename nil t))
         (if (= (length perms-new) 10)
             (condition-case nil
-                (set-file-modes filename (wdired-perms-to-number perms-new))
+               (set-file-modes filename (wdired-perms-to-number perms-new)
+                               'nofollow)
               (error
                (setq errors (1+ errors))
                (dired-log "Setting mode of `%s' to `%s' failed\n\n"
diff --git a/lisp/whitespace.el b/lisp/whitespace.el
index 41b0a34f9e..d7b83ef34a 100644
--- a/lisp/whitespace.el
+++ b/lisp/whitespace.el
@@ -730,7 +730,7 @@ Used when `whitespace-style' includes `indentation',
   :group 'whitespace)
 
 
-(defcustom whitespace-empty-at-bob-regexp "\\`\\(\\([ \t]*\n\\)+\\)"
+(defcustom whitespace-empty-at-bob-regexp "\\`\\([ \t\n]*\\(?:\n\\|$\\)\\)"
   "Specify regexp for empty lines at beginning of buffer.
 
 Used when `whitespace-style' includes `empty'."
@@ -1129,28 +1129,33 @@ SYMBOL  is a valid symbol associated with CHAR.
        See `whitespace-style-value-list'.")
 
 
-(defvar whitespace-active-style nil
+(defvar-local whitespace-active-style nil
   "Used to save locally `whitespace-style' value.")
 
-(defvar whitespace-point (point)
+(defvar-local whitespace-point (point)
   "Used to save locally current point value.
 Used by function `whitespace-trailing-regexp' (which see).")
 (defvar-local whitespace-point--used nil
   "Region whose highlighting depends on `whitespace-point'.")
 
-(defvar whitespace-font-lock-refontify nil
-  "Used to save locally the font-lock refontify state.
-Used by function `whitespace-post-command-hook' (which see).")
-
-(defvar whitespace-bob-marker nil
-  "Used to save locally the bob marker value.
-Used by function `whitespace-post-command-hook' (which see).")
-
-(defvar whitespace-eob-marker nil
-  "Used to save locally the eob marker value.
-Used by function `whitespace-post-command-hook' (which see).")
-
-(defvar whitespace-buffer-changed nil
+(defvar-local whitespace-bob-marker nil
+  "Position of the buffer's first non-empty line.
+This marker is positioned at the beginning of the first line in
+the buffer that contains a non-space character.  If no such line
+exists, this is positioned at the end of the buffer (which could
+be after `whitespace-eob-marker' if the buffer contains nothing
+but empty lines).")
+
+(defvar-local whitespace-eob-marker nil
+  "Position after the buffer's last non-empty line.
+This marker is positioned at the beginning of the first line
+immediately following the last line in the buffer that contains a
+non-space character.  If no such line exists, this is positioned
+at the beginning of the buffer (which could be before
+`whitespace-bob-marker' if the buffer contains nothing but empty
+lines).")
+
+(defvar-local whitespace-buffer-changed nil
   "Used to indicate locally if buffer changed.
 Used by `whitespace-post-command-hook' and `whitespace-buffer-changed'
 functions (which see).")
@@ -1770,7 +1775,7 @@ cleaning up these problems."
 ;;;; Internal functions
 
 
-(defvar whitespace-font-lock-keywords nil
+(defvar-local whitespace-font-lock-keywords nil
   "Used to save the value `whitespace-color-on' adds to `font-lock-keywords'.")
 
 
@@ -1997,10 +2002,10 @@ resultant list will be returned."
   the-list)
 
 
-(defvar whitespace-display-table nil
+(defvar-local whitespace-display-table nil
   "Used to save a local display table.")
 
-(defvar whitespace-display-table-was-local nil
+(defvar-local whitespace-display-table-was-local nil
   "Used to remember whether a buffer initially had a local display table.")
 
 (defun whitespace-turn-on ()
@@ -2061,12 +2066,16 @@ resultant list will be returned."
     (setq whitespace-point--used
           (let ((ol (make-overlay (point) (point) nil nil t)))
             (delete-overlay ol) ol))
-    (setq-local whitespace-font-lock-refontify 0)
     (setq-local whitespace-bob-marker (point-min-marker))
     (setq-local whitespace-eob-marker (point-max-marker))
+    (whitespace--update-bob-eob)
     (setq-local whitespace-buffer-changed nil)
     (add-hook 'post-command-hook #'whitespace-post-command-hook nil t)
     (add-hook 'before-change-functions #'whitespace-buffer-changed nil t)
+    (add-hook 'after-change-functions #'whitespace--update-bob-eob
+              ;; The -1 ensures that it runs before any
+              ;; `font-lock-mode' hook functions.
+              -1 t)
     ;; Add whitespace-mode color into font lock.
     (setq
      whitespace-font-lock-keywords
@@ -2119,11 +2128,11 @@ resultant list will be returned."
            `((,whitespace-big-indent-regexp 1 'whitespace-big-indent t)))
        ,@(when (memq 'empty whitespace-active-style)
            ;; Show empty lines at beginning of buffer.
-           `((,#'whitespace-empty-at-bob-regexp
-              1 whitespace-empty t)
+           `((,#'whitespace--empty-at-bob-matcher
+              0 whitespace-empty t)
              ;; Show empty lines at end of buffer.
-             (,#'whitespace-empty-at-eob-regexp
-              1 whitespace-empty t)))
+             (,#'whitespace--empty-at-eob-matcher
+              0 whitespace-empty t)))
        ,@(when (or (memq 'space-after-tab whitespace-active-style)
                    (memq 'space-after-tab::tab whitespace-active-style)
                    (memq 'space-after-tab::space whitespace-active-style))
@@ -2158,6 +2167,8 @@ resultant list will be returned."
   (when (whitespace-style-face-p)
     (remove-hook 'post-command-hook #'whitespace-post-command-hook t)
     (remove-hook 'before-change-functions #'whitespace-buffer-changed t)
+    (remove-hook 'after-change-functions #'whitespace--update-bob-eob
+                 t)
     (font-lock-remove-keywords nil whitespace-font-lock-keywords)
     (font-lock-flush)))
 
@@ -2206,114 +2217,83 @@ resultant list will be returned."
           (format ".\\{%d\\}" rem)))))
    limit t))
 
-(defun whitespace-empty-at-bob-regexp (limit)
-  "Match spaces at beginning of buffer (BOB) which do not contain point at 
BOB."
-  (let ((b (point))
-       r)
-    (cond
-     ;; at bob
-     ((= b 1)
-      (setq r (and (looking-at whitespace-empty-at-bob-regexp)
-                   (or (/= whitespace-point 1)
-                       (progn (whitespace-point--used (match-beginning 0)
-                                                      (match-end 0))
-                              nil))))
-      (set-marker whitespace-bob-marker (if r (match-end 1) b)))
-     ;; inside bob empty region
-     ((<= limit whitespace-bob-marker)
-      (setq r (looking-at whitespace-empty-at-bob-regexp))
-      (if r
-         (when (< (match-end 1) limit)
-           (set-marker whitespace-bob-marker (match-end 1)))
-       (set-marker whitespace-bob-marker b)))
-     ;; intersection with end of bob empty region
-     ((<= b whitespace-bob-marker)
-      (setq r (looking-at whitespace-empty-at-bob-regexp))
-      (set-marker whitespace-bob-marker (if r (match-end 1) b)))
-     ;; it is not inside bob empty region
-     (t
-      (setq r nil)))
-    ;; move to end of matching
-    (and r (goto-char (match-end 1)))
-    r))
-
-
-(defsubst whitespace-looking-back (regexp limit)
+(defun whitespace--empty-at-bob-matcher (limit)
+  "Match empty/space-only lines at beginning of buffer (BoB).
+Match does not extend past position LIMIT.  For improved UX, the
+line containing `whitespace-point' and subsequent lines are
+excluded from the match.  (The idea is that the user might be
+about to start typing, and if they do, that line and any
+following empty lines will no longer be BoB empty lines.
+Highlighting those lines can be distracting.)"
+  (let ((p (point))
+        (e (min whitespace-bob-marker limit
+                ;; EoB marker will be before BoB marker if the buffer
+                ;; has nothing but empty lines.
+                whitespace-eob-marker
+                (save-excursion (goto-char whitespace-point)
+                                (line-beginning-position)))))
+    (when (= p 1)
+      ;; See the comment in `whitespace--update-bob-eob' for why this
+      ;; text property is added here.
+      (put-text-property 1 whitespace-bob-marker
+                         'font-lock-multiline t))
+    (when (< p e)
+      (set-match-data (list p e))
+      (goto-char e))))
+
+(defsubst whitespace--looking-back (regexp)
   (save-excursion
-    (when (/= 0 (skip-chars-backward " \t\n" limit))
+    (when (/= 0 (skip-chars-backward " \t\n"))
       (unless (bolp)
        (forward-line 1))
       (looking-at regexp))))
 
-
-(defun whitespace-empty-at-eob-regexp (limit)
-  "Match spaces at end of buffer which do not contain the point at end of \
-buffer."
-  (let ((b (point))
-       (e (1+ (buffer-size)))
-       r)
-    (cond
-     ;; at eob
-     ((= limit e)
-      (goto-char limit)
-      (setq r (whitespace-looking-back whitespace-empty-at-eob-regexp b))
-      (when (and r (= whitespace-point e))
-        (setq r nil)
-        (whitespace-point--used (match-beginning 0) (match-end 0)))
-      (if r
-         (set-marker whitespace-eob-marker (match-beginning 1))
-       (set-marker whitespace-eob-marker limit)
-       (goto-char b)))                 ; return back to initial position
-     ;; inside eob empty region
-     ((>= b whitespace-eob-marker)
-      (goto-char limit)
-      (setq r (whitespace-looking-back whitespace-empty-at-eob-regexp b))
-      (if r
-         (when (> (match-beginning 1) b)
-           (set-marker whitespace-eob-marker (match-beginning 1)))
-       (set-marker whitespace-eob-marker limit)
-       (goto-char b)))                 ; return back to initial position
-     ;; intersection with beginning of eob empty region
-     ((>= limit whitespace-eob-marker)
-      (goto-char limit)
-      (setq r (whitespace-looking-back whitespace-empty-at-eob-regexp b))
-      (if r
-         (set-marker whitespace-eob-marker (match-beginning 1))
-       (set-marker whitespace-eob-marker limit)
-       (goto-char b)))                 ; return back to initial position
-     ;; it is not inside eob empty region
-     (t
-      (setq r nil)))
-    r))
-
+(defun whitespace--empty-at-eob-matcher (limit)
+  "Match empty/space-only lines at end of buffer (EoB).
+Match does not extend past position LIMIT.  For improved UX, the
+line containing `whitespace-point' and preceding lines are
+excluded from the match.  (The idea is that the user might be
+about to start typing, and if they do, that line and previous
+empty lines will no longer be EoB empty lines.  Highlighting
+those lines can be distracting.)"
+  (when (= limit (1+ (buffer-size)))
+    ;; See the comment in `whitespace--update-bob-eob' for why this
+    ;; text property is added here.
+    (put-text-property whitespace-eob-marker limit
+                       'font-lock-multiline t))
+  (let ((b (max (point) whitespace-eob-marker
+                whitespace-bob-marker ; See comment in the bob func.
+                (save-excursion (goto-char whitespace-point)
+                                (forward-line 1)
+                                (point)))))
+    (when (< b limit)
+      (set-match-data (list b limit))
+      (goto-char limit))))
 
 (defun whitespace-buffer-changed (_beg _end)
   "Set `whitespace-buffer-changed' variable to t."
   (setq whitespace-buffer-changed t))
 
-
 (defun whitespace-post-command-hook ()
   "Save current point into `whitespace-point' variable.
 Also refontify when necessary."
   (unless (and (eq whitespace-point (point))
                (not whitespace-buffer-changed))
+    (when (and (not whitespace-buffer-changed)
+               (memq 'empty whitespace-active-style))
+      ;; No need to handle the `whitespace-buffer-changed' case here
+      ;; because that is taken care of by the `font-lock-multiline'
+      ;; text property.
+      (when (<= (min (point) whitespace-point) whitespace-bob-marker)
+        (font-lock-flush 1 whitespace-bob-marker))
+      (when (>= (max (point) whitespace-point) whitespace-eob-marker)
+        (font-lock-flush whitespace-eob-marker (1+ (buffer-size)))))
+    (setq-local whitespace-buffer-changed nil)
     (setq whitespace-point (point))    ; current point position
-    (let ((refontify
-           (cond
-            ;; It is at end of buffer (eob).
-            ((= whitespace-point (1+ (buffer-size)))
-             (when (whitespace-looking-back whitespace-empty-at-eob-regexp
-                                            nil)
-               (match-beginning 0)))
-            ;; It is at end of line ...
-            ((and (eolp)
-                  ;; ... with trailing SPACE or TAB
-                  (or (memq (preceding-char) '(?\s ?\t))))
-             (line-beginning-position))
-            ;; It is at beginning of buffer (bob).
-            ((and (= whitespace-point 1)
-                  (looking-at whitespace-empty-at-bob-regexp))
-             (match-end 0))))
+    (let ((refontify (and (eolp) ; It is at end of line ...
+                          ;; ... with trailing SPACE or TAB
+                          (or (memq (preceding-char) '(?\s ?\t)))
+                          (line-beginning-position)))
           (ostart (overlay-start whitespace-point--used)))
       (cond
        ((not refontify)
@@ -2367,6 +2347,78 @@ to `indent-tabs-mode' and `tab-width'."
       (when whitespace-mode
         (font-lock-flush)))))
 
+(defun whitespace--update-bob-eob (&optional beg end &rest _)
+  "Update `whitespace-bob-marker' and `whitespace-eob-marker'.
+Also apply `font-lock-multiline' text property.  If BEG and END
+are non-nil, assume that only characters in that range have
+changed since the last call to this function (for optimization
+purposes)."
+  (when (memq 'empty whitespace-active-style)
+    ;; When a line is changed, `font-lock-mode' normally limits
+    ;; re-processing to only the changed line.  That behavior is
+    ;; problematic for highlighting `empty' lines because adding or
+    ;; deleting a character might affect lines before or after the
+    ;; change.  To address this, all `empty' lines are marked with a
+    ;; non-nil `font-lock-multiline' text property.  This forces
+    ;; `font-lock-mode' to re-process all of the lines whenever
+    ;; there's an edit within any one of them.
+    ;;
+    ;; The text property must be set on `empty' lines twice per
+    ;; relevant change:
+    ;;
+    ;;   1. Before the change.  This is necessary to ensure that
+    ;;      previously highlighted lines become un-highlighted if
+    ;;      necessary.  The text property must be added after the
+    ;;      previous `font-lock-mode' run (the run in reaction to the
+    ;;      previous change) because `font-lock-mode' clears the text
+    ;;      property when it runs.
+    ;;
+    ;;   2. After the change, but before `font-lock-mode' reacts to
+    ;;      the change.  This is necessary to ensure that new `empty'
+    ;;      lines become highlighted.
+    ;;
+    ;; This hook function is responsible for #2, while the
+    ;; `whitespace--empty-at-bob-matcher' and
+    ;; `whitespace--empty-at-eob-matcher' functions are responsible
+    ;; for #1.  (Those functions run after `font-lock-mode' clears the
+    ;; text property and before the next change.)
+    (save-excursion
+      (save-restriction
+        (widen)
+        (let ((inhibit-read-only t))
+          (when (or (null beg)
+                    (<= beg (save-excursion
+                              (goto-char whitespace-bob-marker)
+                              ;; Any change in the first non-`empty'
+                              ;; line, even if it's not the first
+                              ;; character in the line, can potentially
+                              ;; cause subsequent lines to become
+                              ;; classified as `empty' (e.g., delete the
+                              ;; "x" from " x").
+                              (forward-line 1)
+                              (point))))
+            (goto-char 1)
+            (set-marker whitespace-bob-marker (point))
+            (save-match-data
+              (when (looking-at whitespace-empty-at-bob-regexp)
+                (set-marker whitespace-bob-marker (match-end 1))
+                (put-text-property (match-beginning 1) (match-end 1)
+                                   'font-lock-multiline t))))
+          (when (or (null end)
+                    (>= end (save-excursion
+                              (goto-char whitespace-eob-marker)
+                              ;; See above comment for the BoB case.
+                              (forward-line -1)
+                              (point))))
+            (goto-char (1+ (buffer-size)))
+            (set-marker whitespace-eob-marker (point))
+            (save-match-data
+              (when (whitespace--looking-back
+                     whitespace-empty-at-eob-regexp)
+                (set-marker whitespace-eob-marker (match-beginning 1))
+                (put-text-property (match-beginning 1) (match-end 1)
+                                   'font-lock-multiline t)))))))))
+
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;;;; Hacked from visws.el (Miles Bader <miles@gnu.org>)
diff --git a/lisp/wid-browse.el b/lisp/wid-browse.el
index 7fc476e5df..a90f7bc160 100644
--- a/lisp/wid-browse.el
+++ b/lisp/wid-browse.el
@@ -35,12 +35,10 @@
 
 ;;; The Mode.
 
-(defvar widget-browse-mode-map
-  (let ((map (make-sparse-keymap)))
-    (set-keymap-parent map widget-keymap)
-    (define-key map "q" #'bury-buffer)
-    map)
-  "Keymap for `widget-browse-mode'.")
+(defvar-keymap widget-browse-mode-map
+  :doc "Keymap for `widget-browse-mode'."
+  :parent widget-keymap
+  "q" #'bury-buffer)
 
 (easy-menu-define widget-browse-mode-customize-menu
     widget-browse-mode-map
@@ -245,11 +243,9 @@ VALUE is assumed to be a list of widgets."
 
 ;;; Widget Minor Mode.
 
-(defvar widget-minor-mode-map
-  (let ((map (make-sparse-keymap)))
-    (set-keymap-parent map widget-keymap)
-    map)
-  "Keymap used in Widget Minor Mode.")
+(defvar-keymap widget-minor-mode-map
+  :doc "Keymap used in Widget Minor Mode."
+  :parent widget-keymap)
 
 ;;;###autoload
 (define-minor-mode widget-minor-mode
diff --git a/lisp/wid-edit.el b/lisp/wid-edit.el
index ec2eb146e9..4d9663cea9 100644
--- a/lisp/wid-edit.el
+++ b/lisp/wid-edit.el
@@ -3452,11 +3452,9 @@ It reads a directory name from an editable text field."
 (defvar widget-key-sequence-default-value [ignore]
   "Default value for an empty key sequence.")
 
-(defvar widget-key-sequence-map
-  (let ((map (make-sparse-keymap)))
-    (set-keymap-parent map widget-field-keymap)
-    (define-key map [(control ?q)] 'widget-key-sequence-read-event)
-    map))
+(defvar-keymap widget-key-sequence-map
+  :parent widget-field-keymap
+  "C-q" #'widget-key-sequence-read-event)
 
 (define-widget 'key-sequence 'restricted-sexp
   "A key sequence.  This is obsolete; use the `key' type instead."
@@ -4145,6 +4143,15 @@ is inline."
 (define-obsolete-function-alias 'widget-visibility-value-create
   #'widget-toggle-value-create "29.1")
 
+;;; Buffer predicates.
+(define-widget 'buffer-predicate 'lazy
+  "A buffer predicate."
+  :tag "Buffer predicate"
+  :type '(choice (const :tag "All buffers" t)
+                 (const :tag "No buffers" nil)
+                 ;; FIXME: This should be expanded somehow.
+                 sexp))
+
 (provide 'wid-edit)
 
 ;;; wid-edit.el ends here
diff --git a/lisp/window.el b/lisp/window.el
index db69379e69..905803b19e 100644
--- a/lisp/window.el
+++ b/lisp/window.el
@@ -5672,9 +5672,9 @@ the original point in both windows."
   :type 'boolean
   :group 'windows)
 
-(defun split-window-below (&optional size)
-  "Split the selected window into two windows, one above the other.
-The selected window is above.  The newly split-off window is
+(defun split-window-below (&optional size window-to-split)
+  "Split WINDOW-TO-SPLIT into two windows, one above the other.
+WINDOW-TO-SPLIT is above.  The newly split-off window is
 below and displays the same buffer.  Return the new window.
 
 If optional argument SIZE is omitted or nil, both windows get the
@@ -5683,22 +5683,22 @@ same height, or close to it.  If SIZE is positive, the 
upper
 lower (new) window gets -SIZE lines.
 
 If the variable `split-window-keep-point' is non-nil, both
-windows get the same value of point as the selected window.
+windows get the same value of point as the WINDOW-TO-SPLIT.
 Otherwise, the window starts are chosen so as to minimize the
 amount of redisplay; this is convenient on slow terminals."
-  (interactive "P")
-  (let ((old-window (selected-window))
-       (old-point (window-point))
-       (size (and size (prefix-numeric-value size)))
+  (interactive `(,(when current-prefix-arg
+                    (prefix-numeric-value current-prefix-arg))
+                 ,(selected-window)))
+  (let ((old-point (window-point))
         moved-by-window-height moved new-window bottom)
     (when (and size (< size 0) (< (- size) window-min-height))
       ;; `split-window' would not signal an error here.
       (error "Size of new window too small"))
-    (setq new-window (split-window nil size))
+    (setq new-window (split-window window-to-split size))
     (unless split-window-keep-point
-      (with-current-buffer (window-buffer)
+      (with-current-buffer (window-buffer window-to-split)
        ;; Use `save-excursion' around vertical movements below
-       ;; (Bug#10971).  Note: When the selected window's buffer has a
+       ;; (Bug#10971).  Note: When WINDOW-TO-SPLIT's buffer has a
        ;; header line, up to two lines of the buffer may not show up
        ;; in the resulting configuration.
        (save-excursion
@@ -5713,24 +5713,31 @@ amount of redisplay; this is convenient on slow 
terminals."
          (setq bottom (point)))
        (and moved-by-window-height
             (<= bottom (point))
-            (set-window-point old-window (1- bottom)))
+            (set-window-point window-to-split (1- bottom)))
        (and moved-by-window-height
             (<= (window-start new-window) old-point)
             (set-window-point new-window old-point)
             (select-window new-window))))
     ;; Always copy quit-restore parameter in interactive use.
-    (let ((quit-restore (window-parameter old-window 'quit-restore)))
+    (let ((quit-restore (window-parameter window-to-split 'quit-restore)))
       (when quit-restore
        (set-window-parameter new-window 'quit-restore quit-restore)))
     new-window))
 
 (defalias 'split-window-vertically 'split-window-below)
 
-(defun split-window-right (&optional size)
-  "Split the selected window into two side-by-side windows.
-The selected window is on the left.  The newly split-off window
-is on the right and displays the same buffer.  Return the new
-window.
+(defun split-root-window-below (&optional size)
+  "Split root window of current frame in two.
+The current window configuration is retained in the top window,
+the lower window takes up the whole width of the frame.  SIZE is
+handled as in `split-window-below'."
+  (interactive "P")
+  (split-window-below size (frame-root-window)))
+
+(defun split-window-right (&optional size window-to-split)
+  "Split WINDOW-TO-SPLIT into two side-by-side windows.
+WINDOW-TO-SPLIT is on the left.  The newly split-off window is on
+the right and displays the same buffer.  Return the new window.
 
 If optional argument SIZE is omitted or nil, both windows get the
 same width, or close to it.  If SIZE is positive, the left-hand
@@ -5739,21 +5746,30 @@ right-hand (new) window gets -SIZE columns.  Here, SIZE 
includes
 the width of the window's scroll bar; if there are no scroll
 bars, it includes the width of the divider column to the window's
 right, if any."
-  (interactive "P")
-  (let ((old-window (selected-window))
-       (size (and size (prefix-numeric-value size)))
-       new-window)
+  (interactive `(,(when current-prefix-arg
+                    (prefix-numeric-value current-prefix-arg))
+                 ,(selected-window)))
+  (let (new-window)
     (when (and size (< size 0) (< (- size) window-min-width))
       ;; `split-window' would not signal an error here.
       (error "Size of new window too small"))
-    (setq new-window (split-window nil size t))
+    (setq new-window (split-window window-to-split size t))
     ;; Always copy quit-restore parameter in interactive use.
-    (let ((quit-restore (window-parameter old-window 'quit-restore)))
+    (let ((quit-restore (window-parameter window-to-split 'quit-restore)))
       (when quit-restore
        (set-window-parameter new-window 'quit-restore quit-restore)))
     new-window))
 
 (defalias 'split-window-horizontally 'split-window-right)
+
+(defun split-root-window-right (&optional size)
+  "Split root window of current frame into two side-by-side windows.
+The current window configuration is retained within the left
+window, and a new window is created on the right, taking up the
+whole height of the frame.  SIZE is treated as by
+`split-window-right'."
+  (interactive "P")
+  (split-window-right size (frame-root-window)))
 
 ;;; Balancing windows.
 
@@ -6606,24 +6622,6 @@ fourth element is BUFFER."
      window 'quit-restore
      (list 'tab 'tab (selected-window) buffer)))))
 
-(defcustom display-buffer-function nil
-  "If non-nil, function to call to handle `display-buffer'.
-It will receive two args, the buffer and a flag which if non-nil
-means that the currently selected window is not acceptable.  It
-should choose or create a window, display the specified buffer in
-it, and return the window.
-
-The specified function should call `display-buffer-record-window'
-with corresponding arguments to set up the quit-restore parameter
-of the window used."
-  :type '(choice
-         (const nil)
-         (function :tag "function"))
-  :group 'windows)
-
-(make-obsolete-variable 'display-buffer-function
-                       'display-buffer-alist "24.3")
-
 (defcustom pop-up-frame-alist nil
   "Alist of parameters for automatically generated new frames.
 If non-nil, the value you specify here is used by the default
@@ -7729,38 +7727,34 @@ specified by the ACTION argument."
        ;; Handle the old form of the first argument.
        (inhibit-same-window (and action (not (listp action)))))
     (unless (listp action) (setq action nil))
-    (if display-buffer-function
-       ;; If `display-buffer-function' is defined, let it do the job.
-       (funcall display-buffer-function buffer inhibit-same-window)
-      ;; Otherwise, use the defined actions.
-      (let* ((user-action
-             (display-buffer-assq-regexp
-              buffer display-buffer-alist action))
-             (special-action (display-buffer--special-action buffer))
-            ;; Extra actions from the arguments to this function:
-            (extra-action
-             (cons nil (append (if inhibit-same-window
-                                   '((inhibit-same-window . t)))
-                               (if frame
-                                   `((reusable-frames . ,frame))))))
-            ;; Construct action function list and action alist.
-            (actions (list display-buffer-overriding-action
-                           user-action special-action action extra-action
-                           display-buffer-base-action
-                           display-buffer-fallback-action))
-            (functions (apply 'append
-                              (mapcar (lambda (x)
-                                        (setq x (car x))
-                                        (if (functionp x) (list x) x))
-                                      actions)))
-            (alist (apply 'append (mapcar 'cdr actions)))
-            window)
-       (unless (buffer-live-p buffer)
-         (error "Invalid buffer"))
-       (while (and functions (not window))
-         (setq window (funcall (car functions) buffer alist)
-               functions (cdr functions)))
-       (and (windowp window) window)))))
+    (let* ((user-action
+            (display-buffer-assq-regexp
+             buffer display-buffer-alist action))
+           (special-action (display-buffer--special-action buffer))
+           ;; Extra actions from the arguments to this function:
+           (extra-action
+            (cons nil (append (if inhibit-same-window
+                                  '((inhibit-same-window . t)))
+                              (if frame
+                                  `((reusable-frames . ,frame))))))
+           ;; Construct action function list and action alist.
+           (actions (list display-buffer-overriding-action
+                          user-action special-action action extra-action
+                          display-buffer-base-action
+                          display-buffer-fallback-action))
+           (functions (apply 'append
+                             (mapcar (lambda (x)
+                                       (setq x (car x))
+                                       (if (functionp x) (list x) x))
+                                     actions)))
+           (alist (apply 'append (mapcar 'cdr actions)))
+           window)
+      (unless (buffer-live-p buffer)
+        (error "Invalid buffer"))
+      (while (and functions (not window))
+        (setq window (funcall (car functions) buffer alist)
+              functions (cdr functions)))
+      (and (windowp window) window))))
 
 (defun display-buffer-other-frame (buffer)
   "Display buffer BUFFER preferably in another frame.
@@ -10137,7 +10131,7 @@ semipermanent goal column for this command."
   (when goal-column
     ;; Move to the desired column.
     (if (and line-move-visual
-             (not (or truncate-lines (truncated-partial-width-window-p))))
+             (not (or truncate-lines truncate-partial-width-windows)))
         ;; Under line-move-visual, goal-column should be
         ;; interpreted in units of the frame's canonical character
         ;; width, which is exactly what vertical-motion does.
@@ -10594,6 +10588,17 @@ displaying that processes's buffer."
 (put 'shrink-window-horizontally 'repeat-map 'resize-window-repeat-map)
 (put 'shrink-window 'repeat-map 'resize-window-repeat-map)
 
+(defvar-keymap window-prefix-map
+  :doc "Keymap for subcommands of \\`C-x w'."
+  "2" #'split-root-window-below
+  "3" #'split-root-window-right
+  "s" #'window-toggle-side-windows
+  "^ f" #'tear-off-window
+  "^ t" #'tab-window-detach
+  "-" #'fit-window-to-buffer
+  "0" #'delete-windows-on)
+(define-key ctl-x-map "w" window-prefix-map)
+
 (provide 'window)
 
 ;;; window.el ends here
diff --git a/lisp/winner.el b/lisp/winner.el
index 89f337170c..174b698e7b 100644
--- a/lisp/winner.el
+++ b/lisp/winner.el
@@ -171,8 +171,7 @@ You may want to include buffer names such as *Help*, 
*Apropos*,
               (/= 0 (minibuffer-depth)))
     (push (selected-frame) winner-modified-list)))
 
-;; A `post-command-hook' for emacsen with
-;; `window-configuration-change-hook'.
+;; Used as `post-command-hook'.
 (defun winner-save-old-configurations ()
   (when (zerop (minibuffer-depth))
     (unless (eq this-command winner-last-command)
@@ -191,8 +190,7 @@ You may want to include buffer names such as *Help*, 
*Apropos*,
   (winner-insert-if-new (selected-frame))
   (winner-remember))
 
-;; A `post-command-hook' for other emacsen.
-;; Also called by `winner-undo' before "undoing".
+;; Called by `winner-undo' before "undoing".
 (defun winner-save-conditionally ()
   (when (zerop (minibuffer-depth))
     (winner-save-unconditionally)))
@@ -318,9 +316,6 @@ You may want to include buffer names such as *Help*, 
*Apropos*,
   "Functions to run whenever Winner mode is turned on or off."
   :type 'hook)
 
-(define-obsolete-variable-alias 'winner-mode-leave-hook
-  'winner-mode-off-hook "24.3")
-
 (defcustom winner-mode-off-hook nil
   "Functions to run whenever Winner mode is turned off."
   :type 'hook)
diff --git a/lisp/xdg.el b/lisp/xdg.el
index dd0d51290d..82f1f07df5 100644
--- a/lisp/xdg.el
+++ b/lisp/xdg.el
@@ -30,6 +30,7 @@
 ;; - Thumbnail Managing Standard
 ;; - xdg-user-dirs configuration
 ;; - Desktop Entry Specification
+;; - Unofficial extension $XDG_SESSION_TYPE from systemd
 
 ;;; Code:
 
@@ -281,6 +282,18 @@ Optional argument GROUP defaults to the string \"Desktop 
Entry\"."
     (when (null (string-match-p "[^[:blank:]]" (car res))) (pop res))
     (nreverse res)))
 
+(defun xdg-current-desktop ()
+  "Return a list of strings identifying the current desktop environment.
+
+According to the XDG Desktop Entry Specification version 0.5:
+
+    If $XDG_CURRENT_DESKTOP is set then it contains a
+    colon-separated list of strings ... $XDG_CURRENT_DESKTOP
+    should have been set by the login manager, according to the
+    value of the DesktopNames found in the session file."
+  (when-let ((ret (getenv "XDG_CURRENT_DESKTOP")))
+    (string-split ret ":")))
+
 
 ;; MIME apps specification
 ;; https://standards.freedesktop.org/mime-apps-spec/mime-apps-spec-1.0.1.html
@@ -385,6 +398,18 @@ Results are cached in `xdg-mime-table'."
           (put 'xdg-mime-table 'mtime (current-time)))
         (puthash subtype (delq nil files) (cdr (assoc type 
xdg-mime-table)))))))
 
+
+;; Unofficial extension from systemd.
+
+(defun xdg-session-type ()
+  "Return the value of $XDG_SESSION_TYPE.
+Should be one of \"unspecified\", \"tty\", \"x11\", \"wayland\",
+or \"mir\".
+
+This is not part of any official Freedesktop.org standard, but is
+documented in the man page `pam_systemd'."
+  (getenv "XDG_SESSION_TYPE"))
+
 (provide 'xdg)
 
 ;;; xdg.el ends here
diff --git a/m4/assert_h.m4 b/m4/assert_h.m4
new file mode 100644
index 0000000000..c1306daef4
--- /dev/null
+++ b/m4/assert_h.m4
@@ -0,0 +1,61 @@
+# assert-h.m4
+dnl Copyright (C) 2011-2022 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+dnl From Paul Eggert.
+
+AC_DEFUN([gl_ASSERT_H],
+[
+  AC_CACHE_CHECK([for static_assert], [gl_cv_static_assert],
+    [gl_save_CFLAGS=$CFLAGS
+     for gl_working in "yes, a keyword" "yes, an <assert.h> macro"; do
+      AS_CASE([$gl_working],
+        [*assert.h*], [CFLAGS="$gl_save_CFLAGS -DINCLUDE_ASSERT_H"])
+
+      AC_COMPILE_IFELSE(
+       [AC_LANG_PROGRAM(
+          [[#if defined __clang__ && __STDC_VERSION__ < 202311
+             #pragma clang diagnostic error "-Wc2x-extensions"
+             #pragma clang diagnostic error "-Wc++17-extensions"
+            #endif
+            #ifdef INCLUDE_ASSERT_H
+             #include <assert.h>
+            #endif
+            static_assert (2 + 2 == 4, "arithmetic does not work");
+            static_assert (2 + 2 == 4);
+          ]],
+          [[
+            static_assert (sizeof (char) == 1, "sizeof does not work");
+            static_assert (sizeof (char) == 1);
+          ]])],
+       [gl_cv_static_assert=$gl_working],
+       [gl_cv_static_assert=no])
+      CFLAGS=$gl_save_CFLAGS
+      test "$gl_cv_static_assert" != no && break
+     done])
+
+  GL_GENERATE_ASSERT_H=false
+  AS_CASE([$gl_cv_static_assert],
+    [yes*keyword*],
+      [AC_DEFINE([HAVE_C_STATIC_ASSERT], [1],
+         [Define to 1 if the static_assert keyword works.])],
+    [no],
+      [GL_GENERATE_ASSERT_H=true
+       gl_NEXT_HEADERS([assert.h])])
+
+  dnl The "zz" puts this toward config.h's end, to avoid potential
+  dnl collisions with other definitions.  #undef assert so that
+  dnl programs are not tempted to use it without specifically
+  dnl including assert.h.  Break the #undef apart with a comment
+  dnl so that 'configure' does not comment it out.
+  AH_VERBATIM([zzstatic_assert],
+[#if (!defined HAVE_C_STATIC_ASSERT && !defined assert \
+     && (!defined __cplusplus \
+         || (__cpp_static_assert < 201411 \
+             && __GNUG__ < 6 && __clang_major__ < 6)))
+ #include <assert.h>
+ #undef/**/assert
+#endif])
+])
diff --git a/m4/c-bool.m4 b/m4/c-bool.m4
new file mode 100644
index 0000000000..bb109b7796
--- /dev/null
+++ b/m4/c-bool.m4
@@ -0,0 +1,51 @@
+# Check for bool that conforms to C2023.
+
+dnl Copyright 2022 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+AC_DEFUN([gl_C_BOOL],
+[
+  AC_CACHE_CHECK([for bool, true, false], [gl_cv_c_bool],
+    [AC_COMPILE_IFELSE(
+       [AC_LANG_SOURCE([[
+          #if true == false
+           #error "true == false"
+          #endif
+          extern bool b;
+          bool b = true == false;]])],
+       [gl_cv_c_bool=yes],
+       [gl_cv_c_bool=no])])
+  if test "$gl_cv_c_bool" = yes; then
+    AC_DEFINE([HAVE_C_BOOL], [1],
+      [Define to 1 if bool, true and false work as per C2023.])
+  fi
+
+  AC_CHECK_HEADERS_ONCE([stdbool.h])
+
+  dnl The "zz" puts this toward config.h's end, to avoid potential
+  dnl collisions with other definitions.
+  dnl If 'bool', 'true' and 'false' do not work, arrange for them to work.
+  dnl In C, this means including <stdbool.h> if it is not already included.
+  dnl However, if the preprocessor mistakenly treats 'true' as 0,
+  dnl define it to a bool expression equal to 1; this is needed in
+  dnl Sun C++ 5.11 (Oracle Solaris Studio 12.2, 2010) and older.
+  AH_VERBATIM([zzbool],
+[#ifndef HAVE_C_BOOL
+# if !defined __cplusplus && !defined __bool_true_false_are_defined
+#  if HAVE_STDBOOL_H
+#   include <stdbool.h>
+#  else
+#   if defined __SUNPRO_C
+#    error "<stdbool.h> is not usable with this configuration. To make it 
usable, add -D_STDC_C99= to $CC."
+#   else
+#    error "<stdbool.h> does not exist on this platform. Use gnulib module 
'stdbool-c99' instead of gnulib module 'stdbool'."
+#   endif
+#  endif
+# endif
+# if !true
+#  define true (!false)
+# endif
+#endif])
+])
diff --git a/m4/gettime.m4 b/m4/gettime.m4
index f0aeb4d0e4..c3e0713b57 100644
--- a/m4/gettime.m4
+++ b/m4/gettime.m4
@@ -1,4 +1,4 @@
-# gettime.m4 serial 11
+# gettime.m4 serial 12
 dnl Copyright (C) 2002, 2004-2006, 2009-2022 Free Software Foundation, Inc.
 dnl This file is free software; the Free Software Foundation
 dnl gives unlimited permission to copy and/or distribute it,
@@ -9,7 +9,34 @@ AC_DEFUN([gl_GETTIME],
   dnl Prerequisites of lib/gettime.c.
   AC_REQUIRE([gl_CLOCK_TIME])
   AC_REQUIRE([gl_TIMESPEC])
-  AC_CHECK_FUNCS_ONCE([timespec_get])
+
+  AC_REQUIRE([gl_CHECK_FUNC_TIMESPEC_GET])
+  if test $gl_cv_func_timespec_get = yes; then
+    AC_DEFINE([HAVE_TIMESPEC_GET], [1],
+      [Define if you have the timespec_get function.])
+  fi
+])
+
+dnl Tests whether the function timespec_get exists.
+dnl Sets gl_cv_func_timespec_get.
+AC_DEFUN([gl_CHECK_FUNC_TIMESPEC_GET],
+[
+  dnl Persuade OpenBSD <time.h> to declare timespec_get().
+  AC_REQUIRE([gl_USE_SYSTEM_EXTENSIONS])
+
+  dnl We can't use AC_CHECK_FUNC here, because timespec_get() is defined as a
+  dnl static inline function in <time.h> on MSVC 14.
+  AC_CACHE_CHECK([for timespec_get], [gl_cv_func_timespec_get],
+    [AC_LINK_IFELSE(
+       [AC_LANG_PROGRAM(
+          [[#include <time.h>
+            struct timespec ts;
+          ]],
+          [[return timespec_get (&ts, 0);]])
+       ],
+       [gl_cv_func_timespec_get=yes],
+       [gl_cv_func_timespec_get=no])
+    ])
 ])
 
 AC_DEFUN([gl_GETTIME_RES],
diff --git a/m4/gnulib-common.m4 b/m4/gnulib-common.m4
index 8a5daa230e..d17cbec58c 100644
--- a/m4/gnulib-common.m4
+++ b/m4/gnulib-common.m4
@@ -1,4 +1,4 @@
-# gnulib-common.m4 serial 73
+# gnulib-common.m4 serial 74
 dnl Copyright (C) 2007-2022 Free Software Foundation, Inc.
 dnl This file is free software; the Free Software Foundation
 dnl gives unlimited permission to copy and/or distribute it,
@@ -115,7 +115,7 @@ AC_DEFUN([gl_COMMON_BODY], [
 # define _GL_HAS_C_ATTRIBUTE(attr) 0
 #endif
 
-]dnl There is no _GL_ATTRIBUTE_ALIGNED; use stdalign's _Alignas instead.
+]dnl There is no _GL_ATTRIBUTE_ALIGNED; use stdalign's alignas instead.
 [
 /* _GL_ATTRIBUTE_ALLOC_SIZE ((N)) declares that the Nth argument of the 
function
    is the size of the returned memory block.
diff --git a/m4/gnulib-comp.m4 b/m4/gnulib-comp.m4
index 0c43dde716..f1ac499132 100644
--- a/m4/gnulib-comp.m4
+++ b/m4/gnulib-comp.m4
@@ -46,6 +46,7 @@ AC_DEFUN([gl_EARLY],
   # Code from module acl-permissions:
   # Code from module alloca-opt:
   # Code from module allocator:
+  # Code from module assert-h:
   # Code from module at-internal:
   # Code from module attribute:
   # Code from module binary-io:
@@ -122,8 +123,8 @@ AC_DEFUN([gl_EARLY],
   # Code from module intprops:
   # Code from module inttypes-incomplete:
   # Code from module largefile:
-  AC_REQUIRE([AC_SYS_LARGEFILE])
   AC_REQUIRE([gl_YEAR2038_EARLY])
+  AC_REQUIRE([AC_SYS_LARGEFILE])
   # Code from module lchmod:
   # Code from module libc-config:
   # Code from module libgmp:
@@ -171,6 +172,7 @@ AC_DEFUN([gl_EARLY],
   # Code from module stat-time:
   # Code from module std-gnu11:
   # Code from module stdalign:
+  # Code from module stdbool:
   # Code from module stdckdint:
   # Code from module stddef:
   # Code from module stdint:
@@ -233,6 +235,9 @@ AC_DEFUN([gl_INIT],
   gl_FUNC_ALLOCA
   gl_CONDITIONAL_HEADER([alloca.h])
   AC_PROG_MKDIR_P
+  gl_ASSERT_H
+  gl_CONDITIONAL_HEADER([assert.h])
+  AC_PROG_MKDIR_P
   gl___BUILTIN_EXPECT
   gl_BYTESWAP
   gl_CONDITIONAL_HEADER([byteswap.h])
@@ -486,6 +491,7 @@ AC_DEFUN([gl_INIT],
   gl_STDALIGN_H
   gl_CONDITIONAL_HEADER([stdalign.h])
   AC_PROG_MKDIR_P
+  gl_C_BOOL
   gl_STDDEF_H
   gl_STDDEF_H_REQUIRE_DEFAULTS
   gl_CONDITIONAL_HEADER([stddef.h])
@@ -1214,6 +1220,7 @@ AC_DEFUN([gl_FILE_LIST], [
   lib/allocator.c
   lib/allocator.h
   lib/arg-nonnull.h
+  lib/assert.in.h
   lib/at-func.c
   lib/attribute.h
   lib/binary-io.c
@@ -1420,8 +1427,10 @@ AC_DEFUN([gl_FILE_LIST], [
   m4/absolute-header.m4
   m4/acl.m4
   m4/alloca.m4
+  m4/assert_h.m4
   m4/builtin-expect.m4
   m4/byteswap.m4
+  m4/c-bool.m4
   m4/canonicalize.m4
   m4/clock_time.m4
   m4/copy-file-range.m4
diff --git a/m4/nanosleep.m4 b/m4/nanosleep.m4
index 1964b1ea47..dfe21f56d5 100644
--- a/m4/nanosleep.m4
+++ b/m4/nanosleep.m4
@@ -1,4 +1,4 @@
-# serial 41
+# serial 42
 
 dnl From Jim Meyering.
 dnl Check for the nanosleep function.
@@ -100,15 +100,22 @@ AC_DEFUN([gl_FUNC_NANOSLEEP],
             #else /* A simpler test for native Windows.  */
             if (nanosleep (&ts_sleep, &ts_remaining) < 0)
               return 3;
+            /* Test for 32-bit mingw bug: negative nanosecond values do not
+               cause failure.  */
+            ts_sleep.tv_sec = 1;
+            ts_sleep.tv_nsec = -1;
+            if (nanosleep (&ts_sleep, &ts_remaining) != -1)
+              return 7;
             #endif
             return 0;
           }]])],
        [gl_cv_func_nanosleep=yes],
-       [case $? in dnl (
-        4|5|6) gl_cv_func_nanosleep='no (mishandles large arguments)';; dnl (
-        *)   gl_cv_func_nanosleep=no;;
+       [case $? in
+        4|5|6) gl_cv_func_nanosleep='no (mishandles large arguments)' ;;
+        7)     gl_cv_func_nanosleep='no (mishandles negative tv_nsec)' ;;
+        *)     gl_cv_func_nanosleep=no ;;
         esac],
-       [case "$host_os" in dnl ((
+       [case "$host_os" in
           linux*) # Guess it halfway works when the kernel is Linux.
             gl_cv_func_nanosleep='guessing no (mishandles large arguments)' ;;
           mingw*) # Guess no on native Windows.
diff --git a/m4/stdalign.m4 b/m4/stdalign.m4
index 78577cb2ac..324e91dae2 100644
--- a/m4/stdalign.m4
+++ b/m4/stdalign.m4
@@ -1,4 +1,4 @@
-# Check for stdalign.h that conforms to C11.
+# Check for alignas and alignof that conform to C23.
 
 dnl Copyright 2011-2022 Free Software Foundation, Inc.
 dnl This file is free software; the Free Software Foundation
@@ -9,12 +9,18 @@ dnl with or without modifications, as long as this notice is 
preserved.
 
 AC_DEFUN([gl_STDALIGN_H],
 [
-  AC_CACHE_CHECK([for working stdalign.h],
+  AC_CACHE_CHECK([for alignas and alignof],
     [gl_cv_header_working_stdalign_h],
-    [AC_COMPILE_IFELSE(
+    [gl_save_CFLAGS=$CFLAGS
+     for gl_working in "yes, keywords" "yes, <stdalign.h> macros"; do
+      AS_CASE([$gl_working],
+        [*stdalign.h*], [CFLAGS="$gl_save_CFLAGS -DINCLUDE_STDALIGN_H"])
+      AC_COMPILE_IFELSE(
        [AC_LANG_PROGRAM(
           [[#include <stdint.h>
-            #include <stdalign.h>
+            #ifdef INCLUDE_STDALIGN_H
+             #include <stdalign.h>
+            #endif
             #include <stddef.h>
 
             /* Test that alignof yields a result consistent with offsetof.
@@ -30,7 +36,7 @@ AC_DEFUN([gl_STDALIGN_H],
             char test_long[ao (long int) % _Alignof (long int) == 0 ? 1 : -1];
             char test_alignof[alignof (double) == _Alignof (double) ? 1 : -1];
 
-            /* Test _Alignas only on platforms where gnulib can help.  */
+            /* Test alignas only on platforms where gnulib can help.  */
             #if \
                 ((defined __cplusplus && 201103 <= __cplusplus) \
                  || (__TINYC__ && defined __attribute__) \
@@ -45,12 +51,84 @@ AC_DEFUN([gl_STDALIGN_H],
                                 ? 1 : -1];
             #endif
           ]])],
-       [gl_cv_header_working_stdalign_h=yes],
-       [gl_cv_header_working_stdalign_h=no])])
-
-  if test $gl_cv_header_working_stdalign_h = yes; then
-    GL_GENERATE_STDALIGN_H=false
-  else
-    GL_GENERATE_STDALIGN_H=true
-  fi
+       [gl_cv_header_working_stdalign_h=$gl_working],
+       [gl_cv_header_working_stdalign_h=no])
+
+      CFLAGS=$gl_save_CFLAGS
+      test "$gl_cv_header_working_stdalign_h" != no && break
+     done])
+
+  GL_GENERATE_STDALIGN_H=false
+  AS_CASE([$gl_cv_header_working_stdalign_h],
+    [no],
+      [GL_GENERATE_STDALIGN_H=true],
+    [yes*keyword*],
+      [AC_DEFINE([HAVE_C_ALIGNASOF], [1],
+         [Define to 1 if the alignas and alignof keywords work.])])
+
+  AC_CHECK_HEADERS_ONCE([stdalign.h])
+
+  dnl The "zz" puts this toward config.h's end, to avoid potential
+  dnl collisions with other definitions.
+  AH_VERBATIM([zzalignas],
+[#if !defined HAVE_C_ALIGNASOF && __cplusplus < 201103 && !defined alignof
+# if HAVE_STDALIGN_H
+#  include <stdalign.h>
+# else
+   /* Substitute.  Keep consistent with gnulib/lib/stdalign.in.h.  */
+#  ifndef _GL_STDALIGN_H
+#   define _GL_STDALIGN_H
+#   undef _Alignas
+#   undef _Alignof
+#   if (!defined __STDC_VERSION__ || __STDC_VERSION__ < 201112 \
+        || (defined __GNUC__ && __GNUC__ < 4 + (__GNUC_MINOR__ < 9) \
+            && !defined __clang__) \
+        || (defined __clang__ && __clang_major__ < 8))
+#    ifdef __cplusplus
+#     if (201103 <= __cplusplus || defined _MSC_VER)
+#      define _Alignof(type) alignof (type)
+#     else
+       template <class __t> struct __alignof_helper { char __a; __t __b; };
+#      define _Alignof(type) offsetof (__alignof_helper<type>, __b)
+#      define _GL_STDALIGN_NEEDS_STDDEF 1
+#     endif
+#    else
+#     define _Alignof(type) offsetof (struct { char __a; type __b; }, __b)
+#     define _GL_STDALIGN_NEEDS_STDDEF 1
+#    endif
+#   endif
+#   if ! (defined __cplusplus && (201103 <= __cplusplus || defined _MSC_VER))
+#    define alignof _Alignof
+#   endif
+#   define __alignof_is_defined 1
+#   if !defined __STDC_VERSION__ || __STDC_VERSION__ < 201112
+#    if defined __cplusplus && (201103 <= __cplusplus || defined _MSC_VER)
+#     define _Alignas(a) alignas (a)
+#    elif (!defined __attribute__ \
+           && ((defined __APPLE__ && defined __MACH__ \
+                ? 4 < __GNUC__ + (1 <= __GNUC_MINOR__) \
+                : __GNUC__ && !defined __ibmxl__) \
+               || (4 <= __clang_major__) \
+               || (__ia64 && (61200 <= __HP_cc || 61200 <= __HP_aCC)) \
+               || __ICC || 0x590 <= __SUNPRO_C || 0x0600 <= __xlC__))
+#     define _Alignas(a) __attribute__ ((__aligned__ (a)))
+#    elif 1300 <= _MSC_VER
+#     define _Alignas(a) __declspec (align (a))
+#    endif
+#   endif
+#   if ((defined _Alignas \
+         && !(defined __cplusplus && (201103 <= __cplusplus || defined 
_MSC_VER))) \
+        || (defined __STDC_VERSION__ && 201112 <= __STDC_VERSION__))
+#    define alignas _Alignas
+#   endif
+#   if (defined alignas \
+        || (defined __cplusplus && (201103 <= __cplusplus || defined 
_MSC_VER)))
+#    define __alignas_is_defined 1
+#   endif
+#   if _GL_STDALIGN_NEEDS_STDDEF
+#    include <stddef.h>
+#   endif
+#  endif /* _GL_STDALIGN_H */
+# endif
+#endif])
 ])
diff --git a/m4/time_h.m4 b/m4/time_h.m4
index 98d7b6e01b..4ac8fd0075 100644
--- a/m4/time_h.m4
+++ b/m4/time_h.m4
@@ -2,7 +2,7 @@
 
 # Copyright (C) 2000-2001, 2003-2007, 2009-2022 Free Software Foundation, Inc.
 
-# serial 19
+# serial 20
 
 # This file is free software; the Free Software Foundation
 # gives unlimited permission to copy and/or distribute it,
@@ -19,6 +19,12 @@ AC_DEFUN_ONCE([gl_TIME_H],
   gl_NEXT_HEADERS([time.h])
   AC_REQUIRE([gl_CHECK_TYPE_STRUCT_TIMESPEC])
 
+  dnl Check for declarations of anything we want to poison if the
+  dnl corresponding gnulib module is not in use.
+  gl_WARN_ON_USE_PREPARE([[
+#include <time.h>
+    ]], [asctime_r ctime_r])
+
   AC_REQUIRE([AC_C_RESTRICT])
 
   AC_CACHE_CHECK([for TIME_UTC in <time.h>],
diff --git a/msdos/sed2v2.inp b/msdos/sed2v2.inp
index 8728c8dac4..ff6be8d083 100644
--- a/msdos/sed2v2.inp
+++ b/msdos/sed2v2.inp
@@ -114,6 +114,7 @@ s/^#undef POINTER_TYPE *$/#define POINTER_TYPE void/
 #else\
 #undef HAVE_INTTYPES_H\
 #endif
+s/^#undef HAVE_STDBOOL_H/#define HAVE_STDBOOL_H 1/
 /^#undef HAVE_STDINT_H/c\
 #if __DJGPP__ > 2 || __DJGPP_MINOR__ > 3\
 #define HAVE_STDINT_H 1\
diff --git a/msdos/sedlibmk.inp b/msdos/sedlibmk.inp
index 79430bbaf1..3af0db6e0a 100644
--- a/msdos/sedlibmk.inp
+++ b/msdos/sedlibmk.inp
@@ -182,6 +182,7 @@ s/@PACKAGE@/emacs/
 /^GL_GNULIB_GETLOADAVG *=/s/@GL_GNULIB_GETLOADAVG@/1/
 /^GL_GNULIB_GETRANDOM *=/s/@GL_GNULIB_GETRANDOM@/1/
 /^GL_GNULIB_UNISTD_H_GETOPT *=/s/@GL_GNULIB_UNISTD_H_GETOPT@/1/
+/^GL_GNULIB_LCHMOD *=/s/@GL_GNULIB_LCHMOD@/1/
 /^GL_GNULIB_MEMMEM *=/s/@GL_GNULIB_MEMMEM@/1/
 /^GL_GNULIB_MEMRCHR *=/s/@GL_GNULIB_MEMRCHR@/1/
 /^GL_GNULIB_MEMPCPY *=/s/@GL_GNULIB_MEMPCPY@/1/
@@ -216,7 +217,8 @@ s/@PACKAGE@/emacs/
 /^HAVE_GETHOSTNAME *=/s/@HAVE_GETHOSTNAME@/1/
 /^HAVE_GETLOGIN *=/s/@HAVE_GETLOGIN@/1/
 /^HAVE_GETPAGESIZE *=/s/@HAVE_GETPAGESIZE@/1/
-/^HAVE_INTTYPES_H *=/s/@HAVE_INTTYPES_H@/HAVE_INTTYPES_H/
+/^HAVE_INTTYPES_H *=/s/@HAVE_INTTYPES_H@/1/
+/^HAVE_LCHMOD *=/s/@HAVE_LCHMOD@/0/
 /^HAVE_LINK *=/s/@HAVE_LINK@/1/
 /^HAVE_LONG_LONG_INT *=/s/@HAVE_LONG_LONG_INT@/1/
 /^HAVE_LSTAT *=/s/@HAVE_LSTAT@/HAVE_LSTAT/
@@ -293,7 +295,8 @@ s/@PACKAGE@/emacs/
 /^NEXT_AS_FIRST_DIRECTIVE_SYS_TIME_H *=/s/@[^@\n]*@//
 /^NEXT_AS_FIRST_DIRECTIVE_SYS_TYPES_H *=/s!@[^@\n]*@!<sys/types.h>!
 /^NEXT_AS_FIRST_DIRECTIVE_TIME_H *=/s/@[^@\n]*@/<time.h>/
-/^NEXT_AS_FIRST_DIRECTIVE_UNISTD_H *=/s/@[^@\n]*@/<unistd.h>/
+/^NEXT_ASSERT_H *=/s/@[^@\n]*@/<assert.h>/
+/^NEXT_DIRENT_H *=/s/@[^@\n]*@/<dirent.h>/
 /^NEXT_DIRENT_H *=/s/@[^@\n]*@/<dirent.h>/
 /^NEXT_ERRNO_H *=/s/@[^@\n]*@//
 /^NEXT_FCNTL_H *=/s/@[^@\n]*@/<fcntl.h>/
@@ -323,6 +326,7 @@ s/@PACKAGE@/emacs/
 /^LIB_GETRANDOM[^ =]* *= *@/s/@[^@\n]*@//
 /^SIG_ATOMIC_T_SUFFIX *=/s/@SIG_ATOMIC_T_SUFFIX@//
 /^SIZE_T_SUFFIX *=/s/@SIZE_T_SUFFIX@/u/
+/^ASSERT_H *=/s/@[^@\n]*@/assert.h/
 /^ALLOCA_H *=/s/@[^@\n]*@/alloca.h/
 /^BYTESWAP_H *=/s/@[^@\n]*@/byteswap.h/
 /^DIRENT_H *=/s/@[^@\n]*@//
@@ -412,6 +416,9 @@ s/^ -*test -z.*|| rm/        -rm/
 s/@echo /@djecho /
 #
 # Determine which headers to generate
+# DJGPP assert.h lacks static_assert, so assert.h will have to be
+# generated
+s/= @GL_GENERATE_ASSERT_H_CONDITION@/= 1/
 s/= @GL_GENERATE_ALLOCA_H_CONDITION@/= 1/
 s/= @GL_GENERATE_BYTESWAP_H_CONDITION@/= 1/
 s/= @GL_GENERATE_EXECINFO_H_CONDITION@/= 1/
@@ -497,6 +504,14 @@ s/\.in-h\;  *\\$/.in-h >> $@-t/
   s/'\; \\ *$/' >> $@-t/
   /< \$(srcdir)\/string\.in-h >>/d
 }
+/^assert\.h/,/^        \$(AM_V_AT)mv \$@-t \$@/{
+  s/\$(gl_V_at){/\$(gl_V_at)/
+  s/< \$(srcdir)\/assert\.in-h/& > $@-t/
+  s/     sed/  \$(gl_V_at) \$(SED_HEADER_STDOUT)\\\
+            /
+  s/\} > \$@-t/>> $@-t/
+  s/< \$(srcdir)\/verify\.h; \\/\$(srcdir)\/verify\.h >> \$@-t/
+}
 s!\$(MKDIR_P)[         ][      ]*sys!command.com /c "if not exist sys\\stat.h 
md sys"!
 /^     @for dir in/,/^[^        ]/c\
        -rm -rf $(MOSTLYCLEANDIRS)
diff --git a/nextstep/Makefile.in b/nextstep/Makefile.in
index 82bf13bc92..c1200f73fb 100644
--- a/nextstep/Makefile.in
+++ b/nextstep/Makefile.in
@@ -59,7 +59,7 @@ ${ns_appdir}: ${srcdir}/${ns_appsrc} ${ns_appsrc}
        ${MKDIR_P} ${ns_appdir}
        ( cd ${srcdir}/${ns_appsrc} ; tar cfh - . ) | \
          ( cd ${ns_appdir} ; umask 022; tar xf - )
-       [ "`cd ${srcdir} && /bin/pwd`" = "`/bin/pwd`" ] || \
+       [ "`cd ${srcdir} && pwd -P`" = "`pwd -P`" ] || \
          ( cd ${ns_appsrc} ; tar cfh - . ) | \
            ( cd ${ns_appdir} ; umask 022; tar xf - )
        touch ${ns_appdir}
diff --git a/nt/Makefile.in b/nt/Makefile.in
index c904e6d451..c5a9bf4363 100644
--- a/nt/Makefile.in
+++ b/nt/Makefile.in
@@ -163,8 +163,8 @@ $(DESTDIR)${archlibdir}: all
        @echo
        @echo "Installing utilities run internally by Emacs."
        umask 022; ${MKDIR_P} "$(DESTDIR)${archlibdir}"
-       exp_archlibdir=`cd "$(DESTDIR)${archlibdir}" && /bin/pwd`; \
-       if [ "$$exp_archlibdir" != "`/bin/pwd`" ]; then \
+       exp_archlibdir=`cd "$(DESTDIR)${archlibdir}" && pwd -P`; \
+       if [ "$$exp_archlibdir" != "`pwd -P`" ]; then \
          for file in ${UTILITIES}; do \
            $(INSTALL_PROGRAM) $(INSTALL_STRIP) $$file 
"$(DESTDIR)${archlibdir}/$$file" ; \
          done ; \
diff --git a/src/alloc.c b/src/alloc.c
index ced7c73cba..a8b57add60 100644
--- a/src/alloc.c
+++ b/src/alloc.c
@@ -5388,7 +5388,7 @@ void
 check_pure_size (void)
 {
   if (pure_bytes_used_before_overflow)
-    message (("emacs:0:Pure Lisp storage overflow (approx. %"pI"d"
+    message (("emacs:0:Pure Lisp storage overflow (approx. %jd"
              " bytes needed)"),
             pure_bytes_used + pure_bytes_used_before_overflow);
 }
diff --git a/src/bytecode.c b/src/bytecode.c
index d75767bb0c..c765e1be2b 100644
--- a/src/bytecode.c
+++ b/src/bytecode.c
@@ -1431,7 +1431,7 @@ exec_byte_code (Lisp_Object fun, ptrdiff_t args_template,
          NEXT;
 
        CASE (Binteractive_p):  /* Obsolete since 24.1.  */
-         PUSH (call0 (intern ("interactive-p")));
+         PUSH (call0 (Qinteractive_p));
          NEXT;
 
        CASE (Bforward_char):
@@ -1749,6 +1749,8 @@ get_byte_code_arity (Lisp_Object args_template)
 void
 syms_of_bytecode (void)
 {
+  DEFSYM (Qinteractive_p, "interactive-p");
+
   defsubr (&Sbyte_code);
   defsubr (&Sinternal_stack_stats);
 
diff --git a/src/character.c b/src/character.c
index 968daccafa..5df49adade 100644
--- a/src/character.c
+++ b/src/character.c
@@ -178,12 +178,16 @@ usage: (characterp OBJECT)  */
   return (CHARACTERP (object) ? Qt : Qnil);
 }
 
-DEFUN ("max-char", Fmax_char, Smax_char, 0, 0, 0,
-       doc: /* Return the character of the maximum code.  */
+DEFUN ("max-char", Fmax_char, Smax_char, 0, 1, 0,
+       doc: /* Return the maximum character code.
+If UNICODE is non-nil, return the maximum character code defined
+by the Unicode Standard.  */
        attributes: const)
-  (void)
+  (Lisp_Object unicode)
 {
-  return make_fixnum (MAX_CHAR);
+  return (!NILP (unicode)
+         ? make_fixnum (MAX_UNICODE_CHAR)
+         : make_fixnum (MAX_CHAR));
 }
 
 DEFUN ("unibyte-char-to-multibyte", Funibyte_char_to_multibyte,
diff --git a/src/coding.c b/src/coding.c
index 0ae8eb3282..ab73bda844 100644
--- a/src/coding.c
+++ b/src/coding.c
@@ -12014,9 +12014,9 @@ See also the function `find-operation-coding-system'.  
*/);
   Vnetwork_coding_system_alist = Qnil;
 
   DEFVAR_LISP ("locale-coding-system", Vlocale_coding_system,
-              doc: /* Coding system to use with system messages.
-Also used for decoding keyboard input on X Window system, and for
-encoding standard output and error streams.  */);
+    doc: /* Coding system to use with system messages.
+Potentially also used for decoding keyboard input on X Windows, and is
+used for encoding standard output and error streams.  */);
   Vlocale_coding_system = Qnil;
 
   /* The eol mnemonics are reset in startup.el system-dependently.  */
diff --git a/src/comp.c b/src/comp.c
index 70e7d5a8bb..b7541c5d9f 100644
--- a/src/comp.c
+++ b/src/comp.c
@@ -68,6 +68,7 @@ along with GNU Emacs.  If not, see 
<https://www.gnu.org/licenses/>.  */
 #undef gcc_jit_context_get_type
 #undef gcc_jit_context_new_array_access
 #undef gcc_jit_context_new_array_type
+#undef gcc_jit_context_new_bitcast
 #undef gcc_jit_context_new_binary_op
 #undef gcc_jit_context_new_call
 #undef gcc_jit_context_new_call_through_ptr
@@ -108,6 +109,7 @@ along with GNU Emacs.  If not, see 
<https://www.gnu.org/licenses/>.  */
 #undef gcc_jit_struct_set_fields
 #undef gcc_jit_type_get_const
 #undef gcc_jit_type_get_pointer
+#undef gcc_jit_type_is_pointer
 #undef gcc_jit_version_major
 #undef gcc_jit_version_minor
 #undef gcc_jit_version_patchlevel
@@ -180,8 +182,13 @@ DEF_DLL_FN (gcc_jit_rvalue *, 
gcc_jit_context_new_call_through_ptr,
             (gcc_jit_context *ctxt, gcc_jit_location *loc,
              gcc_jit_rvalue *fn_ptr, int numargs, gcc_jit_rvalue **args));
 DEF_DLL_FN (gcc_jit_rvalue *, gcc_jit_context_new_cast,
+            (gcc_jit_context * ctxt, gcc_jit_location *loc,
+             gcc_jit_rvalue *rvalue, gcc_jit_type *type));
+#ifdef LIBGCCJIT_HAVE_gcc_jit_context_new_bitcast
+DEF_DLL_FN (gcc_jit_rvalue *, gcc_jit_context_new_bitcast,
             (gcc_jit_context *ctxt, gcc_jit_location *loc,
              gcc_jit_rvalue *rvalue, gcc_jit_type *type));
+#endif
 DEF_DLL_FN (gcc_jit_rvalue *, gcc_jit_context_new_comparison,
             (gcc_jit_context *ctxt, gcc_jit_location *loc,
              enum gcc_jit_comparison op, gcc_jit_rvalue *a, gcc_jit_rvalue 
*b));
@@ -224,6 +231,9 @@ DEF_DLL_FN (gcc_jit_type *, gcc_jit_struct_as_type,
             (gcc_jit_struct *struct_type));
 DEF_DLL_FN (gcc_jit_type *, gcc_jit_type_get_const, (gcc_jit_type *type));
 DEF_DLL_FN (gcc_jit_type *, gcc_jit_type_get_pointer, (gcc_jit_type *type));
+#ifdef LIBGCCJIT_HAVE_REFLECTION
+DEF_DLL_FN (gcc_jit_type *, gcc_jit_type_is_pointer, (gcc_jit_type *type));
+#endif
 DEF_DLL_FN (void, gcc_jit_block_add_assignment,
             (gcc_jit_block *block, gcc_jit_location *loc, gcc_jit_lvalue 
*lvalue,
              gcc_jit_rvalue *rvalue));
@@ -293,6 +303,9 @@ init_gccjit_functions (void)
   LOAD_DLL_FN (library, gcc_jit_context_get_type);
   LOAD_DLL_FN (library, gcc_jit_context_new_array_access);
   LOAD_DLL_FN (library, gcc_jit_context_new_array_type);
+#ifdef LIBGCCJIT_HAVE_gcc_jit_context_new_bitcast
+  LOAD_DLL_FN (library, gcc_jit_context_new_bitcast);
+#endif
   LOAD_DLL_FN (library, gcc_jit_context_new_binary_op);
   LOAD_DLL_FN (library, gcc_jit_context_new_call);
   LOAD_DLL_FN (library, gcc_jit_context_new_call_through_ptr);
@@ -334,6 +347,9 @@ init_gccjit_functions (void)
   LOAD_DLL_FN (library, gcc_jit_struct_set_fields);
   LOAD_DLL_FN (library, gcc_jit_type_get_const);
   LOAD_DLL_FN (library, gcc_jit_type_get_pointer);
+#ifdef LIBGCCJIT_HAVE_REFLECTION
+  LOAD_DLL_FN (library, gcc_jit_type_is_pointer);
+#endif
   LOAD_DLL_FN_OPT (library, gcc_jit_context_add_command_line_option);
   LOAD_DLL_FN_OPT (library, gcc_jit_context_add_driver_option);
 #if defined (LIBGCCJIT_HAVE_gcc_jit_global_set_initializer)
@@ -368,6 +384,9 @@ init_gccjit_functions (void)
 #define gcc_jit_context_get_type fn_gcc_jit_context_get_type
 #define gcc_jit_context_new_array_access fn_gcc_jit_context_new_array_access
 #define gcc_jit_context_new_array_type fn_gcc_jit_context_new_array_type
+#ifdef LIBGCCJIT_HAVE_gcc_jit_context_new_bitcast
+# define gcc_jit_context_new_bitcast fn_gcc_jit_context_new_bitcast
+#endif
 #define gcc_jit_context_new_binary_op fn_gcc_jit_context_new_binary_op
 #define gcc_jit_context_new_call fn_gcc_jit_context_new_call
 #define gcc_jit_context_new_call_through_ptr 
fn_gcc_jit_context_new_call_through_ptr
@@ -410,6 +429,9 @@ init_gccjit_functions (void)
 #define gcc_jit_rvalue_get_type fn_gcc_jit_rvalue_get_type
 #define gcc_jit_struct_as_type fn_gcc_jit_struct_as_type
 #define gcc_jit_struct_set_fields fn_gcc_jit_struct_set_fields
+#ifdef LIBGCCJIT_HAVE_REFLECTION
+# define gcc_jit_type_is_pointer fn_gcc_jit_type_is_pointer
+#endif
 #define gcc_jit_type_get_const fn_gcc_jit_type_get_const
 #define gcc_jit_type_get_pointer fn_gcc_jit_type_get_pointer
 #if defined (LIBGCCJIT_HAVE_gcc_jit_version)
@@ -518,7 +540,9 @@ typedef struct {
 
 static f_reloc_t freloc;
 
-#define NUM_CAST_TYPES 15
+#ifndef LIBGCCJIT_HAVE_gcc_jit_context_new_bitcast
+# define NUM_CAST_TYPES 15
+#endif
 
 typedef struct {
   EMACS_INT len;
@@ -593,13 +617,15 @@ typedef struct {
   gcc_jit_rvalue *current_thread_ref;
   /* Other globals.  */
   gcc_jit_rvalue *pure_ptr;
-  /* libgccjit has really limited support for casting therefore this union will
-     be used for the scope.  */
+#ifndef LIBGCCJIT_HAVE_gcc_jit_context_new_bitcast
+  /* This version of libgccjit has really limited support for casting
+     therefore this union will be used for the scope.  */
   gcc_jit_type *cast_union_type;
   gcc_jit_function *cast_functions_from_to[NUM_CAST_TYPES][NUM_CAST_TYPES];
   gcc_jit_function *cast_ptr_to_int;
   gcc_jit_function *cast_int_to_ptr;
   gcc_jit_type *cast_types[NUM_CAST_TYPES];
+#endif
   gcc_jit_function *func; /* Current function being compiled.  */
   bool func_has_non_local; /* From comp-func has-non-local slot.  */
   EMACS_INT func_speed; /* From comp-func speed slot.  */
@@ -1100,6 +1126,7 @@ emit_cond_jump (gcc_jit_rvalue *test,
 
 }
 
+#ifndef LIBGCCJIT_HAVE_gcc_jit_context_new_bitcast
 static int
 type_to_cast_index (gcc_jit_type * type)
 {
@@ -1109,6 +1136,7 @@ type_to_cast_index (gcc_jit_type * type)
 
   xsignal1 (Qnative_ice, build_string ("unsupported cast"));
 }
+#endif
 
 static gcc_jit_rvalue *
 emit_coerce (gcc_jit_type *new_type, gcc_jit_rvalue *obj)
@@ -1145,14 +1173,48 @@ emit_coerce (gcc_jit_type *new_type, gcc_jit_rvalue 
*obj)
     }
 #endif
 
+#ifdef LIBGCCJIT_HAVE_gcc_jit_context_new_bitcast
+  bool old_is_ptr = gcc_jit_type_is_pointer (old_type) != NULL;
+  bool new_is_ptr = gcc_jit_type_is_pointer (new_type) != NULL;
+
+  gcc_jit_rvalue *tmp = obj;
+
+  /* `gcc_jit_context_new_bitcast` requires that the types being converted
+     between have the same layout and as such, doesn't allow converting
+     between an arbitrarily sized integer/boolean and a pointer. Casting it
+     to a uintptr/void* is still necessary, to ensure that it can be bitcast
+     into a (void *)/uintptr respectively.  */
+  if (old_is_ptr != new_is_ptr)
+    {
+      if (old_is_ptr)
+       {
+         tmp = gcc_jit_context_new_cast (comp.ctxt, NULL, tmp,
+                                         comp.void_ptr_type);
+         tmp = gcc_jit_context_new_bitcast (comp.ctxt, NULL, tmp,
+                                            comp.uintptr_type);
+       }
+      else
+       {
+         tmp = gcc_jit_context_new_cast (comp.ctxt, NULL, tmp,
+                                         comp.uintptr_type);
+         tmp = gcc_jit_context_new_bitcast (comp.ctxt, NULL, tmp,
+                                            comp.void_ptr_type);
+       }
+    }
+  return gcc_jit_context_new_cast (comp.ctxt, NULL, tmp, new_type);
+
+#else /* !LIBGCCJIT_HAVE_gcc_jit_context_new_bitcast */
+
   int old_index = type_to_cast_index (old_type);
   int new_index = type_to_cast_index (new_type);
 
   /* Lookup the appropriate cast function in the cast matrix.  */
   return gcc_jit_context_new_call (comp.ctxt,
-           NULL,
-           comp.cast_functions_from_to[old_index][new_index],
-           1, &obj);
+                                  NULL,
+                                  comp.cast_functions_from_to
+                                  [old_index][new_index],
+                                  1, &obj);
+#endif
 }
 
 static gcc_jit_rvalue *
@@ -3318,6 +3380,7 @@ define_thread_state_struct (void)
     gcc_jit_type_get_pointer (gcc_jit_struct_as_type (comp.thread_state_s));
 }
 
+#ifndef LIBGCCJIT_HAVE_gcc_jit_context_new_bitcast
 static gcc_jit_function *
 define_type_punning (const char *name,
                     gcc_jit_type *from, gcc_jit_field *from_field,
@@ -3451,6 +3514,7 @@ define_cast_functions (void)
                                              comp.void_ptr_type,
                                              cast_union_fields[0]);
 
+
   for (int i = 0; i < NUM_CAST_TYPES; ++i)
     comp.cast_types[i] = cast_types[i].type;
 
@@ -3460,6 +3524,7 @@ define_cast_functions (void)
         comp.cast_functions_from_to[i][j] =
           define_cast_from_to (cast_types[i], cast_types[j]);
 }
+#endif /* !LIBGCCJIT_HAVE_gcc_jit_context_new_bitcast */
 
 static void
 define_CHECK_TYPE (void)
@@ -4467,7 +4532,7 @@ the latter is supposed to be used by the Emacs build 
procedure.  */)
        }
       if (NILP (base_dir))
        error ("Cannot find suitable directory for output in "
-              "`comp-native-load-path'.");
+              "`native-comp-eln-load-path'.");
     }
 
   if (!file_name_absolute_p (SSDATA (base_dir)))
@@ -4660,7 +4725,9 @@ Return t on success.  */)
   define_jmp_buf ();
   define_handler_struct ();
   define_thread_state_struct ();
+#ifndef LIBGCCJIT_HAVE_gcc_jit_context_new_bitcast
   define_cast_functions ();
+#endif
 
   return Qt;
 }
@@ -5107,6 +5174,7 @@ maybe_defer_native_compilation (Lisp_Object function_name,
     return;
 
   if (!native_comp_deferred_compilation
+      || !NILP (Vinhibit_automatic_native_compilation)
       || noninteractive
       || !NILP (Vpurify_flag)
       || !COMPILEDP (definition)
@@ -5610,6 +5678,14 @@ For internal use.  */);
               doc: /* Non-nil when comp.el can be native compiled.
 For internal use. */);
   /* Compiler control customizes.  */
+  DEFVAR_LISP ("inhibit-automatic-native-compilation",
+              Vinhibit_automatic_native_compilation,
+              doc: /* If non-nil, inhibit automatic native compilation of 
loaded .elc files.
+
+After compilation, each function definition is updated to the native
+compiled one.  */);
+  Vinhibit_automatic_native_compilation = Qnil;
+
   DEFVAR_BOOL ("native-comp-deferred-compilation",
               native_comp_deferred_compilation,
               doc: /* If non-nil compile loaded .elc files asynchronously.
diff --git a/src/composite.c b/src/composite.c
index 22422cca09..6b256171ac 100644
--- a/src/composite.c
+++ b/src/composite.c
@@ -800,6 +800,53 @@ composition_gstring_width (Lisp_Object gstring, ptrdiff_t 
from, ptrdiff_t to,
   return width;
 }
 
+/* Adjust the width of each grapheme cluster of GSTRING because
+   zero-width grapheme clusters are not displayed.  If the width is
+   zero, then the width of the last glyph in the cluster is
+   incremented.  */
+
+void
+composition_gstring_adjust_zero_width (Lisp_Object gstring)
+{
+  ptrdiff_t from = 0;
+  int width = 0;
+
+  for (ptrdiff_t i = 0; ; i++)
+    {
+      Lisp_Object glyph;
+
+      if (i < LGSTRING_GLYPH_LEN (gstring))
+       glyph = LGSTRING_GLYPH (gstring, i);
+      else
+       glyph = Qnil;
+
+      if (NILP (glyph) || from != LGLYPH_FROM (glyph))
+       {
+         eassert (i > 0);
+         Lisp_Object last = LGSTRING_GLYPH (gstring, i - 1);
+
+         if (width == 0)
+           {
+             if (NILP (LGLYPH_ADJUSTMENT (last)))
+               LGLYPH_SET_ADJUSTMENT (last,
+                                      CALLN (Fvector,
+                                             make_fixnum (0), make_fixnum (0),
+                                             make_fixnum (LGLYPH_WIDTH (last)
+                                                          + 1)));
+             else
+               ASET (LGLYPH_ADJUSTMENT (last), 2,
+                     make_fixnum (LGLYPH_WADJUST (last) + 1));
+           }
+         if (NILP (glyph))
+           break;
+         from = LGLYPH_FROM (glyph);
+         width = 0;
+       }
+      width += (NILP (LGLYPH_ADJUSTMENT (glyph))
+               ? LGLYPH_WIDTH (glyph) : LGLYPH_WADJUST (glyph));
+    }
+}
+
 
 static Lisp_Object gstring_work;
 static Lisp_Object gstring_work_headers;
@@ -876,7 +923,8 @@ fill_gstring_body (Lisp_Object gstring)
        }
       LGLYPH_SET_ADJUSTMENT (g, Qnil);
     }
-  if (i < LGSTRING_GLYPH_LEN (gstring))
+  len = LGSTRING_GLYPH_LEN (gstring);
+  for (; i < len; i++)
     LGSTRING_SET_GLYPH (gstring, i, Qnil);
 }
 
diff --git a/src/composite.h b/src/composite.h
index d77dd0d506..8a6fd203d0 100644
--- a/src/composite.h
+++ b/src/composite.h
@@ -340,6 +340,7 @@ extern Lisp_Object composition_gstring_from_id (ptrdiff_t);
 extern bool composition_gstring_p (Lisp_Object);
 extern int composition_gstring_width (Lisp_Object, ptrdiff_t, ptrdiff_t,
                                       struct font_metrics *);
+extern void composition_gstring_adjust_zero_width (Lisp_Object);
 
 extern bool find_automatic_composition (ptrdiff_t, ptrdiff_t, ptrdiff_t,
                                        ptrdiff_t *, ptrdiff_t *,
diff --git a/src/conf_post.h b/src/conf_post.h
index 6ecebf36ab..fb8d2e5d96 100644
--- a/src/conf_post.h
+++ b/src/conf_post.h
@@ -30,14 +30,10 @@ along with GNU Emacs.  If not, see 
<https://www.gnu.org/licenses/>.  */
 #endif
 
 /* To help make dependencies clearer elsewhere, this file typically
-   does not #include other files.  The exceptions are stdbool.h
-   because it is unlikely to interfere with configuration and bool is
-   such a core part of the C language, and ms-w32.h (DOS_NT
+   does not #include other files.  The exception is ms-w32.h (DOS_NT
    only) because it historically was included here and changing that
    would take some work.  */
 
-#include <stdbool.h>
-
 #if defined WINDOWSNT && !defined DEFER_MS_W32_H
 # include <ms-w32.h>
 #endif
diff --git a/src/data.c b/src/data.c
index 6921232665..c6b85e17bc 100644
--- a/src/data.c
+++ b/src/data.c
@@ -4130,6 +4130,7 @@ syms_of_data (void)
   DEFSYM (Qsymbolp, "symbolp");
   DEFSYM (Qfixnump, "fixnump");
   DEFSYM (Qintegerp, "integerp");
+  DEFSYM (Qbooleanp, "booleanp");
   DEFSYM (Qnatnump, "natnump");
   DEFSYM (Qwholenump, "wholenump");
   DEFSYM (Qstringp, "stringp");
diff --git a/src/dbusbind.c b/src/dbusbind.c
index 943a4aff8e..1c74180f15 100644
--- a/src/dbusbind.c
+++ b/src/dbusbind.c
@@ -398,7 +398,7 @@ xd_signature (char *signature, int dtype, int parent_type, 
Lisp_Object object)
     case DBUS_TYPE_BOOLEAN:
       /* There must be an argument.  */
       if (EQ (QCboolean, object))
-       wrong_type_argument (intern ("booleanp"), object);
+       wrong_type_argument (Qbooleanp, object);
       sprintf (signature, "%c", dtype);
       break;
 
@@ -649,7 +649,7 @@ xd_append_arg (int dtype, Lisp_Object object, 
DBusMessageIter *iter)
       case DBUS_TYPE_BOOLEAN:
        /* There must be an argument.  */
        if (EQ (QCboolean, object))
-         wrong_type_argument (intern ("booleanp"), object);
+         wrong_type_argument (Qbooleanp, object);
        {
          dbus_bool_t val = (NILP (object)) ? FALSE : TRUE;
          XD_DEBUG_MESSAGE ("%c %s", dtype, (val == FALSE) ? "false" : "true");
diff --git a/src/dispnew.c b/src/dispnew.c
index 53a47c4b2f..2568ba1086 100644
--- a/src/dispnew.c
+++ b/src/dispnew.c
@@ -1810,9 +1810,12 @@ allocate_matrices_for_window_redisplay (struct window *w)
          if (w->desired_matrix == NULL)
            {
              w->desired_matrix = new_glyph_matrix (NULL);
-             w->current_matrix = new_glyph_matrix (NULL);
+             eassert (w->current_matrix == NULL);
            }
 
+         if (w->current_matrix == NULL)
+           w->current_matrix = new_glyph_matrix (NULL);
+
          dim.width = required_matrix_width (w);
          dim.height = required_matrix_height (w);
          adjust_glyph_matrix (w, w->desired_matrix, 0, 0, dim);
@@ -4929,7 +4932,9 @@ update_frame_1 (struct frame *f, bool force_p, bool 
inhibit_id_p,
     {
       if (MATRIX_ROW_ENABLED_P (desired_matrix, i))
        {
-         if (FRAME_TERMCAP_P (f))
+         /* Note that output_buffer_size being 0 means that we want the
+            old default behavior of flushing output every now and then.  */
+         if (FRAME_TERMCAP_P (f) && FRAME_TTY (f)->output_buffer_size == 0)
            {
              /* Flush out every so many lines.
                 Also flush out if likely to have more than 1k buffered
@@ -6504,9 +6509,6 @@ init_display_interactive (void)
   if (!inhibit_window_system && display_arg)
     {
       Vinitial_window_system = Qx;
-#ifdef HAVE_X11
-      Vwindow_system_version = make_fixnum (11);
-#endif
 #ifdef USE_NCURSES
       /* In some versions of ncurses,
         tputs crashes if we have not called tgetent.
@@ -6521,7 +6523,6 @@ init_display_interactive (void)
   if (!inhibit_window_system)
     {
       Vinitial_window_system = Qw32;
-      Vwindow_system_version = make_fixnum (1);
       return;
     }
 #endif /* HAVE_NTGUI */
@@ -6530,7 +6531,6 @@ init_display_interactive (void)
   if (!inhibit_window_system && !will_dump_p ())
     {
       Vinitial_window_system = Qns;
-      Vwindow_system_version = make_fixnum (10);
       return;
     }
 #endif
@@ -6539,7 +6539,6 @@ init_display_interactive (void)
   if (!inhibit_window_system && !will_dump_p ())
     {
       Vinitial_window_system = Qpgtk;
-      Vwindow_system_version = make_fixnum (3);
       return;
     }
 #endif
@@ -6548,7 +6547,6 @@ init_display_interactive (void)
   if (!inhibit_window_system && !will_dump_p ())
     {
       Vinitial_window_system = Qhaiku;
-      Vwindow_system_version = make_fixnum (1);
       return;
     }
 #endif
@@ -6766,10 +6764,6 @@ Use of this variable as a boolean is deprecated.  
Instead,
 use `display-graphic-p' or any of the other `display-*-p'
 predicates which report frame's specific UI-related capabilities.  */);
 
-  DEFVAR_LISP ("window-system-version", Vwindow_system_version,
-              doc: /* The version number of the window system in use.
-For X windows, this is 11.  */);
-
   DEFVAR_BOOL ("cursor-in-echo-area", cursor_in_echo_area,
               doc: /* Non-nil means put cursor in minibuffer, at end of any 
message there.  */);
 
@@ -6817,5 +6811,4 @@ static void
 syms_of_display_for_pdumper (void)
 {
   Vinitial_window_system = Qnil;
-  Vwindow_system_version = Qnil;
 }
diff --git a/src/doc.c b/src/doc.c
index 34b80d03aa..67a5f845b9 100644
--- a/src/doc.c
+++ b/src/doc.c
@@ -342,7 +342,7 @@ string is passed through `substitute-command-keys'.  */)
     doc = module_function_documentation (XMODULE_FUNCTION (fun));
 #endif
   else
-    doc = call1 (intern ("function-documentation"), fun);
+    doc = call1 (Qfunction_documentation, fun);
 
   /* If DOC is 0, it's typically because of a dumped file missing
      from the DOC file (bug in src/Makefile.in).  */
@@ -643,7 +643,14 @@ default_to_grave_quoting_style (void)
 DEFUN ("text-quoting-style", Ftext_quoting_style,
        Stext_quoting_style, 0, 0, 0,
        doc: /* Return the current effective text quoting style.
-See variable `text-quoting-style'.  */)
+If the variable `text-quoting-style' is `grave', `straight' or
+`curve', just return that value.  If it is nil (the default), return
+`grave' if curved quotes cannot be displayed (for instance, on a
+terminal with no support for these characters), otherwise return
+`quote'.  Any other value is treated as `grave'.
+
+Note that in contrast to the variable `text-quoting-style', this
+function will never return nil.  */)
   (void)
 {
   /* Use grave accent and apostrophe `like this'.  */
@@ -694,7 +701,11 @@ The value should be one of these symbols:
   `grave':    quote with grave accent and apostrophe \\=`like this\\=';
              i.e., do not alter the original quote marks.
   nil:        like `curve' if curved single quotes are displayable,
-             and like `grave' otherwise.  This is the default.  */);
+             and like `grave' otherwise.  This is the default.
+
+You should never read the value of this variable directly from a Lisp
+program.  Use the function `text-quoting-style' instead, as that will
+compute the correct value for the current terminal in the nil case.  */);
   Vtext_quoting_style = Qnil;
 
   DEFVAR_BOOL ("internal--text-quoting-flag", text_quoting_flag,
diff --git a/src/dynlib.h b/src/dynlib.h
index 03b8f98356..9a11c12898 100644
--- a/src/dynlib.h
+++ b/src/dynlib.h
@@ -21,7 +21,6 @@ along with GNU Emacs.  If not, see 
<https://www.gnu.org/licenses/>.  */
 #define DYNLIB_H
 
 #include <attribute.h>
-#include <stdbool.h>
 
 typedef void *dynlib_handle_ptr;
 dynlib_handle_ptr dynlib_open (const char *path);
diff --git a/src/editfns.c b/src/editfns.c
index cd5cddee79..c1414071c7 100644
--- a/src/editfns.c
+++ b/src/editfns.c
@@ -3552,7 +3552,9 @@ styled_format (ptrdiff_t nargs, Lisp_Object *args, bool 
message)
                      || conversion == 'o' || conversion == 'x'
                      || conversion == 'X'))
            error ("Invalid format operation %%%c",
-                  STRING_CHAR ((unsigned char *) format - 1));
+                  multibyte_format
+                  ? STRING_CHAR ((unsigned char *) format - 1)
+                  : *((unsigned char *) format - 1));
          else if (! (FIXNUMP (arg) || ((BIGNUMP (arg) || FLOATP (arg))
                                        && conversion != 'c')))
            error ("Format specifier doesn't match argument type");
@@ -4603,10 +4605,7 @@ it to be non-nil.  */);
 
   DEFSYM (Qrestrictions_locked, "restrictions-locked");
   DEFVAR_LISP ("restrictions-locked", Vrestrictions_locked,
-              doc: /* If non-nil, restrictions are currently locked.
-
-This happens when `narrow-to-region', which see, is called from Lisp
-with an optional argument LOCK non-nil.  */);
+              doc: /* If non-nil, restrictions are currently locked.  */);
   Vrestrictions_locked = Qnil;
   Funintern (Qrestrictions_locked, Qnil);
 
diff --git a/src/emacs-module.c b/src/emacs-module.c
index 1c392d65df..fcdf103c19 100644
--- a/src/emacs-module.c
+++ b/src/emacs-module.c
@@ -78,7 +78,6 @@ To add a new module function, proceed as follows:
 #include "emacs-module.h"
 
 #include <stdarg.h>
-#include <stdbool.h>
 #include <stddef.h>
 #include <stdint.h>
 #include <stdlib.h>
diff --git a/src/emacs-module.h.in b/src/emacs-module.h.in
index 6642b55d93..bef89b059f 100644
--- a/src/emacs-module.h.in
+++ b/src/emacs-module.h.in
@@ -30,7 +30,8 @@ information how to write modules and use this header file.
 #include <stdint.h>
 #include <time.h>
 
-#ifndef __cplusplus
+#if ((defined __STDC_VERSION__ ? __STDC_VERSION__ : 0) < 202311 \
+     && !defined __bool_true_false_are_defined && !defined __cplusplus)
 #include <stdbool.h>
 #endif
 
diff --git a/src/emacs.c b/src/emacs.c
index 8f19c48655..ba8b9c651a 100644
--- a/src/emacs.c
+++ b/src/emacs.c
@@ -303,7 +303,7 @@ Initialization options:\n\
 -x                          to be used in #!/usr/bin/emacs -x\n\
                               and has approximately the same meaning\n\
                              as -Q --script\n\
---terminal, -t DEVICE       use DEVICE for terminal I/O\n              \
+--terminal, -t DEVICE       use DEVICE for terminal I/O\n\
 --user, -u USER             load ~USER/.emacs instead of your own\n\
 \n\
 ",
@@ -895,19 +895,17 @@ load_pdump (int argc, char **argv)
     }
 
   /* Where's our executable?  */
-  ptrdiff_t bufsize;
-#ifndef NS_SELF_CONTAINED
-  ptrdiff_t exec_bufsize;
-#endif
-  emacs_executable = find_emacs_executable (argv[0], &bufsize);
-#ifndef NS_SELF_CONTAINED
-  exec_bufsize = bufsize;
-#endif
+  ptrdiff_t exec_bufsize, bufsize, needed;
+  emacs_executable = find_emacs_executable (argv[0], &exec_bufsize);
 
   /* If we couldn't find our executable, go straight to looking for
      the dump in the hardcoded location.  */
   if (!(emacs_executable && *emacs_executable))
-    goto hardcoded;
+    {
+      bufsize = 0;
+      dump_file = NULL;
+      goto hardcoded;
+    }
 
   if (dump_file)
     {
@@ -935,8 +933,8 @@ load_pdump (int argc, char **argv)
                      strip_suffix_length))
        exenamelen = prefix_length;
     }
-  ptrdiff_t needed = exenamelen + strlen (suffix) + 1;
-  dump_file = xpalloc (NULL, &bufsize, needed - bufsize, -1, 1);
+  bufsize = exenamelen + strlen (suffix) + 1;
+  dump_file = xpalloc (NULL, &bufsize, 1, -1, 1);
   memcpy (dump_file, emacs_executable, exenamelen);
   strcpy (dump_file + exenamelen, suffix);
   result = pdumper_load (dump_file, emacs_executable);
diff --git a/src/eval.c b/src/eval.c
index 6ea7a473f6..de9c07f155 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -211,15 +211,8 @@ backtrace_thread_next (struct thread_state *tstate, union 
specbinding *pdl)
 void
 init_eval_once (void)
 {
-  /* Don't forget to update docs (lispref node "Local Variables").  */
-#ifndef HAVE_NATIVE_COMP
-  max_specpdl_size = 1800; /* See bug#46818.  */
-  max_lisp_eval_depth = 800;
-#else
-  /* Original values increased for comp.el.  */
-  max_specpdl_size = 2500;
+  /* Don't forget to update docs (lispref node "Eval").  */
   max_lisp_eval_depth = 1600;
-#endif
   Vrun_hooks = Qnil;
   pdumper_do_now_and_after_load (init_eval_once_for_pdumper);
 }
@@ -270,8 +263,7 @@ max_ensure_room (intmax_t *m, intmax_t a, intmax_t b)
 static void
 restore_stack_limits (Lisp_Object data)
 {
-  integer_to_intmax (XCAR (data), &max_specpdl_size);
-  integer_to_intmax (XCDR (data), &max_lisp_eval_depth);
+  integer_to_intmax (data, &max_lisp_eval_depth);
 }
 
 /* Call the Lisp debugger, giving it argument ARG.  */
@@ -283,9 +275,6 @@ call_debugger (Lisp_Object arg)
   specpdl_ref count = SPECPDL_INDEX ();
   Lisp_Object val;
   intmax_t old_depth = max_lisp_eval_depth;
-  /* Do not allow max_specpdl_size less than actual depth (Bug#16603).  */
-  ptrdiff_t counti = specpdl_ref_to_count (count);
-  intmax_t old_max = max (max_specpdl_size, counti);
 
   /* The previous value of 40 is too small now that the debugger
      prints using cl-prin1 instead of prin1.  Printing lists nested 8
@@ -293,20 +282,8 @@ call_debugger (Lisp_Object arg)
      currently requires 77 additional frames.  See bug#31919.  */
   max_ensure_room (&max_lisp_eval_depth, lisp_eval_depth, 100);
 
-  /* While debugging Bug#16603, previous value of 100 was found
-     too small to avoid specpdl overflow in the debugger itself.  */
-  max_ensure_room (&max_specpdl_size, counti, 200);
-
-  if (old_max == counti)
-    {
-      /* We can enter the debugger due to specpdl overflow (Bug#16603).  */
-      specpdl_ptr--;
-      grow_specpdl ();
-    }
-
   /* Restore limits after leaving the debugger.  */
-  record_unwind_protect (restore_stack_limits,
-                        Fcons (make_int (old_max), make_int (old_depth)));
+  record_unwind_protect (restore_stack_limits, make_int (old_depth));
 
 #ifdef HAVE_WINDOW_SYSTEM
   if (display_hourglass_p)
@@ -938,12 +915,9 @@ usage: (let* VARLIST BODY...)  */)
   lexenv = Vinternal_interpreter_environment;
 
   Lisp_Object varlist = XCAR (args);
-  while (CONSP (varlist))
+  FOR_EACH_TAIL (varlist)
     {
-      maybe_quit ();
-
       elt = XCAR (varlist);
-      varlist = XCDR (varlist);
       if (SYMBOLP (elt))
        {
          var = elt;
@@ -1757,8 +1731,6 @@ signal_or_quit (Lisp_Object error_symbol, Lisp_Object 
data, bool keyboard_quit)
     {
       /* Edebug takes care of restoring these variables when it exits.  */
       max_ensure_room (&max_lisp_eval_depth, lisp_eval_depth, 20);
-      ptrdiff_t counti = specpdl_ref_to_count (SPECPDL_INDEX ());
-      max_ensure_room (&max_specpdl_size, counti, 40);
 
       call2 (Vsignal_hook_function, error_symbol, data);
     }
@@ -1827,8 +1799,6 @@ signal_or_quit (Lisp_Object error_symbol, Lisp_Object 
data, bool keyboard_quit)
     {
       max_ensure_room (&max_lisp_eval_depth, lisp_eval_depth, 100);
       specpdl_ref count = SPECPDL_INDEX ();
-      ptrdiff_t counti = specpdl_ref_to_count (count);
-      max_ensure_room (&max_specpdl_size, counti, 200);
       specbind (Qdebugger, Qdebug_early);
       call_debugger (list2 (Qerror, Fcons (error_symbol, data)));
       unbind_to (count, Qnil);
@@ -1844,12 +1814,10 @@ signal_or_quit (Lisp_Object error_symbol, Lisp_Object 
data, bool keyboard_quit)
     {
       max_ensure_room (&max_lisp_eval_depth, lisp_eval_depth, 100);
       specpdl_ref count = SPECPDL_INDEX ();
-      ptrdiff_t counti = specpdl_ref_to_count (count);
       AUTO_STRING (redisplay_trace, "*Redisplay_trace*");
       Lisp_Object redisplay_trace_buffer;
       AUTO_STRING (gap, "\n\n\n\n"); /* Separates things in *Redisplay-trace* 
*/
       Lisp_Object delayed_warning;
-      max_ensure_room (&max_specpdl_size, counti, 200);
       redisplay_trace_buffer = Fget_buffer_create (redisplay_trace, Qnil);
       current_buffer = XBUFFER (redisplay_trace_buffer);
       if (!backtrace_yet) /* Are we on the first backtrace of the command?  */
@@ -2394,17 +2362,12 @@ grow_specpdl_allocation (void)
   eassert (specpdl_ptr == specpdl_end);
 
   specpdl_ref count = SPECPDL_INDEX ();
-  ptrdiff_t max_size = min (max_specpdl_size, PTRDIFF_MAX - 1000);
+  ptrdiff_t max_size = PTRDIFF_MAX - 1000;
   union specbinding *pdlvec = specpdl - 1;
   ptrdiff_t size = specpdl_end - specpdl;
   ptrdiff_t pdlvecsize = size + 1;
   if (max_size <= size)
-    {
-      if (max_specpdl_size < 400)
-       max_size = max_specpdl_size = 400;
-      if (max_size <= size)
-       xsignal0 (Qexcessive_variable_binding);
-    }
+    xsignal0 (Qexcessive_variable_binding);  /* Can't happen, essentially.  */
   pdlvec = xpalloc (pdlvec, &pdlvecsize, 1, max_size + 1, sizeof *specpdl);
   specpdl = pdlvec + 1;
   specpdl_end = specpdl + pdlvecsize - 1;
@@ -4247,22 +4210,6 @@ Lisp_Object backtrace_top_function (void)
 void
 syms_of_eval (void)
 {
-  DEFVAR_INT ("max-specpdl-size", max_specpdl_size,
-             doc: /* Limit on number of Lisp variable bindings and 
`unwind-protect's.
-
-If Lisp code tries to use more bindings than this amount, an error is
-signaled.
-
-You can safely increase this variable substantially if the default
-value proves inconveniently small.  However, if you increase it too
-much, Emacs could run out of memory trying to make the stack bigger.
-Note that this limit may be silently increased by the debugger if
-`debug-on-error' or `debug-on-quit' is set.
-
-\"spec\" is short for \"special variables\", i.e., dynamically bound
-variables.  \"PDL\" is short for \"push-down list\", which is an old
-term for \"stack\".  */);
-
   DEFVAR_INT ("max-lisp-eval-depth", max_lisp_eval_depth,
              doc: /* Limit on depth in `eval', `apply' and `funcall' before 
error.
 
diff --git a/src/fileio.c b/src/fileio.c
index 9697f6c8cf..dd7f85ec97 100644
--- a/src/fileio.c
+++ b/src/fileio.c
@@ -3808,7 +3808,7 @@ file_offset (Lisp_Object val)
        }
     }
 
-  wrong_type_argument (intern ("file-offset"), val);
+  wrong_type_argument (Qfile_offset, val);
 }
 
 /* Return a special time value indicating the error number ERRNUM.  */
@@ -4875,7 +4875,7 @@ by calling `format-decode', which see.  */)
       if (! NILP (insval))
        {
          if (! RANGED_FIXNUMP (0, insval, ZV - PT))
-           wrong_type_argument (intern ("inserted-chars"), insval);
+           wrong_type_argument (Qinserted_chars, insval);
          inserted = XFIXNAT (insval);
        }
     }
@@ -4898,7 +4898,7 @@ by calling `format-decode', which see.  */)
          insval = call3 (Qformat_decode,
                          Qnil, make_fixnum (inserted), visit);
          if (! RANGED_FIXNUMP (0, insval, ZV - PT))
-           wrong_type_argument (intern ("inserted-chars"), insval);
+           wrong_type_argument (Qinserted_chars, insval);
          inserted = XFIXNAT (insval);
        }
       else
@@ -4921,7 +4921,7 @@ by calling `format-decode', which see.  */)
          insval = call3 (Qformat_decode,
                          Qnil, make_fixnum (oinserted), visit);
          if (! RANGED_FIXNUMP (0, insval, ZV - PT))
-           wrong_type_argument (intern ("inserted-chars"), insval);
+           wrong_type_argument (Qinserted_chars, insval);
          if (ochars_modiff == CHARS_MODIFF)
            /* format_decode didn't modify buffer's characters => move
               point back to position before inserted text and leave
@@ -4944,7 +4944,7 @@ by calling `format-decode', which see.  */)
              if (!NILP (insval))
                {
                  if (! RANGED_FIXNUMP (0, insval, ZV - PT))
-                   wrong_type_argument (intern ("inserted-chars"), insval);
+                   wrong_type_argument (Qinserted_chars, insval);
                  inserted = XFIXNAT (insval);
                }
            }
@@ -4962,7 +4962,7 @@ by calling `format-decode', which see.  */)
              if (!NILP (insval))
                {
                  if (! RANGED_FIXNUMP (0, insval, ZV - PT))
-                   wrong_type_argument (intern ("inserted-chars"), insval);
+                   wrong_type_argument (Qinserted_chars, insval);
                  if (ochars_modiff == CHARS_MODIFF)
                    /* after_insert_file_functions didn't modify
                       buffer's characters => move point back to
@@ -6019,11 +6019,6 @@ A non-nil CURRENT-ONLY argument means save only current 
buffer.  */)
   bool old_message_p = 0;
   struct auto_save_unwind auto_save_unwind;
 
-  intmax_t sum = INT_ADD_WRAPV (specpdl_end - specpdl, 40, &sum)
-                 ? INTMAX_MAX : sum;
-  if (max_specpdl_size < sum)
-    max_specpdl_size = sum;
-
   if (minibuf_level)
     no_message = Qt;
 
@@ -6431,9 +6426,11 @@ syms_of_fileio (void)
   DEFSYM (Qfile_date_error, "file-date-error");
   DEFSYM (Qfile_missing, "file-missing");
   DEFSYM (Qpermission_denied, "permission-denied");
+  DEFSYM (Qfile_offset, "file-offset");
   DEFSYM (Qfile_notify_error, "file-notify-error");
   DEFSYM (Qremote_file_error, "remote-file-error");
   DEFSYM (Qexcl, "excl");
+  DEFSYM (Qinserted_chars, "inserted-chars");
 
   DEFVAR_LISP ("file-name-coding-system", Vfile_name_coding_system,
               doc: /* Coding system for encoding file names.
diff --git a/src/fns.c b/src/fns.c
index 7e78bba3a0..22e66d3653 100644
--- a/src/fns.c
+++ b/src/fns.c
@@ -449,25 +449,55 @@ Symbols are also allowed; their print names are used 
instead.  */)
     CHECK_STRING (string2);
 
   ptrdiff_t n = min (SCHARS (string1), SCHARS (string2));
-  if (!STRING_MULTIBYTE (string1) && !STRING_MULTIBYTE (string2))
+
+  if ((!STRING_MULTIBYTE (string1) || SCHARS (string1) == SBYTES (string1))
+      && (!STRING_MULTIBYTE (string2) || SCHARS (string2) == SBYTES (string2)))
     {
-      /* Both arguments are unibyte (hot path).  */
+      /* Each argument is either unibyte or all-ASCII multibyte:
+        we can compare bytewise.
+        (Arbitrary multibyte strings cannot be compared bytewise because
+        that would give a different order for raw bytes 80..FF.)  */
       int d = memcmp (SSDATA (string1), SSDATA (string2), n);
       return d < 0 || (d == 0 && n < SCHARS (string2)) ? Qt : Qnil;
     }
-
-  ptrdiff_t i1 = 0, i1_byte = 0, i2 = 0, i2_byte = 0;
-
-  while (i1 < n)
+  else if (STRING_MULTIBYTE (string1) && STRING_MULTIBYTE (string2))
     {
-      /* When we find a mismatch, we must compare the
-        characters, not just the bytes.  */
-      int c1 = fetch_string_char_advance (string1, &i1, &i1_byte);
-      int c2 = fetch_string_char_advance (string2, &i2, &i2_byte);
-      if (c1 != c2)
-       return c1 < c2 ? Qt : Qnil;
+      ptrdiff_t i1 = 0, i1_byte = 0, i2 = 0, i2_byte = 0;
+      while (i1 < n)
+       {
+         int c1 = fetch_string_char_advance_no_check (string1, &i1, &i1_byte);
+         int c2 = fetch_string_char_advance_no_check (string2, &i2, &i2_byte);
+         if (c1 != c2)
+           return c1 < c2 ? Qt : Qnil;
+       }
+      return i1 < SCHARS (string2) ? Qt : Qnil;
+    }
+  else if (STRING_MULTIBYTE (string1))
+    {
+      /* string1 multibyte, string2 unibyte */
+      ptrdiff_t i1 = 0, i1_byte = 0, i2 = 0;
+      while (i1 < n)
+       {
+         int c1 = fetch_string_char_advance_no_check (string1, &i1, &i1_byte);
+         int c2 = SREF (string2, i2++);
+         if (c1 != c2)
+           return c1 < c2 ? Qt : Qnil;
+       }
+      return i1 < SCHARS (string2) ? Qt : Qnil;
+    }
+  else
+    {
+      /* string1 unibyte, string2 multibyte */
+      ptrdiff_t i1 = 0, i2 = 0, i2_byte = 0;
+      while (i1 < n)
+       {
+         int c1 = SREF (string1, i1++);
+         int c2 = fetch_string_char_advance_no_check (string2, &i2, &i2_byte);
+         if (c1 != c2)
+           return c1 < c2 ? Qt : Qnil;
+       }
+      return i1 < SCHARS (string2) ? Qt : Qnil;
     }
-  return i1 < SCHARS (string2) ? Qt : Qnil;
 }
 
 DEFUN ("string-version-lessp", Fstring_version_lessp,
@@ -610,7 +640,10 @@ DEFUN ("append", Fappend, Sappend, 0, MANY, 0,
        doc: /* Concatenate all the arguments and make the result a list.
 The result is a list whose elements are the elements of all the arguments.
 Each argument may be a list, vector or string.
-The last argument is not copied, just used as the tail of the new list.
+
+All arguments except the last argument are copied.  The last argument
+is just used as the tail of the new list.
+
 usage: (append &rest SEQUENCES)  */)
   (ptrdiff_t nargs, Lisp_Object *args)
 {
@@ -1412,6 +1445,7 @@ are shared, however.
 Elements of ALIST that are not conses are also shared.  */)
   (Lisp_Object alist)
 {
+  CHECK_LIST (alist);
   if (NILP (alist))
     return alist;
   alist = Fcopy_sequence (alist);
@@ -1563,10 +1597,21 @@ If N is zero or negative, return nil.
 If N is greater or equal to the length of LIST, return LIST (or a copy).  */)
   (Lisp_Object n, Lisp_Object list)
 {
-  CHECK_FIXNUM (n);
-  EMACS_INT m = XFIXNUM (n);
-  if (m <= 0)
-    return Qnil;
+  EMACS_INT m;
+  if (FIXNUMP (n))
+    {
+      m = XFIXNUM (n);
+      if (m <= 0)
+       return Qnil;
+    }
+  else if (BIGNUMP (n))
+    {
+      if (mpz_sgn (*xbignum_val (n)) < 0)
+       return Qnil;
+      m = MOST_POSITIVE_FIXNUM;
+    }
+  else
+    wrong_type_argument (Qintegerp, n);
   CHECK_LIST (list);
   if (NILP (list))
     return Qnil;
@@ -1594,10 +1639,21 @@ If N is greater or equal to the length of LIST, return 
LIST unmodified.
 Otherwise, return LIST after truncating it.  */)
   (Lisp_Object n, Lisp_Object list)
 {
-  CHECK_FIXNUM (n);
-  EMACS_INT m = XFIXNUM (n);
-  if (m <= 0)
-    return Qnil;
+  EMACS_INT m;
+  if (FIXNUMP (n))
+    {
+      m = XFIXNUM (n);
+      if (m <= 0)
+       return Qnil;
+    }
+  else if (BIGNUMP (n))
+    {
+      if (mpz_sgn (*xbignum_val (n)) < 0)
+       return Qnil;
+      m = MOST_POSITIVE_FIXNUM;
+    }
+  else
+    wrong_type_argument (Qintegerp, n);
   CHECK_LIST (list);
   Lisp_Object tail = list;
   --m;
@@ -2908,15 +2964,37 @@ FUNCTION must be a function of one argument, and must 
return a value
     return empty_unibyte_string;
   Lisp_Object *args;
   SAFE_ALLOCA_LISP (args, args_alloc);
+  if (EQ (function, Qidentity))
+    {
+      /* Fast path when no function call is necessary.  */
+      if (CONSP (sequence))
+       {
+         Lisp_Object src = sequence;
+         Lisp_Object *dst = args;
+         do
+           {
+             *dst++ = XCAR (src);
+             src = XCDR (src);
+           }
+         while (!NILP (src));
+         goto concat;
+       }
+      else if (VECTORP (sequence))
+       {
+         memcpy (args, XVECTOR (sequence)->contents, leni * sizeof *args);
+         goto concat;
+       }
+    }
   ptrdiff_t nmapped = mapcar1 (leni, args, function, sequence);
-  ptrdiff_t nargs = 2 * nmapped - 1;
   eassert (nmapped == leni);
 
+ concat: ;
+  ptrdiff_t nargs = args_alloc;
   if (NILP (separator) || (STRINGP (separator) && SCHARS (separator) == 0))
-    nargs = nmapped;
+    nargs = leni;
   else
     {
-      for (ptrdiff_t i = nmapped - 1; i > 0; i--)
+      for (ptrdiff_t i = leni - 1; i > 0; i--)
         args[i + i] = args[i];
 
       for (ptrdiff_t i = 1; i < nargs; i += 2)
diff --git a/src/font.c b/src/font.c
index 8acedb9bf8..6e720bc285 100644
--- a/src/font.c
+++ b/src/font.c
@@ -1836,296 +1836,6 @@ font_parse_family_registry (Lisp_Object family, 
Lisp_Object registry, Lisp_Objec
 }
 
 
-/* This part (through the next ^L) is still experimental and not
-   tested much.  We may drastically change codes.  */
-
-/* OTF handler.  */
-
-#if 0
-
-#define LGSTRING_HEADER_SIZE 6
-#define LGSTRING_GLYPH_SIZE 8
-
-static int
-check_gstring (Lisp_Object gstring)
-{
-  Lisp_Object val;
-  ptrdiff_t i;
-  int j;
-
-  CHECK_VECTOR (gstring);
-  val = AREF (gstring, 0);
-  CHECK_VECTOR (val);
-  if (ASIZE (val) < LGSTRING_HEADER_SIZE)
-    goto err;
-  CHECK_FONT_OBJECT (LGSTRING_FONT (gstring));
-  if (!NILP (LGSTRING_SLOT (gstring, LGSTRING_IX_LBEARING)))
-    CHECK_FIXNUM (LGSTRING_SLOT (gstring, LGSTRING_IX_LBEARING));
-  if (!NILP (LGSTRING_SLOT (gstring, LGSTRING_IX_RBEARING)))
-    CHECK_FIXNUM (LGSTRING_SLOT (gstring, LGSTRING_IX_RBEARING));
-  if (!NILP (LGSTRING_SLOT (gstring, LGSTRING_IX_WIDTH)))
-    CHECK_FIXNAT (LGSTRING_SLOT (gstring, LGSTRING_IX_WIDTH));
-  if (!NILP (LGSTRING_SLOT (gstring, LGSTRING_IX_ASCENT)))
-    CHECK_FIXNUM (LGSTRING_SLOT (gstring, LGSTRING_IX_ASCENT));
-  if (!NILP (LGSTRING_SLOT (gstring, LGSTRING_IX_ASCENT)))
-    CHECK_FIXNUM (LGSTRING_SLOT (gstring, LGSTRING_IX_ASCENT));
-
-  for (i = 0; i < LGSTRING_GLYPH_LEN (gstring); i++)
-    {
-      val = LGSTRING_GLYPH (gstring, i);
-      CHECK_VECTOR (val);
-      if (ASIZE (val) < LGSTRING_GLYPH_SIZE)
-       goto err;
-      if (NILP (AREF (val, LGLYPH_IX_CHAR)))
-       break;
-      CHECK_FIXNAT (AREF (val, LGLYPH_IX_FROM));
-      CHECK_FIXNAT (AREF (val, LGLYPH_IX_TO));
-      CHECK_CHARACTER (AREF (val, LGLYPH_IX_CHAR));
-      if (!NILP (AREF (val, LGLYPH_IX_CODE)))
-       CHECK_FIXNAT (AREF (val, LGLYPH_IX_CODE));
-      if (!NILP (AREF (val, LGLYPH_IX_WIDTH)))
-       CHECK_FIXNAT (AREF (val, LGLYPH_IX_WIDTH));
-      if (!NILP (AREF (val, LGLYPH_IX_ADJUSTMENT)))
-       {
-         val = AREF (val, LGLYPH_IX_ADJUSTMENT);
-         CHECK_VECTOR (val);
-         if (ASIZE (val) < 3)
-           goto err;
-         for (j = 0; j < 3; j++)
-           CHECK_FIXNUM (AREF (val, j));
-       }
-    }
-  return i;
- err:
-  error ("Invalid glyph-string format");
-  return -1;
-}
-
-static void
-check_otf_features (Lisp_Object otf_features)
-{
-  Lisp_Object val;
-
-  CHECK_CONS (otf_features);
-  CHECK_SYMBOL (XCAR (otf_features));
-  otf_features = XCDR (otf_features);
-  CHECK_CONS (otf_features);
-  CHECK_SYMBOL (XCAR (otf_features));
-  otf_features = XCDR (otf_features);
-  for (val = Fcar (otf_features); CONSP (val); val = XCDR (val))
-    {
-      CHECK_SYMBOL (XCAR (val));
-      if (SBYTES (SYMBOL_NAME (XCAR (val))) > 4)
-       error ("Invalid OTF GSUB feature: %s",
-              SDATA (SYMBOL_NAME (XCAR (val))));
-    }
-  otf_features = XCDR (otf_features);
-  for (val = Fcar (otf_features); CONSP (val); val = XCDR (val))
-    {
-      CHECK_SYMBOL (XCAR (val));
-      if (SBYTES (SYMBOL_NAME (XCAR (val))) > 4)
-       error ("Invalid OTF GPOS feature: %s",
-              SDATA (SYMBOL_NAME (XCAR (val))));
-    }
-}
-
-#ifdef HAVE_LIBOTF
-#include <otf.h>
-
-Lisp_Object otf_list;
-
-static Lisp_Object
-otf_tag_symbol (OTF_Tag tag)
-{
-  char name[5];
-
-  OTF_tag_name (tag, name);
-  return Fintern (make_unibyte_string (name, 4), Qnil);
-}
-
-static OTF *
-otf_open (Lisp_Object file)
-{
-  Lisp_Object val = Fassoc (file, otf_list, Qnil);
-  OTF *otf;
-
-  if (! NILP (val))
-    otf = xmint_pointer (XCDR (val));
-  else
-    {
-      otf = STRINGP (file) ? OTF_open (SSDATA (file)) : NULL;
-      val = make_mint_ptr (otf);
-      otf_list = Fcons (Fcons (file, val), otf_list);
-    }
-  return otf;
-}
-
-
-/* Return a list describing which scripts/languages FONT supports by
-   which GSUB/GPOS features of OpenType tables.  See the comment of
-   (struct font_driver).otf_capability.  */
-
-Lisp_Object
-font_otf_capability (struct font *font)
-{
-  OTF *otf;
-  Lisp_Object capability = Fcons (Qnil, Qnil);
-  int i;
-
-  otf = otf_open (font->props[FONT_FILE_INDEX]);
-  if (! otf)
-    return Qnil;
-  for (i = 0; i < 2; i++)
-    {
-      OTF_GSUB_GPOS *gsub_gpos;
-      Lisp_Object script_list = Qnil;
-      int j;
-
-      if (OTF_get_features (otf, i == 0) < 0)
-       continue;
-      gsub_gpos = i == 0 ? otf->gsub : otf->gpos;
-      for (j = gsub_gpos->ScriptList.ScriptCount - 1; j >= 0; j--)
-       {
-         OTF_Script *script = gsub_gpos->ScriptList.Script + j;
-         Lisp_Object langsys_list = Qnil;
-         Lisp_Object script_tag = otf_tag_symbol (script->ScriptTag);
-         int k;
-
-         for (k = script->LangSysCount; k >= 0; k--)
-           {
-             OTF_LangSys *langsys;
-             Lisp_Object feature_list = Qnil;
-             Lisp_Object langsys_tag;
-             int l;
-
-             if (k == script->LangSysCount)
-               {
-                 langsys = &script->DefaultLangSys;
-                 langsys_tag = Qnil;
-               }
-             else
-               {
-                 langsys = script->LangSys + k;
-                 langsys_tag
-                   = otf_tag_symbol (script->LangSysRecord[k].LangSysTag);
-               }
-             for (l = langsys->FeatureCount - 1; l >= 0; l--)
-               {
-                 OTF_Feature *feature
-                   = gsub_gpos->FeatureList.Feature + langsys->FeatureIndex[l];
-                 Lisp_Object feature_tag
-                   = otf_tag_symbol (feature->FeatureTag);
-
-                 feature_list = Fcons (feature_tag, feature_list);
-               }
-             langsys_list = Fcons (Fcons (langsys_tag, feature_list),
-                                   langsys_list);
-           }
-         script_list = Fcons (Fcons (script_tag, langsys_list),
-                              script_list);
-       }
-
-      if (i == 0)
-       XSETCAR (capability, script_list);
-      else
-       XSETCDR (capability, script_list);
-    }
-
-  return capability;
-}
-
-/* Parse OTF features in SPEC and write a proper features spec string
-   in FEATURES for the call of OTF_drive_gsub/gpos (of libotf).  It is
-   assured that the sufficient memory has already allocated for
-   FEATURES.  */
-
-static void
-generate_otf_features (Lisp_Object spec, char *features)
-{
-  Lisp_Object val;
-  char *p;
-  bool asterisk;
-
-  p = features;
-  *p = '\0';
-  for (asterisk = 0; CONSP (spec); spec = XCDR (spec))
-    {
-      val = XCAR (spec);
-      CHECK_SYMBOL (val);
-      if (p > features)
-       *p++ = ',';
-      if (SREF (SYMBOL_NAME (val), 0) == '*')
-       {
-         asterisk = 1;
-         *p++ = '*';
-       }
-      else if (! asterisk)
-       {
-         val = SYMBOL_NAME (val);
-         p += esprintf (p, "%s", SDATA (val));
-       }
-      else
-       {
-         val = SYMBOL_NAME (val);
-         p += esprintf (p, "~%s", SDATA (val));
-       }
-    }
-  if (CONSP (spec))
-    error ("OTF spec too long");
-}
-
-Lisp_Object
-font_otf_DeviceTable (OTF_DeviceTable *device_table)
-{
-  int len = device_table->StartSize - device_table->EndSize + 1;
-
-  return Fcons (make_fixnum (len),
-               make_unibyte_string (device_table->DeltaValue, len));
-}
-
-Lisp_Object
-font_otf_ValueRecord (int value_format, OTF_ValueRecord *value_record)
-{
-  Lisp_Object val = make_nil_vector (8);
-
-  if (value_format & OTF_XPlacement)
-    ASET (val, 0, make_fixnum (value_record->XPlacement));
-  if (value_format & OTF_YPlacement)
-    ASET (val, 1, make_fixnum (value_record->YPlacement));
-  if (value_format & OTF_XAdvance)
-    ASET (val, 2, make_fixnum (value_record->XAdvance));
-  if (value_format & OTF_YAdvance)
-    ASET (val, 3, make_fixnum (value_record->YAdvance));
-  if (value_format & OTF_XPlaDevice)
-    ASET (val, 4, font_otf_DeviceTable (&value_record->XPlaDevice));
-  if (value_format & OTF_YPlaDevice)
-    ASET (val, 4, font_otf_DeviceTable (&value_record->YPlaDevice));
-  if (value_format & OTF_XAdvDevice)
-    ASET (val, 4, font_otf_DeviceTable (&value_record->XAdvDevice));
-  if (value_format & OTF_YAdvDevice)
-    ASET (val, 4, font_otf_DeviceTable (&value_record->YAdvDevice));
-  return val;
-}
-
-Lisp_Object
-font_otf_Anchor (OTF_Anchor *anchor)
-{
-  Lisp_Object val = make_nil_vector (anchor->AnchorFormat + 1);
-  ASET (val, 0, make_fixnum (anchor->XCoordinate));
-  ASET (val, 1, make_fixnum (anchor->YCoordinate));
-  if (anchor->AnchorFormat == 2)
-    ASET (val, 2, make_fixnum (anchor->f.f1.AnchorPoint));
-  else
-    {
-      ASET (val, 3, font_otf_DeviceTable (&anchor->f.f2.XDeviceTable));
-      ASET (val, 4, font_otf_DeviceTable (&anchor->f.f2.YDeviceTable));
-    }
-  return val;
-}
-#endif /* HAVE_LIBOTF */
-#endif /* 0 */
-
-
 /* Font sorting.  */
 
 static double
@@ -4041,7 +3751,7 @@ which kind of font it is.  It must be one of `font-spec', 
`font-entity',
     return (FONT_ENTITY_P (object) ? Qt : Qnil);
   if (EQ (extra_type, Qfont_object))
     return (FONT_OBJECT_P (object) ? Qt : Qnil);
-  wrong_type_argument (intern ("font-extra-type"), extra_type);
+  wrong_type_argument (Qfont_extra_type, extra_type); ;
 }
 
 DEFUN ("font-spec", Ffont_spec, Sfont_spec, 0, MANY, 0,
@@ -4678,6 +4388,7 @@ GSTRING.  */)
       from = LGLYPH_FROM (glyph);
       to = LGLYPH_TO (glyph);
     }
+  composition_gstring_adjust_zero_width (gstring);
   return composition_gstring_put_cache (gstring, XFIXNUM (n));
 
  shaper_error:
@@ -4694,131 +4405,424 @@ where
   GLYPH-ID is a glyph code of the corresponding variation glyph, an integer.  
*/)
   (Lisp_Object font_object, Lisp_Object character)
 {
-  unsigned variations[256];
-  struct font *font;
-  int i, n;
-  Lisp_Object val;
+  unsigned variations[256];
+  struct font *font;
+  int i, n;
+  Lisp_Object val;
+
+  CHECK_FONT_OBJECT (font_object);
+  CHECK_CHARACTER (character);
+  font = XFONT_OBJECT (font_object);
+  if (! font->driver->get_variation_glyphs)
+    return Qnil;
+  n = font->driver->get_variation_glyphs (font, XFIXNUM (character), 
variations);
+  if (! n)
+    return Qnil;
+  val = Qnil;
+  for (i = 0; i < 255; i++)
+    if (variations[i])
+      {
+       int vs = (i < 16 ? 0xFE00 + i : 0xE0100 + (i - 16));
+       Lisp_Object code = INT_TO_INTEGER (variations[i]);
+       val = Fcons (Fcons (make_fixnum (vs), code), val);
+      }
+  return val;
+}
+
+/* Return a description of the font at POSITION in the current buffer.
+   If the 2nd optional arg CH is non-nil, it is a character to check
+   the font instead of the character at POSITION.
+
+   For a graphical display, return a cons (FONT-OBJECT . GLYPH-CODE).
+   FONT-OBJECT is the font for the character at POSITION in the current
+   buffer.  This is computed from all the text properties and overlays
+   that apply to POSITION.  POSITION may be nil, in which case,
+   FONT-SPEC is the font for displaying the character CH with the
+   default face.  GLYPH-CODE is the glyph code in the font to use for
+   the character, it is a fixnum, if it is small enough, otherwise a
+   bignum.
+
+   For a text terminal, return a nonnegative integer glyph code for
+   the character, or a negative integer if the character is not
+   displayable.  Terminal glyph codes are system-dependent integers
+   that represent displayable characters: for example, on a Linux x86
+   console they represent VGA code points.
+
+   It returns nil in the following cases:
+
+   (1) The window system doesn't have a font for the character (thus
+   it is displayed by an empty box).
+
+   (2) The character code is invalid.
+
+   (3) If POSITION is not nil, and the current buffer is not displayed
+   in any window.
+
+   (4) For a text terminal, the terminal does not report glyph codes.
+
+   In addition, the returned font name may not take into account of
+   such redisplay engine hooks as what used in jit-lock-mode if
+   POSITION is currently not visible.  */
+
+
+DEFUN ("internal-char-font", Finternal_char_font, Sinternal_char_font, 1, 2, 0,
+       doc: /* For internal use only.  */)
+  (Lisp_Object position, Lisp_Object ch)
+{
+  ptrdiff_t pos, pos_byte, dummy;
+  int face_id;
+  int c;
+  struct frame *f;
+
+  if (NILP (position))
+    {
+      CHECK_CHARACTER (ch);
+      c = XFIXNUM (ch);
+      f = XFRAME (selected_frame);
+      face_id = lookup_basic_face (NULL, f, DEFAULT_FACE_ID);
+      pos = -1;
+    }
+  else
+    {
+      Lisp_Object window;
+      struct window *w;
+
+      EMACS_INT fixed_pos = fix_position (position);
+      if (! (BEGV <= fixed_pos && fixed_pos < ZV))
+       args_out_of_range_3 (position, make_fixnum (BEGV), make_fixnum (ZV));
+      pos = fixed_pos;
+      pos_byte = CHAR_TO_BYTE (pos);
+      if (NILP (ch))
+       c = FETCH_CHAR (pos_byte);
+      else
+       {
+         CHECK_FIXNAT (ch);
+         c = XFIXNUM (ch);
+       }
+      window = Fget_buffer_window (Fcurrent_buffer (), Qnil);
+      if (NILP (window))
+       return Qnil;
+      w = XWINDOW (window);
+      f = XFRAME (w->frame);
+      face_id = face_at_buffer_position (w, pos, &dummy,
+                                         pos + 100, false, -1, 0);
+    }
+  if (! CHAR_VALID_P (c))
+    return Qnil;
+
+  if (! FRAME_WINDOW_P (f))
+    return terminal_glyph_code (FRAME_TERMINAL (f), c);
+
+  /* We need the basic faces to be valid below, so recompute them if
+     some code just happened to clear the face cache.  */
+  if (FRAME_FACE_CACHE (f)->used == 0)
+    recompute_basic_faces (f);
+
+  face_id = FACE_FOR_CHAR (f, FACE_FROM_ID (f, face_id), c, pos, Qnil);
+  struct face *face = FACE_FROM_ID (f, face_id);
+  if (! face->font)
+    return Qnil;
+  unsigned code = face->font->driver->encode_char (face->font, c);
+  if (code == FONT_INVALID_CODE)
+    return Qnil;
+  Lisp_Object font_object;
+  XSETFONT (font_object, face->font);
+  return Fcons (font_object, INT_TO_INTEGER (code));
+}
+
+
+/* This part (through the next ^L) is still experimental and not
+   tested much.  We may drastically change codes.  */
+
+/* This code implements support for extracting OTF features of a font
+   and exposing them to Lisp, including application of those features
+   to arbitrary stretches of text.  FIXME: it would be good to finish
+   this work and have this in Emacs.  */
+
+/* OTF handler.  */
+
+#if 0
+
+#define LGSTRING_HEADER_SIZE 6
+#define LGSTRING_GLYPH_SIZE 8
+
+static int
+check_gstring (Lisp_Object gstring)
+{
+  Lisp_Object val;
+  ptrdiff_t i;
+  int j;
+
+  CHECK_VECTOR (gstring);
+  val = AREF (gstring, 0);
+  CHECK_VECTOR (val);
+  if (ASIZE (val) < LGSTRING_HEADER_SIZE)
+    goto err;
+  CHECK_FONT_OBJECT (LGSTRING_FONT (gstring));
+  if (!NILP (LGSTRING_SLOT (gstring, LGSTRING_IX_LBEARING)))
+    CHECK_FIXNUM (LGSTRING_SLOT (gstring, LGSTRING_IX_LBEARING));
+  if (!NILP (LGSTRING_SLOT (gstring, LGSTRING_IX_RBEARING)))
+    CHECK_FIXNUM (LGSTRING_SLOT (gstring, LGSTRING_IX_RBEARING));
+  if (!NILP (LGSTRING_SLOT (gstring, LGSTRING_IX_WIDTH)))
+    CHECK_FIXNAT (LGSTRING_SLOT (gstring, LGSTRING_IX_WIDTH));
+  if (!NILP (LGSTRING_SLOT (gstring, LGSTRING_IX_ASCENT)))
+    CHECK_FIXNUM (LGSTRING_SLOT (gstring, LGSTRING_IX_ASCENT));
+  if (!NILP (LGSTRING_SLOT (gstring, LGSTRING_IX_ASCENT)))
+    CHECK_FIXNUM (LGSTRING_SLOT (gstring, LGSTRING_IX_ASCENT));
+
+  for (i = 0; i < LGSTRING_GLYPH_LEN (gstring); i++)
+    {
+      val = LGSTRING_GLYPH (gstring, i);
+      CHECK_VECTOR (val);
+      if (ASIZE (val) < LGSTRING_GLYPH_SIZE)
+       goto err;
+      if (NILP (AREF (val, LGLYPH_IX_CHAR)))
+       break;
+      CHECK_FIXNAT (AREF (val, LGLYPH_IX_FROM));
+      CHECK_FIXNAT (AREF (val, LGLYPH_IX_TO));
+      CHECK_CHARACTER (AREF (val, LGLYPH_IX_CHAR));
+      if (!NILP (AREF (val, LGLYPH_IX_CODE)))
+       CHECK_FIXNAT (AREF (val, LGLYPH_IX_CODE));
+      if (!NILP (AREF (val, LGLYPH_IX_WIDTH)))
+       CHECK_FIXNAT (AREF (val, LGLYPH_IX_WIDTH));
+      if (!NILP (AREF (val, LGLYPH_IX_ADJUSTMENT)))
+       {
+         val = AREF (val, LGLYPH_IX_ADJUSTMENT);
+         CHECK_VECTOR (val);
+         if (ASIZE (val) < 3)
+           goto err;
+         for (j = 0; j < 3; j++)
+           CHECK_FIXNUM (AREF (val, j));
+       }
+    }
+  return i;
+ err:
+  error ("Invalid glyph-string format");
+  return -1;
+}
+
+static void
+check_otf_features (Lisp_Object otf_features)
+{
+  Lisp_Object val;
+
+  CHECK_CONS (otf_features);
+  CHECK_SYMBOL (XCAR (otf_features));
+  otf_features = XCDR (otf_features);
+  CHECK_CONS (otf_features);
+  CHECK_SYMBOL (XCAR (otf_features));
+  otf_features = XCDR (otf_features);
+  for (val = Fcar (otf_features); CONSP (val); val = XCDR (val))
+    {
+      CHECK_SYMBOL (XCAR (val));
+      if (SBYTES (SYMBOL_NAME (XCAR (val))) > 4)
+       error ("Invalid OTF GSUB feature: %s",
+              SDATA (SYMBOL_NAME (XCAR (val))));
+    }
+  otf_features = XCDR (otf_features);
+  for (val = Fcar (otf_features); CONSP (val); val = XCDR (val))
+    {
+      CHECK_SYMBOL (XCAR (val));
+      if (SBYTES (SYMBOL_NAME (XCAR (val))) > 4)
+       error ("Invalid OTF GPOS feature: %s",
+              SDATA (SYMBOL_NAME (XCAR (val))));
+    }
+}
+
+#ifdef HAVE_LIBOTF
+#include <otf.h>
+
+Lisp_Object otf_list;
+
+static Lisp_Object
+otf_tag_symbol (OTF_Tag tag)
+{
+  char name[5];
+
+  OTF_tag_name (tag, name);
+  return Fintern (make_unibyte_string (name, 4), Qnil);
+}
+
+static OTF *
+otf_open (Lisp_Object file)
+{
+  Lisp_Object val = Fassoc (file, otf_list, Qnil);
+  OTF *otf;
 
-  CHECK_FONT_OBJECT (font_object);
-  CHECK_CHARACTER (character);
-  font = XFONT_OBJECT (font_object);
-  if (! font->driver->get_variation_glyphs)
-    return Qnil;
-  n = font->driver->get_variation_glyphs (font, XFIXNUM (character), 
variations);
-  if (! n)
-    return Qnil;
-  val = Qnil;
-  for (i = 0; i < 255; i++)
-    if (variations[i])
-      {
-       int vs = (i < 16 ? 0xFE00 + i : 0xE0100 + (i - 16));
-       Lisp_Object code = INT_TO_INTEGER (variations[i]);
-       val = Fcons (Fcons (make_fixnum (vs), code), val);
-      }
-  return val;
+  if (! NILP (val))
+    otf = xmint_pointer (XCDR (val));
+  else
+    {
+      otf = STRINGP (file) ? OTF_open (SSDATA (file)) : NULL;
+      val = make_mint_ptr (otf);
+      otf_list = Fcons (Fcons (file, val), otf_list);
+    }
+  return otf;
 }
 
-/* Return a description of the font at POSITION in the current buffer.
-   If the 2nd optional arg CH is non-nil, it is a character to check
-   the font instead of the character at POSITION.
 
-   For a graphical display, return a cons (FONT-OBJECT . GLYPH-CODE).
-   FONT-OBJECT is the font for the character at POSITION in the current
-   buffer.  This is computed from all the text properties and overlays
-   that apply to POSITION.  POSITION may be nil, in which case,
-   FONT-SPEC is the font for displaying the character CH with the
-   default face.  GLYPH-CODE is the glyph code in the font to use for
-   the character, as an integer.
+/* Return a list describing which scripts/languages FONT supports by
+   which GSUB/GPOS features of OpenType tables.  See the comment of
+   (struct font_driver).otf_capability.  */
 
-   For a text terminal, return a nonnegative integer glyph code for
-   the character, or a negative integer if the character is not
-   displayable.  Terminal glyph codes are system-dependent integers
-   that represent displayable characters: for example, on a Linux x86
-   console they represent VGA code points.
+Lisp_Object
+font_otf_capability (struct font *font)
+{
+  OTF *otf;
+  Lisp_Object capability = Fcons (Qnil, Qnil);
+  int i;
 
-   It returns nil in the following cases:
+  otf = otf_open (font->props[FONT_FILE_INDEX]);
+  if (! otf)
+    return Qnil;
+  for (i = 0; i < 2; i++)
+    {
+      OTF_GSUB_GPOS *gsub_gpos;
+      Lisp_Object script_list = Qnil;
+      int j;
 
-   (1) The window system doesn't have a font for the character (thus
-   it is displayed by an empty box).
+      if (OTF_get_features (otf, i == 0) < 0)
+       continue;
+      gsub_gpos = i == 0 ? otf->gsub : otf->gpos;
+      for (j = gsub_gpos->ScriptList.ScriptCount - 1; j >= 0; j--)
+       {
+         OTF_Script *script = gsub_gpos->ScriptList.Script + j;
+         Lisp_Object langsys_list = Qnil;
+         Lisp_Object script_tag = otf_tag_symbol (script->ScriptTag);
+         int k;
 
-   (2) The character code is invalid.
+         for (k = script->LangSysCount; k >= 0; k--)
+           {
+             OTF_LangSys *langsys;
+             Lisp_Object feature_list = Qnil;
+             Lisp_Object langsys_tag;
+             int l;
 
-   (3) If POSITION is not nil, and the current buffer is not displayed
-   in any window.
+             if (k == script->LangSysCount)
+               {
+                 langsys = &script->DefaultLangSys;
+                 langsys_tag = Qnil;
+               }
+             else
+               {
+                 langsys = script->LangSys + k;
+                 langsys_tag
+                   = otf_tag_symbol (script->LangSysRecord[k].LangSysTag);
+               }
+             for (l = langsys->FeatureCount - 1; l >= 0; l--)
+               {
+                 OTF_Feature *feature
+                   = gsub_gpos->FeatureList.Feature + langsys->FeatureIndex[l];
+                 Lisp_Object feature_tag
+                   = otf_tag_symbol (feature->FeatureTag);
 
-   (4) For a text terminal, the terminal does not report glyph codes.
+                 feature_list = Fcons (feature_tag, feature_list);
+               }
+             langsys_list = Fcons (Fcons (langsys_tag, feature_list),
+                                   langsys_list);
+           }
+         script_list = Fcons (Fcons (script_tag, langsys_list),
+                              script_list);
+       }
 
-   In addition, the returned font name may not take into account of
-   such redisplay engine hooks as what used in jit-lock-mode if
-   POSITION is currently not visible.  */
+      if (i == 0)
+       XSETCAR (capability, script_list);
+      else
+       XSETCDR (capability, script_list);
+    }
 
+  return capability;
+}
 
-DEFUN ("internal-char-font", Finternal_char_font, Sinternal_char_font, 1, 2, 0,
-       doc: /* For internal use only.  */)
-  (Lisp_Object position, Lisp_Object ch)
+/* Parse OTF features in SPEC and write a proper features spec string
+   in FEATURES for the call of OTF_drive_gsub/gpos (of libotf).  It is
+   assured that the sufficient memory has already allocated for
+   FEATURES.  */
+
+static void
+generate_otf_features (Lisp_Object spec, char *features)
 {
-  ptrdiff_t pos, pos_byte, dummy;
-  int face_id;
-  int c;
-  struct frame *f;
+  Lisp_Object val;
+  char *p;
+  bool asterisk;
 
-  if (NILP (position))
-    {
-      CHECK_CHARACTER (ch);
-      c = XFIXNUM (ch);
-      f = XFRAME (selected_frame);
-      face_id = lookup_basic_face (NULL, f, DEFAULT_FACE_ID);
-      pos = -1;
-    }
-  else
+  p = features;
+  *p = '\0';
+  for (asterisk = 0; CONSP (spec); spec = XCDR (spec))
     {
-      Lisp_Object window;
-      struct window *w;
-
-      EMACS_INT fixed_pos = fix_position (position);
-      if (! (BEGV <= fixed_pos && fixed_pos < ZV))
-       args_out_of_range_3 (position, make_fixnum (BEGV), make_fixnum (ZV));
-      pos = fixed_pos;
-      pos_byte = CHAR_TO_BYTE (pos);
-      if (NILP (ch))
-       c = FETCH_CHAR (pos_byte);
+      val = XCAR (spec);
+      CHECK_SYMBOL (val);
+      if (p > features)
+       *p++ = ',';
+      if (SREF (SYMBOL_NAME (val), 0) == '*')
+       {
+         asterisk = 1;
+         *p++ = '*';
+       }
+      else if (! asterisk)
+       {
+         val = SYMBOL_NAME (val);
+         p += esprintf (p, "%s", SDATA (val));
+       }
       else
        {
-         CHECK_FIXNAT (ch);
-         c = XFIXNUM (ch);
+         val = SYMBOL_NAME (val);
+         p += esprintf (p, "~%s", SDATA (val));
        }
-      window = Fget_buffer_window (Fcurrent_buffer (), Qnil);
-      if (NILP (window))
-       return Qnil;
-      w = XWINDOW (window);
-      f = XFRAME (w->frame);
-      face_id = face_at_buffer_position (w, pos, &dummy,
-                                         pos + 100, false, -1, 0);
     }
-  if (! CHAR_VALID_P (c))
-    return Qnil;
+  if (CONSP (spec))
+    error ("OTF spec too long");
+}
 
-  if (! FRAME_WINDOW_P (f))
-    return terminal_glyph_code (FRAME_TERMINAL (f), c);
+Lisp_Object
+font_otf_DeviceTable (OTF_DeviceTable *device_table)
+{
+  int len = device_table->StartSize - device_table->EndSize + 1;
 
-  /* We need the basic faces to be valid below, so recompute them if
-     some code just happened to clear the face cache.  */
-  if (FRAME_FACE_CACHE (f)->used == 0)
-    recompute_basic_faces (f);
+  return Fcons (make_fixnum (len),
+               make_unibyte_string (device_table->DeltaValue, len));
+}
 
-  face_id = FACE_FOR_CHAR (f, FACE_FROM_ID (f, face_id), c, pos, Qnil);
-  struct face *face = FACE_FROM_ID (f, face_id);
-  if (! face->font)
-    return Qnil;
-  unsigned code = face->font->driver->encode_char (face->font, c);
-  if (code == FONT_INVALID_CODE)
-    return Qnil;
-  Lisp_Object font_object;
-  XSETFONT (font_object, face->font);
-  return Fcons (font_object, INT_TO_INTEGER (code));
+Lisp_Object
+font_otf_ValueRecord (int value_format, OTF_ValueRecord *value_record)
+{
+  Lisp_Object val = make_nil_vector (8);
+
+  if (value_format & OTF_XPlacement)
+    ASET (val, 0, make_fixnum (value_record->XPlacement));
+  if (value_format & OTF_YPlacement)
+    ASET (val, 1, make_fixnum (value_record->YPlacement));
+  if (value_format & OTF_XAdvance)
+    ASET (val, 2, make_fixnum (value_record->XAdvance));
+  if (value_format & OTF_YAdvance)
+    ASET (val, 3, make_fixnum (value_record->YAdvance));
+  if (value_format & OTF_XPlaDevice)
+    ASET (val, 4, font_otf_DeviceTable (&value_record->XPlaDevice));
+  if (value_format & OTF_YPlaDevice)
+    ASET (val, 4, font_otf_DeviceTable (&value_record->YPlaDevice));
+  if (value_format & OTF_XAdvDevice)
+    ASET (val, 4, font_otf_DeviceTable (&value_record->XAdvDevice));
+  if (value_format & OTF_YAdvDevice)
+    ASET (val, 4, font_otf_DeviceTable (&value_record->YAdvDevice));
+  return val;
 }
 
-#if 0
+Lisp_Object
+font_otf_Anchor (OTF_Anchor *anchor)
+{
+  Lisp_Object val = make_nil_vector (anchor->AnchorFormat + 1);
+  ASET (val, 0, make_fixnum (anchor->XCoordinate));
+  ASET (val, 1, make_fixnum (anchor->YCoordinate));
+  if (anchor->AnchorFormat == 2)
+    ASET (val, 2, make_fixnum (anchor->f.f1.AnchorPoint));
+  else
+    {
+      ASET (val, 3, font_otf_DeviceTable (&anchor->f.f2.XDeviceTable));
+      ASET (val, 4, font_otf_DeviceTable (&anchor->f.f2.YDeviceTable));
+    }
+  return val;
+}
+#endif /* HAVE_LIBOTF */
 
 DEFUN ("font-drive-otf", Ffont_drive_otf, Sfont_drive_otf, 6, 6, 0,
        doc: /* Apply OpenType features on glyph-string GSTRING-IN.
@@ -4938,6 +4942,7 @@ corresponding character.  */)
 }
 #endif /* 0 */
 
+
 #ifdef FONT_DEBUG
 
 DEFUN ("open-font", Fopen_font, Sopen_font, 1, 3, 0,
@@ -5555,6 +5560,10 @@ syms_of_font (void)
 
   DEFSYM (Qopentype, "opentype");
 
+  /* Currently used by hbfont.c, which has no syms_of_hbfont function
+     of its own.  */
+  DEFSYM (Qcanonical_combining_class, "canonical-combining-class");
+
   /* Important character set symbols.  */
   DEFSYM (Qascii_0, "ascii-0");
   DEFSYM (Qiso8859_1, "iso8859-1");
@@ -5594,6 +5603,7 @@ syms_of_font (void)
   DEFSYM (QL2R, "L2R");
   DEFSYM (QR2L, "R2L");
 
+  DEFSYM (Qfont_extra_type, "font-extra-type");
   DEFSYM (Qfont_driver_superseded_by, "font-driver-superseded-by");
 
   scratch_font_spec = Ffont_spec (0, NULL);
diff --git a/src/font.h b/src/font.h
index 06bd297ccb..3475189206 100644
--- a/src/font.h
+++ b/src/font.h
@@ -660,7 +660,7 @@ struct font_driver
 
   /* Optional.
      Draw glyphs between FROM and TO of S->char2b at (X Y) pixel
-     position of frame F with S->FACE and S->GC.  If WITH_BACKGROUND,
+     position of frame S->f with S->face and S->gc.  If WITH_BACKGROUND,
      fill the background in advance.  It is assured that WITH_BACKGROUND
      is false when (FROM > 0 || TO < S->nchars).  */
   int (*draw) (struct glyph_string *s, int from, int to,
diff --git a/src/fontset.c b/src/fontset.c
index 1793715450..4b91eff2ef 100644
--- a/src/fontset.c
+++ b/src/fontset.c
@@ -922,8 +922,6 @@ face_for_char (struct frame *f, struct face *face, int c,
   int face_id;
   int id;
 
-  eassert (fontset_id_valid_p (face->fontset));
-
   if (ASCII_CHAR_P (c) || CHAR_BYTE8_P (c))
     return face->ascii_face->id;
 
@@ -969,6 +967,7 @@ face_for_char (struct frame *f, struct face *face, int c,
 #endif
     }
 
+  eassert (fontset_id_valid_p (face->fontset));
   fontset = FONTSET_FROM_ID (face->fontset);
   eassert (!BASE_FONTSET_P (fontset));
 
diff --git a/src/frame.c b/src/frame.c
index 25d71e0769..91b9bec82c 100644
--- a/src/frame.c
+++ b/src/frame.c
@@ -6243,7 +6243,7 @@ You can also use a floating number between 0.0 and 1.0.  
*/);
 #endif
 
   DEFVAR_LISP ("default-frame-alist", Vdefault_frame_alist,
-              doc: /* Alist of default values for frame creation.
+    doc: /* Alist of default values of frame parameters for frame creation.
 These may be set in your init file, like this:
   (setq default-frame-alist \\='((width . 80) (height . 55) (menu-bar-lines . 
1)))
 
diff --git a/src/ftcrfont.c b/src/ftcrfont.c
index e089f9dea8..dc765e5aee 100644
--- a/src/ftcrfont.c
+++ b/src/ftcrfont.c
@@ -233,6 +233,7 @@ ftcrfont_open (struct frame *f, Lisp_Object entity, int 
pixel_size)
   cairo_glyph_t stack_glyph;
   font->min_width = font->max_width = 0;
   font->average_width = font->space_width = 0;
+  int n = 0;
   for (char c = 32; c < 127; c++)
     {
       cairo_glyph_t *glyphs = &stack_glyph;
@@ -252,17 +253,20 @@ ftcrfont_open (struct frame *f, Lisp_Object entity, int 
pixel_size)
          stack_glyph.index = 0;
        }
       int this_width = ftcrfont_glyph_extents (font, stack_glyph.index, NULL);
-      if (this_width > 0
-         && (! font->min_width
-             || font->min_width > this_width))
-       font->min_width = this_width;
-      if (this_width > font->max_width)
-       font->max_width = this_width;
-      if (c == 32)
-       font->space_width = this_width;
-      font->average_width += this_width;
+      if (this_width > 0)
+       {
+         if (! font->min_width || font->min_width > this_width)
+           font->min_width = this_width;
+         if (this_width > font->max_width)
+           font->max_width = this_width;
+         if (c == 32)
+           font->space_width = this_width;
+         font->average_width += this_width;
+         n++;
+       }
     }
-  font->average_width /= 95;
+  if (n)
+    font->average_width /= n;
 
   cairo_scaled_font_extents (ftcrfont_info->cr_scaled_font, &extents);
   font->ascent = lround (extents.ascent);
@@ -679,8 +683,12 @@ ftcrhbfont_begin_hb_font (struct font *font, double 
*position_unit)
   hb_font_t *hb_font = fthbfont_begin_hb_font (font, position_unit);
   /* HarfBuzz 5 correctly scales bitmap-only fonts without position
      unit adjustment.
-     (https://github.com/harfbuzz/harfbuzz/issues/489) */
-  if (!hb_version_atleast (5, 0, 0)
+     (https://github.com/harfbuzz/harfbuzz/issues/489)
+
+     Update: HarfBuzz 5.2.0 no longer does this for an hb_font_t font
+     object created from a given FT_Face.
+     (https://github.com/harfbuzz/harfbuzz/issues/3788) */
+  if ((hb_version_atleast (5, 2, 0) || !hb_version_atleast (5, 0, 0))
       && ftcrfont_info->bitmap_position_unit)
     *position_unit = ftcrfont_info->bitmap_position_unit;
 
diff --git a/src/haiku_font_support.cc b/src/haiku_font_support.cc
index d824cc59ae..9a2492c9a1 100644
--- a/src/haiku_font_support.cc
+++ b/src/haiku_font_support.cc
@@ -21,6 +21,14 @@ along with GNU Emacs.  If not, see 
<https://www.gnu.org/licenses/>.  */
 #include <Font.h>
 #include <Rect.h>
 #include <AffineTransform.h>
+#include <FindDirectory.h>
+#include <Path.h>
+#include <File.h>
+#include <Message.h>
+#include <OS.h>
+#include <Locker.h>
+#include <NodeMonitor.h>
+#include <Looper.h>
 
 #include <cstring>
 #include <cmath>
@@ -39,15 +47,57 @@ struct font_object_cache_bucket
 
 static struct font_object_cache_bucket *font_object_cache[2048];
 
+/* The current global monospace family and style.  */
+static char *fixed_family, *fixed_style;
+
+/* The current global variable-width family and style.  */
+static char *default_family, *default_style;
+
+/* The sizes of each of those fonts.  */
+static float default_size, fixed_size;
+
+/* The locker controlling access to those variables.  */
+static BLocker default_locker;
+
 /* Haiku doesn't expose font language data in BFont objects.  Thus, we
    select a few representative characters for each supported `:lang'
    (currently Chinese, Korean and Japanese,) and test for those
    instead.  */
 
 static int language_code_points[MAX_LANGUAGE][3] =
-  {{20154, 20754, 22996}, /* Chinese.  */
-   {51312, 49440, 44544}, /* Korean.  */
-   {26085, 26412, 12371}, /* Japanese.  */};
+  {
+    {20154, 20754, 22996}, /* Chinese.  */
+    {51312, 49440, 44544}, /* Korean.  */
+    {26085, 26412, 12371}, /* Japanese.  */
+  };
+
+static void be_send_font_settings (void);
+
+/* Looper used to track changes to system-wide font settings.  */
+class EmacsFontMonitorLooper : public BLooper
+{
+  void
+  MessageReceived (BMessage *msg)
+  {
+    int32 opcode;
+
+    if (msg->what != B_NODE_MONITOR)
+      return;
+
+    if (msg->FindInt32 ("opcode", &opcode) != B_OK)
+      return;
+
+    if (opcode != B_STAT_CHANGED)
+      return;
+
+    /* Wait a little for any message to be completely written after
+       the file's modification time changes.  */
+    snooze (10000);
+
+    /* Read and apply font settings.  */
+    be_send_font_settings ();
+  }
+};
 
 static unsigned int
 hash_string (const char *name_or_style)
@@ -288,12 +338,15 @@ BFont_nchar_bounds (void *font, const char *mb_str, int 
*advance,
 }
 
 static void
-font_style_to_flags (char *st, struct haiku_font_pattern *pattern)
+font_style_to_flags (const char *style_string,
+                    struct haiku_font_pattern *pattern)
 {
-  char *style = strdup (st);
+  char *style;
   char *token;
   int tok = 0;
 
+  style = strdup (style_string);
+
   if (!style)
     return;
 
@@ -385,7 +438,8 @@ font_style_to_flags (char *st, struct haiku_font_pattern 
*pattern)
       pattern->specified &= ~FSPEC_WEIGHT;
       pattern->specified &= ~FSPEC_WIDTH;
       pattern->specified |= FSPEC_STYLE;
-      std::strncpy ((char *) &pattern->style, st,
+      std::strncpy ((char *) &pattern->style,
+                   style_string,
                    sizeof pattern->style - 1);
       pattern->style[sizeof pattern->style - 1] = '\0';
     }
@@ -887,7 +941,7 @@ be_evict_font_cache (void)
 }
 
 void
-be_font_style_to_flags (char *style, struct haiku_font_pattern *pattern)
+be_font_style_to_flags (const char *style, struct haiku_font_pattern *pattern)
 {
   pattern->specified = 0;
 
@@ -939,3 +993,217 @@ be_set_font_antialiasing (void *font, bool antialias_p)
                         ? B_FORCE_ANTIALIASING
                         : B_DISABLE_ANTIALIASING);
 }
+
+static void
+be_send_font_settings (void)
+{
+  struct haiku_font_change_event rq;
+  BFile file;
+  BPath path;
+  status_t rc;
+  BMessage message;
+  font_family family;
+  font_style style;
+  const char *new_family, *new_style;
+  float new_size;
+
+  rc = find_directory (B_USER_SETTINGS_DIRECTORY, &path);
+
+  if (rc < B_OK)
+    return;
+
+  rc = path.Append ("system/app_server/fonts");
+
+  if (rc < B_OK)
+    return;
+
+  if (file.SetTo (path.Path (), B_READ_ONLY) != B_OK)
+    return;
+
+  if (message.Unflatten (&file) != B_OK)
+    return;
+
+  /* Now, populate with new values.  */
+  if (!default_locker.Lock ())
+    gui_abort ("Failed to lock font data locker");
+
+  /* Obtain default values.  */
+  be_fixed_font->GetFamilyAndStyle (&family, &style);
+  default_size = be_fixed_font->Size ();
+
+  /* And the new values.  */
+  new_family = message.GetString ("fixed family", family);
+  new_style = message.GetString ("fixed style", style);
+  new_size = message.GetFloat ("fixed size", default_size);
+
+  /* If it turns out the fixed family changed, send the new family and
+     style.  */
+
+  if (!fixed_family || !fixed_style
+      || new_size != fixed_size
+      || strcmp (new_family, fixed_family)
+      || strcmp (new_style, fixed_style))
+    {
+      memset (&rq, 0, sizeof rq);
+      strncpy (rq.new_family, (char *) new_family,
+              sizeof rq.new_family - 1);
+      strncpy (rq.new_style, (char *) new_style,
+              sizeof rq.new_style - 1);
+      rq.new_size = new_size;
+      rq.what = FIXED_FAMILY;
+
+      haiku_write (FONT_CHANGE_EVENT, &rq);
+    }
+
+  if (fixed_family)
+    free (fixed_family);
+
+  if (fixed_style)
+    free (fixed_style);
+
+  fixed_family = strdup (new_family);
+  fixed_style = strdup (new_style);
+  fixed_size = new_size;
+
+  /* Obtain default values.  */
+  be_plain_font->GetFamilyAndStyle (&family, &style);
+  default_size = be_plain_font->Size ();
+
+  /* And the new values.  */
+  new_family = message.GetString ("plain family", family);
+  new_style = message.GetString ("plain style", style);
+  new_size = message.GetFloat ("plain style", default_size);
+
+  if (!default_family || !default_style
+      || new_size != default_size
+      || strcmp (new_family, default_family)
+      || strcmp (new_style, default_style))
+    {
+      memset (&rq, 0, sizeof rq);
+      strncpy (rq.new_family, (char *) new_family,
+              sizeof rq.new_family - 1);
+      strncpy (rq.new_style, (char *) new_style,
+              sizeof rq.new_style - 1);
+      rq.new_size = new_size;
+      rq.what = DEFAULT_FAMILY;
+
+      haiku_write (FONT_CHANGE_EVENT, &rq);
+    }
+
+  if (default_family)
+    free (default_family);
+
+  if (default_style)
+    free (default_style);
+
+  default_family = strdup (new_family);
+  default_style = strdup (new_style);
+  default_size = new_size;
+
+  default_locker.Unlock ();
+}
+
+/* Begin listening to font settings changes, by installing a node
+   watcher.  This relies on the settings file already being present
+   and has several inherent race conditions, but users shouldn't be
+   changing font settings very quickly.  */
+
+void
+be_listen_font_settings (void)
+{
+  BPath path;
+  status_t rc;
+  BNode node;
+  node_ref node_ref;
+  EmacsFontMonitorLooper *looper;
+  font_family family;
+  font_style style;
+
+  /* Set up initial values.  */
+  be_fixed_font->GetFamilyAndStyle (&family, &style);
+  fixed_family = strdup (family);
+  fixed_style = strdup (style);
+  fixed_size = be_fixed_font->Size ();
+
+  be_plain_font->GetFamilyAndStyle (&family, &style);
+  default_family = strdup (family);
+  default_style = strdup (style);
+  default_size = be_plain_font->Size ();
+
+  rc = find_directory (B_USER_SETTINGS_DIRECTORY, &path);
+
+  if (rc < B_OK)
+    return;
+
+  rc = path.Append ("system/app_server/fonts");
+
+  if (rc < B_OK)
+    return;
+
+  rc = node.SetTo (path.Path ());
+
+  if (rc < B_OK)
+    return;
+
+  if (node.GetNodeRef (&node_ref) < B_OK)
+    return;
+
+  looper = new EmacsFontMonitorLooper;
+
+  if (watch_node (&node_ref, B_WATCH_STAT, looper) < B_OK)
+    {
+      delete looper;
+      return;
+    }
+
+  looper->Run ();
+}
+
+bool
+be_lock_font_defaults (void)
+{
+  return default_locker.Lock ();
+}
+
+void
+be_unlock_font_defaults (void)
+{
+  return default_locker.Unlock ();
+}
+
+const char *
+be_get_font_default (enum haiku_what_font what)
+{
+  switch (what)
+    {
+    case FIXED_FAMILY:
+      return fixed_family;
+
+    case FIXED_STYLE:
+      return fixed_style;
+
+    case DEFAULT_FAMILY:
+      return default_family;
+
+    case DEFAULT_STYLE:
+      return default_style;
+    }
+
+  return NULL;
+}
+
+int
+be_get_font_size (enum haiku_what_font what)
+{
+  switch (what)
+    {
+    case FIXED_FAMILY:
+      return fixed_size;
+
+    case DEFAULT_FAMILY:
+      return default_size;
+
+    default:
+      return 0;
+    }
+}
diff --git a/src/haiku_io.c b/src/haiku_io.c
index 5cc70f6f71..6ef6f2ebd0 100644
--- a/src/haiku_io.c
+++ b/src/haiku_io.c
@@ -109,6 +109,8 @@ haiku_len (enum haiku_event_type type)
       return sizeof (struct haiku_screen_changed_event);
     case CLIPBOARD_CHANGED_EVENT:
       return sizeof (struct haiku_clipboard_changed_event);
+    case FONT_CHANGE_EVENT:
+      return sizeof (struct haiku_font_change_event);
     }
 
   emacs_abort ();
diff --git a/src/haiku_support.cc b/src/haiku_support.cc
index 983928442a..0f8e26d0db 100644
--- a/src/haiku_support.cc
+++ b/src/haiku_support.cc
@@ -54,12 +54,14 @@ along with GNU Emacs.  If not, see 
<https://www.gnu.org/licenses/>.  */
 #include <game/WindowScreen.h>
 #include <game/DirectWindow.h>
 
+#include <storage/FindDirectory.h>
 #include <storage/Entry.h>
 #include <storage/Path.h>
 #include <storage/FilePanel.h>
 #include <storage/AppFileInfo.h>
 #include <storage/Path.h>
 #include <storage/PathFinder.h>
+#include <storage/Node.h>
 
 #include <support/Beep.h>
 #include <support/DataIO.h>
@@ -5501,3 +5503,54 @@ be_set_use_frame_synchronization (void *view, bool sync)
   vw = (EmacsView *) view;
   vw->SetFrameSynchronization (sync);
 }
+
+status_t
+be_write_node_message (const char *path, const char *name, void *message)
+{
+  BNode node (path);
+  status_t rc;
+  ssize_t flat, result;
+  char *buffer;
+  BMessage *msg;
+
+  rc = node.InitCheck ();
+  msg = (BMessage *) message;
+
+  if (rc < B_OK)
+    return rc;
+
+  flat = msg->FlattenedSize ();
+  if (flat < B_OK)
+    return flat;
+
+  buffer = new (std::nothrow) char[flat];
+  if (!buffer)
+    return B_NO_MEMORY;
+
+  rc = msg->Flatten (buffer, flat);
+  if (rc < B_OK)
+    {
+      delete[] buffer;
+      return rc;
+    }
+
+  result = node.WriteAttr (name, B_MIME_TYPE, 0,
+                          buffer, flat);
+  delete[] buffer;
+
+  if (result < B_OK)
+    return result;
+
+  if (result != flat)
+    return B_ERROR;
+
+  return B_OK;
+}
+
+void
+be_send_message (const char *app_id, void *message)
+{
+  BMessenger messenger (app_id);
+
+  messenger.SendMessage ((BMessage *) message);
+}
diff --git a/src/haiku_support.h b/src/haiku_support.h
index ca1808556a..e940e69bf1 100644
--- a/src/haiku_support.h
+++ b/src/haiku_support.h
@@ -115,6 +115,7 @@ enum haiku_event_type
     SCREEN_CHANGED_EVENT,
     MENU_BAR_LEFT,
     CLIPBOARD_CHANGED_EVENT,
+    FONT_CHANGE_EVENT,
   };
 
 struct haiku_clipboard_changed_event
@@ -442,6 +443,27 @@ struct haiku_menu_bar_state_event
   void *window;
 };
 
+enum haiku_what_font
+  {
+    FIXED_FAMILY,
+    FIXED_STYLE,
+    DEFAULT_FAMILY,
+    DEFAULT_STYLE,
+  };
+
+struct haiku_font_change_event
+{
+  /* New family, style and size of the font.  */
+  haiku_font_family_or_style new_family;
+  haiku_font_family_or_style new_style;
+  int new_size;
+
+  /* What changed.  FIXED_FAMILY means this is the new fixed font.
+     DEFAULT_FAMILY means this is the new plain font.  The other enums
+     have no meaning.  */
+  enum haiku_what_font what;
+};
+
 struct haiku_session_manager_reply
 {
   bool quit_reply;
@@ -697,7 +719,7 @@ extern int be_get_display_screens (void);
 extern bool be_use_subpixel_antialiasing (void);
 extern const char *be_find_setting (const char *);
 extern haiku_font_family_or_style *be_list_font_families (size_t *);
-extern void be_font_style_to_flags (char *, struct haiku_font_pattern *);
+extern void be_font_style_to_flags (const char *, struct haiku_font_pattern *);
 extern void *be_open_font_at_index (int, int, float);
 extern void be_set_font_antialiasing (void *, bool);
 extern int be_get_ui_color (const char *, uint32_t *);
@@ -724,11 +746,21 @@ extern void be_get_window_decorator_frame (void *, int *, 
int *, int *, int *);
 extern void be_send_move_frame_event (void *);
 extern void be_set_window_fullscreen_mode (void *, enum haiku_fullscreen_mode);
 
+extern status_t be_write_node_message (const char *, const char *, void *);
+extern void be_send_message (const char *, void *);
+
 extern void be_lock_window (void *);
 extern void be_unlock_window (void *);
 extern bool be_get_explicit_workarea (int *, int *, int *, int *);
 extern void be_clear_grab_view (void);
 extern void be_set_use_frame_synchronization (void *, bool);
+
+extern void be_listen_font_settings (void);
+
+extern bool be_lock_font_defaults (void);
+extern const char *be_get_font_default (enum haiku_what_font);
+extern int be_get_font_size (enum haiku_what_font);
+extern void be_unlock_font_defaults (void);
 #ifdef __cplusplus
 }
 
diff --git a/src/haikufns.c b/src/haikufns.c
index aaa4e86622..711202c5df 100644
--- a/src/haikufns.c
+++ b/src/haikufns.c
@@ -2636,8 +2636,7 @@ DEFUN ("x-show-tip", Fx_show_tip, Sx_show_tip, 1, 6, 0,
 
  start_timer:
   /* Let the tip disappear after timeout seconds.  */
-  tip_timer = call3 (intern ("run-at-time"), timeout, Qnil,
-                    intern ("x-hide-tip"));
+  tip_timer = call3 (Qrun_at_time, timeout, Qnil, Qx_hide_tip);
 
   return unbind_to (count, Qnil);
 }
@@ -3149,6 +3148,9 @@ syms_of_haikufns (void)
   DEFSYM (Qcancel_timer, "cancel-timer");
   DEFSYM (Qassq_delete_all, "assq-delete-all");
 
+  DEFSYM (Qrun_at_time, "run-at-time");
+  DEFSYM (Qx_hide_tip, "x-hide-tip");
+
   DEFSYM (Qalways, "always");
   DEFSYM (Qnot_useful, "not-useful");
   DEFSYM (Qwhen_mapped, "when-mapped");
diff --git a/src/haikufont.c b/src/haikufont.c
index 3e7f6f86dc..335c312ceb 100644
--- a/src/haikufont.c
+++ b/src/haikufont.c
@@ -370,7 +370,7 @@ haikufont_maybe_handle_special_family (Lisp_Object family,
       BFont_populate_fixed_family (ptn);
       return 1;
     }
-  else if (EQ (family, intern ("Sans Serif")))
+  else if (EQ (family, QSans_Serif))
     {
       BFont_populate_plain_family (ptn);
       return 1;
@@ -1311,6 +1311,98 @@ in the font selection dialog.  */)
                QCsize, lsize);
 }
 
+DEFUN ("font-get-system-normal-font", Ffont_get_system_normal_font,
+       Sfont_get_system_normal_font, 0, 0, 0,
+       doc: /* SKIP: real doc in xsettings.c.  */)
+  (void)
+{
+  Lisp_Object value;
+  const char *name, *style;
+  struct haiku_font_pattern pattern;
+  Lisp_Object lfamily, lweight, lslant, lwidth, ladstyle;
+  int size;
+
+  if (!be_lock_font_defaults ())
+    return Qnil;
+
+  name = be_get_font_default (DEFAULT_FAMILY);
+  style = be_get_font_default (DEFAULT_STYLE);
+  size = be_get_font_size (DEFAULT_FAMILY);
+
+  be_font_style_to_flags (style, &pattern);
+
+  lfamily = build_string_from_utf8 (name);
+  lweight = (pattern.specified & FSPEC_WEIGHT
+            ? haikufont_weight_to_lisp (pattern.weight) : Qnil);
+  lslant = (pattern.specified & FSPEC_SLANT
+           ? haikufont_slant_to_lisp (pattern.slant) : Qnil);
+  lwidth = (pattern.specified & FSPEC_WIDTH
+           ? haikufont_width_to_lisp (pattern.width) : Qnil);
+  ladstyle = (pattern.specified & FSPEC_STYLE
+             ? intern (pattern.style) : Qnil);
+
+  value = CALLN (Ffont_spec, QCfamily, lfamily,
+                QCweight, lweight, QCslant, lslant,
+                QCwidth, lwidth, QCadstyle, ladstyle,
+                QCsize, make_fixnum (size));
+  be_unlock_font_defaults ();
+
+  return value;
+}
+
+DEFUN ("font-get-system-font", Ffont_get_system_font,
+       Sfont_get_system_font, 0, 0, 0,
+       doc: /* SKIP: real doc in xsettings.c.  */)
+  (void)
+{
+  Lisp_Object value;
+  const char *name, *style;
+  struct haiku_font_pattern pattern;
+  Lisp_Object lfamily, lweight, lslant, lwidth, ladstyle;
+  int size;
+
+  if (!be_lock_font_defaults ())
+    return Qnil;
+
+  name = be_get_font_default (FIXED_FAMILY);
+  style = be_get_font_default (FIXED_STYLE);
+  size = be_get_font_size (FIXED_FAMILY);
+
+  be_font_style_to_flags (style, &pattern);
+
+  lfamily = build_string_from_utf8 (name);
+  lweight = (pattern.specified & FSPEC_WEIGHT
+            ? haikufont_weight_to_lisp (pattern.weight) : Qnil);
+  lslant = (pattern.specified & FSPEC_SLANT
+           ? haikufont_slant_to_lisp (pattern.slant) : Qnil);
+  lwidth = (pattern.specified & FSPEC_WIDTH
+           ? haikufont_width_to_lisp (pattern.width) : Qnil);
+  ladstyle = (pattern.specified & FSPEC_STYLE
+             ? intern (pattern.style) : Qnil);
+
+  value = CALLN (Ffont_spec, QCfamily, lfamily,
+                QCweight, lweight, QCslant, lslant,
+                QCwidth, lwidth, QCadstyle, ladstyle,
+                QCsize, make_fixnum (size));
+  be_unlock_font_defaults ();
+
+  return value;
+}
+
+void
+haiku_handle_font_change_event (struct haiku_font_change_event *event,
+                               struct input_event *ie)
+{
+  ie->kind = CONFIG_CHANGED_EVENT;
+
+  /* This is the name of the display.  */
+  ie->frame_or_window = XCAR (x_display_list->name_list_element);
+
+  /* And this is the font that changed.  */
+  ie->arg = (event->what == FIXED_FAMILY
+            ? Qmonospace_font_name : Qfont_name);
+}
+
 static void
 syms_of_haikufont_for_pdumper (void)
 {
@@ -1320,6 +1412,7 @@ syms_of_haikufont_for_pdumper (void)
 void
 syms_of_haikufont (void)
 {
+  DEFSYM (QSans_Serif, "Sans Serif");
   DEFSYM (Qfontsize, "fontsize");
   DEFSYM (Qfixed, "fixed");
   DEFSYM (Qplain, "plain");
@@ -1343,6 +1436,14 @@ syms_of_haikufont (void)
 
   DEFSYM (QCindices, ":indices");
 
+  DEFSYM (Qmonospace_font_name, "monospace-font-name");
+  DEFSYM (Qfont_name, "font-name");
+  DEFSYM (Qdynamic_setting, "dynamic-setting");
+
+  DEFVAR_BOOL ("font-use-system-font", use_system_font,
+    doc: /* SKIP: real doc in xsettings.c.  */);
+  use_system_font = false;
+
 #ifdef USE_BE_CAIRO
   Fput (Qhaiku, Qfont_driver_superseded_by, Qftcr);
 #endif
@@ -1352,6 +1453,12 @@ syms_of_haikufont (void)
   staticpro (&font_cache);
 
   defsubr (&Sx_select_font);
+  defsubr (&Sfont_get_system_normal_font);
+  defsubr (&Sfont_get_system_font);
 
   be_init_font_data ();
+
+  /* This tells loadup to load dynamic-setting.el, which handles
+     config-changed events.  */
+  Fprovide (Qdynamic_setting, Qnil);
 }
diff --git a/src/haikuselect.c b/src/haikuselect.c
index 7eb93a2754..bd004f4900 100644
--- a/src/haikuselect.c
+++ b/src/haikuselect.c
@@ -325,6 +325,15 @@ haiku_message_to_lisp (void *message)
              t1 = make_float (*(float *) buf);
              break;
 
+           case 'CSTR':
+             /* Is this even possible? */
+             if (!buf_size)
+               buf_size = 1;
+
+             t1 = make_uninit_string (buf_size - 1);
+             memcpy (SDATA (t1), buf, buf_size - 1);
+             break;
+
            default:
              t1 = make_uninit_string (buf_size);
              memcpy (SDATA (t1), buf, buf_size);
@@ -747,6 +756,21 @@ haiku_lisp_to_message (Lisp_Object obj, void *message)
                signal_error ("Failed to add bool", data);
              break;
 
+           case 'CSTR':
+             /* C strings must be handled specially, since they
+                include a trailing NULL byte.  */
+             CHECK_STRING (data);
+
+             block_input ();
+             rc = be_add_message_data (message, SSDATA (name),
+                                       type_code, SDATA (data),
+                                       SBYTES (data) + 1);
+             unblock_input ();
+
+             if (rc)
+               signal_error ("Failed to add", data);
+             break;
+
            default:
            decode_normally:
              CHECK_STRING (data);
@@ -779,6 +803,49 @@ haiku_unwind_drag_message (void *message)
   BMessage_delete (message);
 }
 
+static void
+haiku_report_system_error (status_t code, const char *format)
+{
+  switch (code)
+    {
+    case B_BAD_VALUE:
+      error (format, "Bad value");
+      break;
+
+    case B_ENTRY_NOT_FOUND:
+      error (format, "File not found");
+      break;
+
+    case B_PERMISSION_DENIED:
+      error (format, "Permission denied");
+      break;
+
+    case B_LINK_LIMIT:
+      error (format, "Link limit reached");
+      break;
+
+    case B_BUSY:
+      error (format, "Device busy");
+      break;
+
+    case B_NO_MORE_FDS:
+      error (format, "No more file descriptors");
+      break;
+
+    case B_FILE_ERROR:
+      error (format, "File error");
+      break;
+
+    case B_NO_MEMORY:
+      memory_full (SIZE_MAX);
+      break;
+
+    default:
+      error (format, "Unknown error");
+      break;
+    }
+}
+
 DEFUN ("haiku-drag-message", Fhaiku_drag_message, Shaiku_drag_message,
        2, 4, 0,
        doc: /* Begin dragging MESSAGE from FRAME.
@@ -958,6 +1025,66 @@ after it starts.  */)
   return SAFE_FREE_UNBIND_TO (depth, Qnil);
 }
 
+DEFUN ("haiku-write-node-attribute", Fhaiku_write_node_attribute,
+       Shaiku_write_node_attribute, 3, 3, 0,
+       doc: /* Write a message as a file-system attribute of NODE.
+FILE should be a file name of a file on a Be File System volume, NAME
+should be a string describing the name of the attribute that will be
+written, and MESSAGE will be the attribute written to FILE, as a
+system message in the format accepted by `haiku-drag-message', which
+see.  */)
+  (Lisp_Object file, Lisp_Object name, Lisp_Object message)
+{
+  void *be_message;
+  status_t rc;
+  specpdl_ref count;
+
+  CHECK_STRING (file);
+  CHECK_STRING (name);
+
+  file = ENCODE_FILE (file);
+  name = ENCODE_SYSTEM (name);
+
+  be_message = be_create_simple_message ();
+  count = SPECPDL_INDEX ();
+
+  record_unwind_protect_ptr (BMessage_delete, be_message);
+  haiku_lisp_to_message (message, be_message);
+  rc = be_write_node_message (SSDATA (file), SSDATA (name),
+                             be_message);
+
+  if (rc < B_OK)
+    haiku_report_system_error (rc, "Failed to set attribute: %s");
+
+  return unbind_to (count, Qnil);
+}
+
+DEFUN ("haiku-send-message", Fhaiku_send_message, Shaiku_send_message,
+       2, 2, 0,
+       doc: /* Send a system message to PROGRAM.
+PROGRAM must be the name of the application to which the message will
+be sent.  MESSAGE is the system message, serialized in the format
+accepted by `haiku-drag-message', that will be sent to the application
+specified by PROGRAM.  There is no guarantee that the message will
+arrive after this function is called.  */)
+  (Lisp_Object program, Lisp_Object message)
+{
+  specpdl_ref count;
+  void *be_message;
+
+  CHECK_STRING (program);
+  program = ENCODE_SYSTEM (program);
+
+  be_message = be_create_simple_message ();
+  count = SPECPDL_INDEX ();
+
+  record_unwind_protect_ptr (BMessage_delete, be_message);
+  haiku_lisp_to_message (message, be_message);
+  be_send_message (SSDATA (program), be_message);
+
+  return unbind_to (count, Qnil);
+}
+
 static void
 haiku_dnd_compute_tip_xy (int *root_x, int *root_y)
 {
@@ -1191,6 +1318,8 @@ keyboard modifiers currently held down.  */);
   defsubr (&Shaiku_selection_owner_p);
   defsubr (&Shaiku_drag_message);
   defsubr (&Shaiku_roster_launch);
+  defsubr (&Shaiku_write_node_attribute);
+  defsubr (&Shaiku_send_message);
 
   haiku_dnd_frame = NULL;
 }
diff --git a/src/haikuterm.c b/src/haikuterm.c
index df1c39974f..838eb128fa 100644
--- a/src/haikuterm.c
+++ b/src/haikuterm.c
@@ -2988,18 +2988,11 @@ haiku_default_font_parameter (struct frame *f, 
Lisp_Object parms)
     font_param = Qnil;
 
   if (NILP (font_param))
-    {
-      /* System font should take precedence over X resources.  We suggest this
-         regardless of font-use-system-font because .emacs may not have been
-         read yet.  */
-      struct haiku_font_pattern ptn;
-      ptn.specified = 0;
-
-      BFont_populate_fixed_family (&ptn);
-
-      if (ptn.specified & FSPEC_FAMILY)
-       font = font_open_by_name (f, build_unibyte_string (ptn.family));
-    }
+    /* System font should take precedence over X resources.  We
+       suggest this regardless of font-use-system-font because .emacs
+       may not have been read yet.  Returning a font-spec is Haiku
+       specific behavior.  */
+    font = font_open_by_spec (f, Ffont_get_system_font ());
 
   if (NILP (font))
       font = !NILP (font_param) ? font_param
@@ -4027,6 +4020,11 @@ haiku_read_socket (struct terminal *terminal, struct 
input_event *hold_quit)
          inev.kind = SAVE_SESSION_EVENT;
          inev.arg = Qt;
          break;
+       case FONT_CHANGE_EVENT:
+         /* This generates CONFIG_CHANGED_EVENTs, which are then
+            handled in Lisp.  */
+         haiku_handle_font_change_event (buf, &inev);
+         break;
        case KEY_UP:
        case DUMMY_EVENT:
        default:
@@ -4349,7 +4347,7 @@ haiku_term_init (void)
     emacs_abort ();
 
   color_file = Fexpand_file_name (build_string ("rgb.txt"),
-                                 Fsymbol_value (intern ("data-directory")));
+                                 Fsymbol_value (Qdata_directory));
   color_map = Fx_load_color_file (color_file);
 
   if (NILP (color_map))
@@ -4417,6 +4415,9 @@ haiku_term_init (void)
     dpyinfo->default_name = build_string ("GNU Emacs");
 
   haiku_start_watching_selections ();
+
+  /* Start listening for font configuration changes.  */
+  be_listen_font_settings ();
   unblock_input ();
 
   return dpyinfo;
@@ -4634,6 +4635,8 @@ syms_of_haikuterm (void)
   DEFSYM (Qoption, "option");
   DEFSYM (Qcommand, "command");
 
+  DEFSYM (Qdata_directory, "data-directory");
+
   DEFVAR_LISP ("haiku-meta-keysym", Vhaiku_meta_keysym,
      doc: /* Which key Emacs uses as the meta modifier.
 This is either one of the symbols `shift', `control', `command', and
diff --git a/src/haikuterm.h b/src/haikuterm.h
index b603c0a482..86274fd42a 100644
--- a/src/haikuterm.h
+++ b/src/haikuterm.h
@@ -34,6 +34,9 @@ along with GNU Emacs.  If not, see 
<https://www.gnu.org/licenses/>.  */
 
 #define HAVE_CHAR_CACHE_MAX 65535
 
+/* This is really defined in haiku_support.h.  */
+struct haiku_font_change_event;
+
 extern int popup_activated_p;
 
 struct haikufont_info
@@ -361,4 +364,7 @@ extern void haiku_merge_cursor_foreground (struct 
glyph_string *, unsigned long
                                           unsigned long *);
 extern void haiku_handle_selection_clear (struct input_event *);
 extern void haiku_start_watching_selections (void);
+extern void haiku_handle_font_change_event (struct haiku_font_change_event *,
+                                           struct input_event *);
+
 #endif /* _HAIKU_TERM_H_ */
diff --git a/src/hbfont.c b/src/hbfont.c
index 2721a66120..476e08020e 100644
--- a/src/hbfont.c
+++ b/src/hbfont.c
@@ -249,7 +249,7 @@ uni_combining (hb_unicode_funcs_t *funcs, hb_codepoint_t 
ch, void *user_data)
   if (!combining_class_loaded)
     {
       canonical_combining_class_table =
-       uniprop_table (intern ("canonical-combining-class"));
+       uniprop_table (Qcanonical_combining_class);
       if (NILP (canonical_combining_class_table))
        emacs_abort ();
       staticpro (&canonical_combining_class_table);
diff --git a/src/image.c b/src/image.c
index f5004c2c4c..1e323ba66a 100644
--- a/src/image.c
+++ b/src/image.c
@@ -1,6 +1,6 @@
 /* Functions for image support on window system.
 
-Copyright (C) 1989, 1992-2022 Free Software Foundation, Inc.
+Copyright (C) 1989-2022 Free Software Foundation, Inc.
 
 This file is part of GNU Emacs.
 
@@ -10907,7 +10907,7 @@ DEF_DLL_FN (int, gdk_pixbuf_get_bits_per_sample, (const 
GdkPixbuf *));
 DEF_DLL_FN (void, g_type_init, (void));
 #  endif
 DEF_DLL_FN (void, g_object_unref, (gpointer));
-DEF_DLL_FN (void, g_clear_error, (GError **));
+DEF_DLL_FN (void, g_error_free, (GError *));
 
 static bool
 init_svg_functions (void)
@@ -10967,7 +10967,7 @@ init_svg_functions (void)
   LOAD_DLL_FN (gobject, g_type_init);
 #  endif
   LOAD_DLL_FN (gobject, g_object_unref);
-  LOAD_DLL_FN (glib, g_clear_error);
+  LOAD_DLL_FN (glib, g_error_free);
 
   return 1;
 }
@@ -10983,7 +10983,7 @@ init_svg_functions (void)
 #  undef gdk_pixbuf_get_pixels
 #  undef gdk_pixbuf_get_rowstride
 #  undef gdk_pixbuf_get_width
-#  undef g_clear_error
+#  undef g_error_free
 #  undef g_object_unref
 #  undef g_type_init
 #  if LIBRSVG_CHECK_VERSION (2, 52, 1)
@@ -11019,7 +11019,7 @@ init_svg_functions (void)
 #  define gdk_pixbuf_get_pixels fn_gdk_pixbuf_get_pixels
 #  define gdk_pixbuf_get_rowstride fn_gdk_pixbuf_get_rowstride
 #  define gdk_pixbuf_get_width fn_gdk_pixbuf_get_width
-#  define g_clear_error fn_g_clear_error
+#  define g_error_free fn_g_error_free
 #  define g_object_unref fn_g_object_unref
 #  if ! GLIB_CHECK_VERSION (2, 36, 0)
 #   define g_type_init fn_g_type_init
@@ -11183,6 +11183,10 @@ svg_load_image (struct frame *f, struct image *img, 
char *contents,
   char *wrapped_contents = NULL;
   ptrdiff_t wrapped_size;
 
+  bool empty_errmsg = true;
+  const char *errmsg = "";
+  ptrdiff_t errlen = 0;
+
 #if LIBRSVG_CHECK_VERSION (2, 48, 0)
   char *css = NULL;
 #endif
@@ -11353,7 +11357,7 @@ svg_load_image (struct frame *f, struct image *img, 
char *contents,
   if (! check_image_size (f, width, height))
     {
       image_size_error ();
-      goto rsvg_error;
+      goto done_error;
     }
 
   /* We are now done with the unmodified data.  */
@@ -11491,7 +11495,7 @@ svg_load_image (struct frame *f, struct image *img, 
char *contents,
     if (!image_create_x_image_and_pixmap (f, img, width, height, 0, &ximg, 0))
       {
        g_object_unref (pixbuf);
-       return 0;
+       return false;
       }
 
     init_color_table ();
@@ -11536,9 +11540,30 @@ svg_load_image (struct frame *f, struct image *img, 
char *contents,
     image_put_x_image (f, img, ximg, 0);
   }
 
-  return 1;
+  eassume (err == NULL);
+  return true;
 
  rsvg_error:
+  if (err && err->message[0])
+    {
+      errmsg = err->message;
+      errlen = strlen (errmsg);
+      /* Remove trailing whitespace from the error message text.  It
+        has a newline at the end, and perhaps more whitespace.  */
+      while (errlen && c_isspace (errmsg[errlen - 1]))
+       errlen--;
+      empty_errmsg = errlen == 0;
+    }
+
+  if (empty_errmsg)
+    image_error ("Error parsing SVG image");
+  else
+    image_error ("Error parsing SVG image: %s", make_string (errmsg, errlen));
+
+  if (err)
+    g_error_free (err);
+
+ done_error:
   if (rsvg_handle)
     g_object_unref (rsvg_handle);
   if (wrapped_contents)
@@ -11547,11 +11572,7 @@ svg_load_image (struct frame *f, struct image *img, 
char *contents,
   if (css && !STRINGP (lcss))
     xfree (css);
 #endif
-  /* FIXME: Use error->message so the user knows what is the actual
-     problem with the image.  */
-  image_error ("Error parsing SVG image");
-  g_clear_error (&err);
-  return 0;
+  return false;
 }
 
 #endif /* defined (HAVE_RSVG) */
@@ -11817,9 +11838,6 @@ x_kill_gs_process (Pixmap pixmap, struct frame *f)
 /***********************************************************************
                                Tests
  ***********************************************************************/
-
-#ifdef GLYPH_DEBUG
-
 DEFUN ("imagep", Fimagep, Simagep, 1, 1, 0,
        doc: /* Value is non-nil if SPEC is a valid image specification.  */)
   (Lisp_Object spec)
@@ -11827,6 +11845,7 @@ DEFUN ("imagep", Fimagep, Simagep, 1, 1, 0,
   return valid_image_p (spec) ? Qt : Qnil;
 }
 
+#ifdef GLYPH_DEBUG
 
 DEFUN ("lookup-image", Flookup_image, Slookup_image, 1, 1, 0,
        doc: /* */)
@@ -12219,9 +12238,9 @@ non-numeric, there is no explicit limit on the size of 
images.  */);
   defsubr (&Simage_mask_p);
   defsubr (&Simage_metadata);
   defsubr (&Simage_cache_size);
+  defsubr (&Simagep);
 
 #ifdef GLYPH_DEBUG
-  defsubr (&Simagep);
   defsubr (&Slookup_image);
 #endif
 
@@ -12264,5 +12283,4 @@ The options are:
   /* MagickExportImagePixels is in 6.4.6-9, but not 6.4.4-10.  */
   imagemagick_render_type = 0;
 #endif
-
 }
diff --git a/src/intervals.c b/src/intervals.c
index 85152c58a5..7f11981557 100644
--- a/src/intervals.c
+++ b/src/intervals.c
@@ -2171,8 +2171,8 @@ get_property_and_range (ptrdiff_t pos, Lisp_Object prop, 
Lisp_Object *val,
 
 /* Return the proper local keymap TYPE for position POSITION in
    BUFFER; TYPE should be one of `keymap' or `local-map'.  Use the map
-   specified by the PROP property, if any.  Otherwise, if TYPE is
-   `local-map' use BUFFER's local map.  */
+   specified by the TYPE property, if any.  Otherwise, if TYPE is
+   `local-map', use BUFFER's local map.  */
 
 Lisp_Object
 get_local_map (ptrdiff_t position, struct buffer *buffer, Lisp_Object type)
diff --git a/src/keyboard.c b/src/keyboard.c
index 1d7125a0a3..8ab4a451b4 100644
--- a/src/keyboard.c
+++ b/src/keyboard.c
@@ -499,27 +499,18 @@ echo_add_key (Lisp_Object c)
                        STRING_MULTIBYTE (name), 1);
     }
 
+  Lisp_Object new_string = make_string (buffer, ptr - buffer);
   if ((NILP (echo_string) || SCHARS (echo_string) == 0)
       && help_char_p (c))
     {
-      static const char text[] = " (Type ? for further options)";
-      int len = sizeof text - 1;
-
-      if (size - (ptr - buffer) < len)
-       {
-         ptrdiff_t offset = ptr - buffer;
-         size += len;
-         buffer = SAFE_ALLOCA (size);
-         ptr = buffer + offset;
-       }
-
-      memcpy (ptr, text, len);
-      ptr += len;
+      AUTO_STRING (str, " (Type ? for further options)");
+      AUTO_LIST2 (props, Qface, Qhelp_key_binding);
+      Fadd_text_properties (make_fixnum (7), make_fixnum (8), props, str);
+      new_string = concat2 (new_string, str);
     }
 
-  kset_echo_string
-    (current_kboard,
-     concat2 (echo_string, make_string (buffer, ptr - buffer)));
+  kset_echo_string (current_kboard,
+                   concat2 (echo_string, new_string));
   SAFE_FREE ();
 }
 
@@ -1827,21 +1818,15 @@ adjust_point_for_property (ptrdiff_t last_pt, bool 
modified)
     }
 }
 
-/* Subroutine for safe_run_hooks: run the hook, which is ARGS[1].  */
+/* Subroutine for safe_run_hooks: run the hook's function.
+   ARGS[0] holds the name of the hook, which we don't need here (we only use
+   it in the failure case of the internal_condition_case_n).  */
 
 static Lisp_Object
 safe_run_hooks_1 (ptrdiff_t nargs, Lisp_Object *args)
 {
-  eassert (nargs >= 2 && nargs <= 4);
-  switch (nargs)
-    {
-    case 2:
-      return call0 (args[1]);
-    case 3:
-      return call1 (args[1], args[2]);
-    default:
-      return call2 (args[1], args[2], args[3]);
-    }
+  eassert (nargs >= 2);
+  return Ffuncall (nargs - 1, args + 1);
 }
 
 /* Subroutine for safe_run_hooks: handle an error by clearing out the function
@@ -1850,7 +1835,7 @@ safe_run_hooks_1 (ptrdiff_t nargs, Lisp_Object *args)
 static Lisp_Object
 safe_run_hooks_error (Lisp_Object error, ptrdiff_t nargs, Lisp_Object *args)
 {
-  eassert (nargs >= 2 && nargs <= 4);
+  eassert (nargs >= 2);
   AUTO_STRING (format, "Error in %s (%S): %S");
   Lisp_Object hook = args[0];
   Lisp_Object fun = args[1];
@@ -1886,27 +1871,22 @@ safe_run_hooks_error (Lisp_Object error, ptrdiff_t 
nargs, Lisp_Object *args)
 static Lisp_Object
 safe_run_hook_funcall (ptrdiff_t nargs, Lisp_Object *args)
 {
-  eassert (nargs >= 2 && nargs <= 4);
-  /* Yes, run_hook_with_args works with args in the other order.  */
-  switch (nargs)
-    {
-    case 2:
-      internal_condition_case_n (safe_run_hooks_1,
-                                2, ((Lisp_Object []) {args[1], args[0]}),
-                                Qt, safe_run_hooks_error);
-      break;
-    case 3:
-      internal_condition_case_n (safe_run_hooks_1,
-                                3, ((Lisp_Object []) {args[1], args[0], 
args[2]}),
-                                Qt, safe_run_hooks_error);
-      break;
-    default:
-      internal_condition_case_n (safe_run_hooks_1,
-                                4, ((Lisp_Object [])
-                                    {args[1], args[0], args[2], args[3]}),
-                                Qt, safe_run_hooks_error);
-      break;
-    }
+  /* We need to swap args[0] and args[1] here or in `safe_run_hooks_1`.
+     It's more convenient to do it here.  */
+  eassert (nargs >= 2);
+  Lisp_Object fun = args[0], hook = args[1];
+  /* The `nargs` array cannot be mutated safely here because it is
+     reused by our caller `run_hook_with_args`.
+     We could arguably change it temporarily if we set it back
+     to its original state before returning, but it's too ugly.  */
+  USE_SAFE_ALLOCA;
+  Lisp_Object *newargs;
+  SAFE_ALLOCA_LISP (newargs, nargs);
+  newargs[0] = hook, newargs[1] = fun;
+  memcpy (newargs + 2, args + 2, (nargs - 2) * word_size);
+  internal_condition_case_n (safe_run_hooks_1, nargs, newargs,
+                             Qt, safe_run_hooks_error);
+  SAFE_FREE ();
   return Qnil;
 }
 
@@ -1920,7 +1900,8 @@ safe_run_hooks (Lisp_Object hook)
   specpdl_ref count = SPECPDL_INDEX ();
 
   specbind (Qinhibit_quit, Qt);
-  run_hook_with_args (2, ((Lisp_Object []) {hook, hook}), 
safe_run_hook_funcall);
+  run_hook_with_args (2, ((Lisp_Object []) {hook, hook}),
+                      safe_run_hook_funcall);
   unbind_to (count, Qnil);
 }
 
@@ -1936,7 +1917,8 @@ safe_run_hooks_maybe_narrowed (Lisp_Object hook, struct 
window *w)
                               make_fixnum (get_narrowed_zv (w, PT)),
                               true);
 
-  run_hook_with_args (2, ((Lisp_Object []) {hook, hook}), 
safe_run_hook_funcall);
+  run_hook_with_args (2, ((Lisp_Object []) {hook, hook}),
+                      safe_run_hook_funcall);
   unbind_to (count, Qnil);
 }
 
@@ -11806,6 +11788,9 @@ DEFUN ("posn-at-point", Fposn_at_point, Sposn_at_point, 
0, 2, 0,
        doc: /* Return position information for buffer position POS in WINDOW.
 POS defaults to point in WINDOW; WINDOW defaults to the selected window.
 
+If POS is in invisible text or is hidden by `display' properties,
+this function may report on buffer positions before or after POS.
+
 Return nil if POS is not visible in WINDOW.  Otherwise,
 the return value is similar to that returned by `event-start' for
 a mouse click at the upper left corner of the glyph corresponding
@@ -12258,6 +12243,8 @@ syms_of_keyboard (void)
 
   DEFSYM (Qhelp_form_show, "help-form-show");
 
+  DEFSYM (Qhelp_key_binding, "help-key-binding");
+
   DEFSYM (Qecho_keystrokes, "echo-keystrokes");
 
   Fset (Qinput_method_exit_on_first_char, Qnil);
diff --git a/src/lisp.h b/src/lisp.h
index 0281c483e3..1e41e2064c 100644
--- a/src/lisp.h
+++ b/src/lisp.h
@@ -245,7 +245,8 @@ DEFINE_GDB_SYMBOL_BEGIN (EMACS_INT, VALMASK)
 DEFINE_GDB_SYMBOL_END (VALMASK)
 
 /* Ignore 'alignas' on compilers lacking it.  */
-#if !defined alignas && !defined __alignas_is_defined
+#if (!defined alignas && !defined __alignas_is_defined \
+     && __STDC_VERSION__ < 202311 && __cplusplus < 201103)
 # define alignas(a)
 #endif
 
diff --git a/src/lread.c b/src/lread.c
index 06fac7185b..37ee3a00ec 100644
--- a/src/lread.c
+++ b/src/lread.c
@@ -1423,6 +1423,7 @@ Return t if the file exists and loads successfully.  */)
          struct stat s1, s2;
          int result;
 
+         struct timespec epoch_timespec = {(time_t)0, 0}; /* 1970-01-01T00:00 
UTC */
          if (version < 0 && !(version = safe_to_load_version (file, fd)))
            error ("File `%s' was not compiled in Emacs", SDATA (found));
 
@@ -1451,7 +1452,12 @@ Return t if the file exists and loads successfully.  */)
                   newer = 1;
 
                   /* If we won't print another message, mention this anyway.  
*/
-                  if (!NILP (nomessage) && !force_load_messages)
+                  if (!NILP (nomessage) && !force_load_messages
+                     /* We don't want this message during
+                        bootstrapping for the "compile-first" .elc
+                        files, which have had their timestamps set to
+                        the epoch.  See bug #58224.  */
+                     && timespec_cmp (get_stat_mtime (&s1), epoch_timespec))
                     {
                       Lisp_Object msg_file;
                       msg_file = Fsubstring (found, make_fixnum (0), 
make_fixnum (-1));
@@ -2905,31 +2911,26 @@ digit_to_number (int character, int base)
   return digit < base ? digit : -1;
 }
 
-/* Size of the fixed-size buffer used during reading.
-   It should be at least big enough for `invalid_radix_integer' but
-   can usefully be much bigger than that.  */
-enum { stackbufsize = 1024 };
-
 static void
-invalid_radix_integer (EMACS_INT radix, char stackbuf[VLA_ELEMS 
(stackbufsize)],
-                      Lisp_Object readcharfun)
+invalid_radix_integer (EMACS_INT radix, Lisp_Object readcharfun)
 {
-  int n = snprintf (stackbuf, stackbufsize, "integer, radix %"pI"d", radix);
-  eassert (n < stackbufsize);
-  invalid_syntax (stackbuf, readcharfun);
+  char buf[64];
+  int n = snprintf (buf, sizeof buf, "integer, radix %"pI"d", radix);
+  eassert (n < sizeof buf);
+  invalid_syntax (buf, readcharfun);
 }
 
 /* Read an integer in radix RADIX using READCHARFUN to read
-   characters.  RADIX must be in the interval [2..36].  Use STACKBUF
-   for temporary storage as needed.  Value is the integer read.
+   characters.  RADIX must be in the interval [2..36].
+   Value is the integer read.
    Signal an error if encountering invalid read syntax.  */
 
 static Lisp_Object
-read_integer (Lisp_Object readcharfun, int radix,
-             char stackbuf[VLA_ELEMS (stackbufsize)])
+read_integer (Lisp_Object readcharfun, int radix)
 {
+  char stackbuf[20];
   char *read_buffer = stackbuf;
-  ptrdiff_t read_buffer_size = stackbufsize;
+  ptrdiff_t read_buffer_size = sizeof stackbuf;
   char *p = read_buffer;
   char *heapbuf = NULL;
   int valid = -1; /* 1 if valid, 0 if not, -1 if incomplete.  */
@@ -2976,7 +2977,7 @@ read_integer (Lisp_Object readcharfun, int radix,
   UNREAD (c);
 
   if (valid != 1)
-    invalid_radix_integer (radix, stackbuf, readcharfun);
+    invalid_radix_integer (radix, readcharfun);
 
   *p = '\0';
   return unbind_to (count, string_to_number (read_buffer, radix, NULL));
@@ -3030,11 +3031,11 @@ read_char_literal (Lisp_Object readcharfun)
 
 /* Read a string literal (preceded by '"').  */
 static Lisp_Object
-read_string_literal (char stackbuf[VLA_ELEMS (stackbufsize)],
-                    Lisp_Object readcharfun)
+read_string_literal (Lisp_Object readcharfun)
 {
+  char stackbuf[1024];
   char *read_buffer = stackbuf;
-  ptrdiff_t read_buffer_size = stackbufsize;
+  ptrdiff_t read_buffer_size = sizeof stackbuf;
   specpdl_ref count = SPECPDL_INDEX ();
   char *heapbuf = NULL;
   char *p = read_buffer;
@@ -3357,8 +3358,7 @@ string_props_from_rev_list (Lisp_Object elems, 
Lisp_Object readcharfun)
 
 /* Read a bool vector (preceded by "#&").  */
 static Lisp_Object
-read_bool_vector (char stackbuf[VLA_ELEMS (stackbufsize)],
-                 Lisp_Object readcharfun)
+read_bool_vector (Lisp_Object readcharfun)
 {
   ptrdiff_t length = 0;
   for (;;)
@@ -3376,7 +3376,7 @@ read_bool_vector (char stackbuf[VLA_ELEMS (stackbufsize)],
     }
 
   ptrdiff_t size_in_chars = bool_vector_bytes (length);
-  Lisp_Object str = read_string_literal (stackbuf, readcharfun);
+  Lisp_Object str = read_string_literal (readcharfun);
   if (STRING_MULTIBYTE (str)
       || !(size_in_chars == SCHARS (str)
           /* We used to print 1 char too many when the number of bits
@@ -3686,19 +3686,28 @@ read_stack_push (struct read_stack_entry e)
   rdstack.stack[rdstack.sp++] = e;
 }
 
+static void
+read_stack_reset (intmax_t sp)
+{
+  eassert (sp <= rdstack.sp);
+  rdstack.sp = sp;
+}
 
 /* Read a Lisp object.
    If LOCATE_SYMS is true, symbols are read with position.  */
 static Lisp_Object
 read0 (Lisp_Object readcharfun, bool locate_syms)
 {
-  char stackbuf[stackbufsize];
+  char stackbuf[64];
   char *read_buffer = stackbuf;
   ptrdiff_t read_buffer_size = sizeof stackbuf;
   char *heapbuf = NULL;
-  specpdl_ref count = SPECPDL_INDEX ();
 
+  specpdl_ref base_pdl = SPECPDL_INDEX ();
   ptrdiff_t base_sp = rdstack.sp;
+  record_unwind_protect_intmax (read_stack_reset, base_sp);
+
+  specpdl_ref count = SPECPDL_INDEX ();
 
   bool uninterned_symbol;
   bool skip_shorthand;
@@ -3886,7 +3895,7 @@ read0 (Lisp_Object readcharfun, bool locate_syms)
 
          case '&':
            /* #&N"..." -- bool-vector */
-           obj = read_bool_vector (stackbuf, readcharfun);
+           obj = read_bool_vector (readcharfun);
            break;
 
          case '!':
@@ -3902,17 +3911,17 @@ read0 (Lisp_Object readcharfun, bool locate_syms)
 
          case 'x':
          case 'X':
-           obj = read_integer (readcharfun, 16, stackbuf);
+           obj = read_integer (readcharfun, 16);
            break;
 
          case 'o':
          case 'O':
-           obj = read_integer (readcharfun, 8, stackbuf);
+           obj = read_integer (readcharfun, 8);
            break;
 
          case 'b':
          case 'B':
-           obj = read_integer (readcharfun, 2, stackbuf);
+           obj = read_integer (readcharfun, 2);
            break;
 
          case '@':
@@ -3980,8 +3989,8 @@ read0 (Lisp_Object readcharfun, bool locate_syms)
                  {
                    /* #NrDIGITS -- radix-N number */
                    if (n < 0 || n > 36)
-                     invalid_radix_integer (n, stackbuf, readcharfun);
-                   obj = read_integer (readcharfun, n, stackbuf);
+                     invalid_radix_integer (n, readcharfun);
+                   obj = read_integer (readcharfun, n);
                    break;
                  }
                else if (n <= MOST_POSITIVE_FIXNUM && !NILP (Vread_circle))
@@ -4036,7 +4045,7 @@ read0 (Lisp_Object readcharfun, bool locate_syms)
       break;
 
     case '"':
-      obj = read_string_literal (stackbuf, readcharfun);
+      obj = read_string_literal (readcharfun);
       break;
 
     case '\'':
@@ -4347,7 +4356,7 @@ read0 (Lisp_Object readcharfun, bool locate_syms)
        }
     }
 
-  return unbind_to (count, obj);
+  return unbind_to (base_pdl, obj);
 }
 
 
diff --git a/src/macuvs.h b/src/macuvs.h
index 4c08415698..7b8c77f017 100644
--- a/src/macuvs.h
+++ b/src/macuvs.h
@@ -8,29 +8,29 @@
 
 static const unsigned char mac_uvs_table_adobe_japan1_bytes[] =
   {
-    0x00, 0x0e, 0x00, 0x01, 0x1f, 0xb2, 0x00, 0x00,
+    0x00, 0x0e, 0x00, 0x01, 0x1f, 0xb7, 0x00, 0x00,
     0x00, 0x0f, 0x0e, 0x01, 0x00, 0x00, 0x00, 0x00,
     0x00, 0x00, 0x00, 0x00, 0xaf, 0x0e, 0x01, 0x01,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x04, 0xae,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x04, 0xb3,
     0x0e, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
-    0x01, 0x1c, 0x45, 0x0e, 0x01, 0x03, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x01, 0x1e, 0xc4, 0x0e, 0x01,
+    0x01, 0x1c, 0x4a, 0x0e, 0x01, 0x03, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x01, 0x1e, 0xc9, 0x0e, 0x01,
     0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x1f,
-    0x1d, 0x0e, 0x01, 0x05, 0x00, 0x00, 0x00, 0x00,
-    0x00, 0x01, 0x1f, 0x3f, 0x0e, 0x01, 0x06, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x01, 0x1f, 0x57, 0x0e,
+    0x22, 0x0e, 0x01, 0x05, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x01, 0x1f, 0x44, 0x0e, 0x01, 0x06, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x01, 0x1f, 0x5c, 0x0e,
     0x01, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
-    0x1f, 0x65, 0x0e, 0x01, 0x08, 0x00, 0x00, 0x00,
-    0x00, 0x00, 0x01, 0x1f, 0x73, 0x0e, 0x01, 0x09,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x1f, 0x7c,
+    0x1f, 0x6a, 0x0e, 0x01, 0x08, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x01, 0x1f, 0x78, 0x0e, 0x01, 0x09,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x1f, 0x81,
     0x0e, 0x01, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00,
-    0x01, 0x1f, 0x85, 0x0e, 0x01, 0x0b, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x01, 0x1f, 0x8e, 0x0e, 0x01,
+    0x01, 0x1f, 0x8a, 0x0e, 0x01, 0x0b, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x01, 0x1f, 0x93, 0x0e, 0x01,
     0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x1f,
-    0x97, 0x0e, 0x01, 0x0d, 0x00, 0x00, 0x00, 0x00,
-    0x00, 0x01, 0x1f, 0xa0, 0x0e, 0x01, 0x0e, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x01, 0x1f, 0xa9, 0x00,
-    0x00, 0x33, 0xff, 0x00, 0x34, 0x02, 0x35, 0x82,
+    0x9c, 0x0e, 0x01, 0x0d, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x01, 0x1f, 0xa5, 0x0e, 0x01, 0x0e, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x01, 0x1f, 0xae, 0x00,
+    0x00, 0x34, 0x00, 0x00, 0x34, 0x02, 0x35, 0x82,
     0x00, 0x34, 0x05, 0x3c, 0x1b, 0x00, 0x34, 0x06,
     0x43, 0x5a, 0x00, 0x34, 0x27, 0x36, 0x56, 0x00,
     0x34, 0x2c, 0x43, 0x5e, 0x00, 0x34, 0x2e, 0x37,
@@ -8349,870 +8349,870 @@ static const unsigned char 
mac_uvs_table_adobe_japan1_bytes[] =
     0x02, 0xb8, 0x17, 0x4f, 0x24, 0x02, 0xb8, 0x1a,
     0x37, 0xc6, 0x02, 0xd5, 0x44, 0x36, 0x0a, 0x02,
     0xe2, 0x78, 0x37, 0x6b, 0x02, 0xe5, 0x69, 0x36,
-    0x27, 0x02, 0xe6, 0xea, 0x37, 0x92, 0x00, 0x00,
-    0x04, 0xb7, 0x00, 0x34, 0x02, 0x35, 0x81, 0x00,
-    0x4e, 0x08, 0x34, 0x97, 0x00, 0x4e, 0x0e, 0x4e,
-    0x69, 0x00, 0x4e, 0x19, 0x36, 0xb9, 0x00, 0x4e,
-    0x26, 0x4e, 0x6a, 0x00, 0x4e, 0x30, 0x3c, 0x1a,
-    0x00, 0x4e, 0x39, 0x36, 0x5a, 0x00, 0x4e, 0x3b,
-    0x35, 0xf4, 0x00, 0x4e, 0x73, 0x36, 0x90, 0x00,
-    0x4e, 0xa1, 0x36, 0xcf, 0x00, 0x4e, 0xa4, 0x34,
-    0x7f, 0x00, 0x4e, 0xca, 0x35, 0xd4, 0x00, 0x4f,
-    0x34, 0x36, 0xa0, 0x00, 0x4f, 0x4f, 0x35, 0xfc,
-    0x00, 0x4f, 0x60, 0x3c, 0x1c, 0x00, 0x4f, 0x73,
-    0x4e, 0x6c, 0x00, 0x4f, 0x75, 0x34, 0x47, 0x00,
-    0x4f, 0x7f, 0x34, 0x8a, 0x00, 0x4f, 0xae, 0x34,
-    0x46, 0x00, 0x4f, 0xb5, 0x36, 0x1b, 0x00, 0x4f,
-    0xbf, 0x34, 0xc2, 0x00, 0x50, 0x24, 0x36, 0x5f,
-    0x00, 0x50, 0x26, 0x1d, 0xfa, 0x00, 0x50, 0x49,
-    0x34, 0x61, 0x00, 0x50, 0x4f, 0x36, 0xbe, 0x00,
-    0x50, 0x56, 0x4e, 0x6d, 0x00, 0x50, 0x65, 0x34,
-    0x7b, 0x00, 0x50, 0x85, 0x34, 0xd0, 0x00, 0x50,
-    0x91, 0x34, 0x79, 0x00, 0x50, 0xc5, 0x1d, 0xee,
-    0x00, 0x50, 0xca, 0x1f, 0x2e, 0x00, 0x50, 0xcf,
-    0x34, 0xa2, 0x00, 0x50, 0xe7, 0x34, 0x30, 0x00,
-    0x50, 0xed, 0x4e, 0x6e, 0x00, 0x50, 0xf2, 0x52,
-    0xac, 0x00, 0x51, 0x1a, 0x37, 0x16, 0x00, 0x51,
-    0x32, 0x1e, 0x76, 0x00, 0x51, 0x46, 0x34, 0xa5,
-    0x00, 0x51, 0x4d, 0x34, 0x4d, 0x00, 0x51, 0x4e,
-    0x36, 0x7d, 0x00, 0x51, 0x54, 0x10, 0x74, 0x00,
-    0x51, 0x68, 0x36, 0x42, 0x00, 0x51, 0x6b, 0x4e,
-    0x6f, 0x00, 0x51, 0x6c, 0x34, 0x80, 0x00, 0x51,
-    0x77, 0x35, 0xa5, 0x00, 0x51, 0x7c, 0x35, 0xb4,
-    0x00, 0x51, 0x89, 0x1e, 0x87, 0x00, 0x51, 0x8d,
-    0x08, 0x36, 0x00, 0x51, 0x92, 0x36, 0xd6, 0x00,
-    0x51, 0x93, 0x34, 0xd1, 0x00, 0x51, 0x95, 0x10,
-    0x82, 0x00, 0x51, 0xa4, 0x1e, 0x89, 0x00, 0x51,
-    0xac, 0x0c, 0x59, 0x00, 0x51, 0xb4, 0x34, 0x5c,
-    0x00, 0x51, 0xcb, 0x1e, 0x3e, 0x00, 0x51, 0xdb,
-    0x34, 0xd2, 0x00, 0x51, 0xde, 0x4f, 0x53, 0x00,
-    0x51, 0xe1, 0x36, 0xd9, 0x00, 0x51, 0xfd, 0x4e,
-    0x72, 0x00, 0x52, 0x03, 0x36, 0x22, 0x00, 0x52,
-    0x06, 0x34, 0xbb, 0x00, 0x52, 0x24, 0x36, 0xa1,
-    0x00, 0x52, 0x38, 0x35, 0xb5, 0x00, 0x52, 0x4a,
-    0x35, 0xdd, 0x00, 0x52, 0x4d, 0x36, 0x41, 0x00,
-    0x52, 0x64, 0x4e, 0x74, 0x00, 0x52, 0x71, 0x4e,
-    0x75, 0x00, 0x52, 0x72, 0x05, 0xc2, 0x00, 0x52,
-    0x75, 0x1e, 0x2b, 0x00, 0x52, 0x8d, 0x37, 0x1a,
-    0x00, 0x52, 0xc7, 0x36, 0xf6, 0x00, 0x52, 0xc9,
-    0x0e, 0x29, 0x00, 0x52, 0xd7, 0x37, 0x1b, 0x00,
-    0x52, 0xdd, 0x36, 0x05, 0x00, 0x52, 0xe2, 0x36,
-    0x2a, 0x00, 0x52, 0xe4, 0x34, 0x1a, 0x00, 0x52,
-    0xfa, 0x09, 0x07, 0x00, 0x53, 0x00, 0x37, 0xcf,
-    0x00, 0x53, 0x05, 0x36, 0xc3, 0x00, 0x53, 0x07,
-    0x20, 0xd4, 0x00, 0x53, 0x15, 0x1f, 0x2f, 0x00,
-    0x53, 0x16, 0x35, 0x61, 0x00, 0x53, 0x39, 0x36,
-    0xaa, 0x00, 0x53, 0x3f, 0x4e, 0x77, 0x00, 0x53,
-    0x40, 0x34, 0xd4, 0x00, 0x53, 0x4a, 0x36, 0xa2,
-    0x00, 0x53, 0x51, 0x0d, 0x70, 0x00, 0x53, 0x5a,
-    0x36, 0x98, 0x00, 0x53, 0x65, 0x52, 0xf3, 0x00,
-    0x53, 0x71, 0x35, 0x80, 0x00, 0x53, 0x78, 0x35,
-    0x5f, 0x00, 0x53, 0x7f, 0x06, 0xa2, 0x00, 0x53,
-    0xa9, 0x1d, 0xd8, 0x00, 0x53, 0xc9, 0x4f, 0x39,
-    0x00, 0x53, 0xca, 0x35, 0x8e, 0x00, 0x53, 0xce,
-    0x34, 0x8f, 0x00, 0x53, 0xd7, 0x35, 0xf5, 0x00,
-    0x53, 0xdb, 0x1f, 0x2a, 0x00, 0x53, 0xdf, 0x37,
-    0x1f, 0x00, 0x53, 0xe0, 0x53, 0x00, 0x00, 0x53,
-    0xf1, 0x1e, 0x0c, 0x00, 0x53, 0xf2, 0x34, 0x8b,
-    0x00, 0x54, 0x0f, 0x34, 0xc9, 0x00, 0x54, 0x38,
-    0x35, 0x8f, 0x00, 0x54, 0x40, 0x4e, 0x79, 0x00,
-    0x54, 0x48, 0x36, 0x76, 0x00, 0x54, 0x68, 0x09,
-    0x2a, 0x00, 0x54, 0xac, 0x4f, 0x35, 0x00, 0x54,
-    0xb2, 0x35, 0xdc, 0x00, 0x54, 0xe8, 0x1e, 0x17,
-    0x00, 0x55, 0x10, 0x36, 0x83, 0x00, 0x55, 0x33,
-    0x1e, 0x8b, 0x00, 0x55, 0x39, 0x1e, 0x8a, 0x00,
-    0x55, 0x44, 0x1e, 0x32, 0x00, 0x55, 0x46, 0x36,
-    0x06, 0x00, 0x55, 0x53, 0x35, 0xaa, 0x00, 0x55,
-    0x61, 0x38, 0x39, 0x00, 0x55, 0x84, 0x4e, 0x5e,
-    0x00, 0x55, 0x9c, 0x4e, 0x7b, 0x00, 0x55, 0x9d,
-    0x1d, 0xe3, 0x00, 0x55, 0xa9, 0x1f, 0x30, 0x00,
-    0x55, 0xab, 0x35, 0x8b, 0x00, 0x55, 0xb0, 0x1d,
-    0xf0, 0x00, 0x55, 0xe4, 0x1e, 0x8c, 0x00, 0x56,
-    0x05, 0x53, 0x2d, 0x00, 0x56, 0x06, 0x0b, 0x70,
-    0x00, 0x56, 0x09, 0x4e, 0x7d, 0x00, 0x56, 0x32,
-    0x1e, 0x8d, 0x00, 0x56, 0x42, 0x1d, 0xda, 0x00,
-    0x56, 0x4c, 0x1e, 0x29, 0x00, 0x56, 0x68, 0x34,
-    0x15, 0x00, 0x56, 0x74, 0x34, 0xbc, 0x00, 0x56,
-    0x78, 0x1e, 0x52, 0x00, 0x56, 0xa5, 0x1e, 0x8e,
-    0x00, 0x56, 0xae, 0x1f, 0x31, 0x00, 0x56, 0xc0,
-    0x37, 0x24, 0x00, 0x56, 0xc1, 0x34, 0xd7, 0x00,
-    0x56, 0xce, 0x4e, 0x82, 0x00, 0x56, 0xee, 0x4e,
-    0x83, 0x00, 0x57, 0x0d, 0x34, 0xd8, 0x00, 0x57,
-    0x47, 0x34, 0x78, 0x00, 0x57, 0x6a, 0x36, 0x74,
-    0x00, 0x57, 0xce, 0x09, 0xd3, 0x00, 0x57, 0xd6,
-    0x4e, 0x84, 0x00, 0x57, 0xf4, 0x34, 0x98, 0x00,
-    0x58, 0x0b, 0x1e, 0x8f, 0x00, 0x58, 0x19, 0x1f,
-    0x32, 0x00, 0x58, 0x35, 0x1e, 0x49, 0x00, 0x58,
-    0x3d, 0x4e, 0x85, 0x00, 0x58, 0x40, 0x34, 0x48,
-    0x00, 0x58, 0x58, 0x1e, 0x4d, 0x00, 0x58, 0x59,
-    0x4e, 0x86, 0x00, 0x58, 0x5a, 0x1e, 0x42, 0x00,
-    0x58, 0x9c, 0x36, 0x71, 0x00, 0x58, 0xa8, 0x0e,
-    0x7d, 0x00, 0x58, 0xab, 0x34, 0xd9, 0x00, 0x59,
-    0x06, 0x53, 0x7b, 0x00, 0x59, 0x1b, 0x1f, 0x33,
-    0x00, 0x59, 0x27, 0x36, 0x55, 0x00, 0x59, 0x4f,
-    0x4e, 0x87, 0x00, 0x59, 0x51, 0x35, 0xab, 0x00,
-    0x59, 0x53, 0x37, 0xd1, 0x00, 0x59, 0x60, 0x4e,
-    0x89, 0x00, 0x59, 0x62, 0x4e, 0x8a, 0x00, 0x59,
-    0x73, 0x36, 0x04, 0x00, 0x59, 0x84, 0x36, 0xe7,
-    0x00, 0x59, 0xa5, 0x36, 0x4f, 0x00, 0x59, 0xc9,
-    0x34, 0x8c, 0x00, 0x59, 0xda, 0x34, 0xda, 0x00,
-    0x59, 0xec, 0x36, 0xae, 0x00, 0x59, 0xff, 0x35,
-    0xe0, 0x00, 0x5a, 0x1c, 0x37, 0x26, 0x00, 0x5a,
-    0x29, 0x1e, 0x6f, 0x00, 0x5a, 0x36, 0x34, 0xdb,
-    0x00, 0x5a, 0x66, 0x36, 0xb2, 0x00, 0x5a, 0x9b,
-    0x1e, 0x68, 0x00, 0x5a, 0xbe, 0x1e, 0x90, 0x00,
-    0x5a, 0xc2, 0x37, 0x27, 0x00, 0x5a, 0xcc, 0x1d,
-    0xfb, 0x00, 0x5a, 0xda, 0x4e, 0x8b, 0x00, 0x5b,
-    0x5a, 0x4e, 0x8c, 0x00, 0x5b, 0x73, 0x4e, 0x8d,
-    0x00, 0x5b, 0x7c, 0x4e, 0x8e, 0x00, 0x5b, 0xb3,
-    0x34, 0x6d, 0x00, 0x5b, 0xb5, 0x36, 0x07, 0x00,
-    0x5b, 0xc3, 0x37, 0x29, 0x00, 0x5b, 0xd2, 0x35,
-    0x78, 0x00, 0x5b, 0xdb, 0x20, 0xf4, 0x00, 0x5b,
-    0xe7, 0x0c, 0xe1, 0x00, 0x5b, 0xe8, 0x37, 0x42,
-    0x00, 0x5c, 0x06, 0x09, 0x95, 0x00, 0x5c, 0x0a,
-    0x36, 0x4d, 0x00, 0x5c, 0x0b, 0x36, 0x23, 0x00,
-    0x5c, 0x0e, 0x36, 0x8a, 0x00, 0x5c, 0x0f, 0x36,
-    0x09, 0x00, 0x5c, 0x28, 0x1f, 0x34, 0x00, 0x5c,
-    0x51, 0x1d, 0xf2, 0x00, 0x5c, 0x60, 0x1e, 0x4a,
-    0x00, 0x5c, 0x64, 0x34, 0x31, 0x00, 0x5c, 0x6e,
-    0x12, 0x32, 0x00, 0x5d, 0x29, 0x36, 0xc4, 0x00,
-    0x5d, 0x4e, 0x34, 0xdd, 0x00, 0x5d, 0x87, 0x34,
-    0xde, 0x00, 0x5d, 0xb2, 0x3c, 0x2d, 0x00, 0x5d,
-    0xc9, 0x34, 0xdf, 0x00, 0x5d, 0xcc, 0x34, 0x73,
-    0x00, 0x5d, 0xd3, 0x34, 0xe0, 0x00, 0x5d, 0xe1,
-    0x35, 0xff, 0x00, 0x5d, 0xe5, 0x35, 0xc3, 0x00,
-    0x5d, 0xe8, 0x35, 0x92, 0x00, 0x5d, 0xf7, 0x1d,
-    0xff, 0x00, 0x5d, 0xfd, 0x0b, 0x65, 0x00, 0x5e,
-    0x06, 0x36, 0xa3, 0x00, 0x5e, 0x1d, 0x36, 0x77,
-    0x00, 0x5e, 0x30, 0x34, 0x75, 0x00, 0x5e, 0x3d,
-    0x36, 0xd0, 0x00, 0x5e, 0x43, 0x4e, 0x91, 0x00,
-    0x5e, 0x54, 0x37, 0x2d, 0x00, 0x5e, 0x63, 0x36,
-    0xba, 0x00, 0x5e, 0x64, 0x1e, 0x93, 0x00, 0x5e,
-    0x73, 0x36, 0xbb, 0x00, 0x5e, 0x7e, 0x35, 0x84,
-    0x00, 0x5e, 0x96, 0x1e, 0x70, 0x00, 0x5e, 0xa7,
-    0x4e, 0x92, 0x00, 0x5e, 0xad, 0x34, 0xa9, 0x00,
-    0x5e, 0xc9, 0x0f, 0xbf, 0x00, 0x5e, 0xca, 0x4f,
-    0x4f, 0x00, 0x5e, 0xcb, 0x38, 0xae, 0x00, 0x5e,
-    0xcf, 0x1f, 0x36, 0x00, 0x5e, 0xd0, 0x1f, 0x35,
-    0x00, 0x5e, 0xdf, 0x1e, 0x6a, 0x00, 0x5e, 0xe0,
-    0x1e, 0x18, 0x00, 0x5e, 0xe3, 0x37, 0x2f, 0x00,
-    0x5e, 0xf6, 0x34, 0x67, 0x00, 0x5e, 0xf7, 0x34,
-    0xaa, 0x00, 0x5e, 0xfa, 0x34, 0x7c, 0x00, 0x5e,
-    0xfb, 0x35, 0x69, 0x00, 0x5f, 0x0a, 0x36, 0xbc,
-    0x00, 0x5f, 0x2d, 0x34, 0xe1, 0x00, 0x5f, 0x31,
-    0x35, 0xf3, 0x00, 0x5f, 0x38, 0x4e, 0x94, 0x00,
-    0x5f, 0x45, 0x37, 0xce, 0x00, 0x5f, 0x50, 0x3c,
-    0x1f, 0x00, 0x5f, 0x62, 0x4e, 0x5f, 0x00, 0x5f,
-    0x69, 0x35, 0xd7, 0x00, 0x5f, 0x6b, 0x36, 0x68,
-    0x00, 0x5f, 0x80, 0x35, 0x5d, 0x00, 0x5f, 0x98,
-    0x34, 0xe2, 0x00, 0x5f, 0xa1, 0x4e, 0x95, 0x00,
-    0x5f, 0xae, 0x36, 0xa8, 0x00, 0x5f, 0xb5, 0x36,
-    0x69, 0x00, 0x5f, 0xbd, 0x1d, 0xea, 0x00, 0x5f,
-    0xcd, 0x36, 0x91, 0x00, 0x5f, 0xd8, 0x36, 0xd1,
-    0x00, 0x5f, 0xd9, 0x36, 0xd2, 0x00, 0x5f, 0xdd,
-    0x4e, 0x96, 0x00, 0x60, 0x25, 0x35, 0x90, 0x00,
-    0x60, 0x50, 0x35, 0x99, 0x00, 0x60, 0x62, 0x1d,
-    0xe0, 0x00, 0x60, 0x65, 0x34, 0xa4, 0x00, 0x60,
-    0x75, 0x35, 0xac, 0x00, 0x60, 0x94, 0x05, 0x79,
-    0x00, 0x60, 0x97, 0x1e, 0x94, 0x00, 0x60, 0x9e,
-    0x54, 0x36, 0x00, 0x60, 0xa4, 0x3c, 0x20, 0x00,
-    0x60, 0xb2, 0x34, 0xb3, 0x00, 0x60, 0xc5, 0x36,
-    0x12, 0x00, 0x60, 0xd8, 0x34, 0xe3, 0x00, 0x61,
-    0x08, 0x1e, 0x7a, 0x00, 0x61, 0x09, 0x36, 0xf3,
-    0x00, 0x61, 0x0f, 0x35, 0x47, 0x00, 0x61, 0x3d,
-    0x34, 0xe4, 0x00, 0x61, 0x48, 0x4e, 0x60, 0x00,
-    0x61, 0x4c, 0x35, 0xc4, 0x00, 0x61, 0x4e, 0x34,
-    0x2c, 0x00, 0x61, 0x62, 0x4e, 0x97, 0x00, 0x61,
-    0x67, 0x1d, 0xf5, 0x00, 0x61, 0x68, 0x34, 0x10,
-    0x00, 0x61, 0x8e, 0x0b, 0x00, 0x00, 0x61, 0x90,
-    0x37, 0x10, 0x00, 0x61, 0xa4, 0x34, 0xbd, 0x00,
-    0x61, 0xb2, 0x35, 0xb6, 0x00, 0x61, 0xf2, 0x34,
-    0x39, 0x00, 0x61, 0xf8, 0x4e, 0x99, 0x00, 0x61,
-    0xfe, 0x34, 0xe5, 0x00, 0x62, 0x10, 0x36, 0x2b,
-    0x00, 0x62, 0x3b, 0x36, 0xeb, 0x00, 0x62, 0x3f,
-    0x36, 0xd3, 0x00, 0x62, 0x40, 0x36, 0x02, 0x00,
-    0x62, 0x41, 0x1f, 0x37, 0x00, 0x62, 0x47, 0x36,
-    0x3b, 0x00, 0x62, 0x48, 0x1e, 0xc2, 0x00, 0x62,
-    0x49, 0x1e, 0x63, 0x00, 0x62, 0x68, 0x34, 0xe6,
-    0x00, 0x62, 0x71, 0x35, 0x45, 0x00, 0x62, 0xb1,
-    0x36, 0xc5, 0x00, 0x62, 0xcc, 0x37, 0x32, 0x00,
-    0x62, 0xcf, 0x34, 0xe7, 0x00, 0x62, 0xd0, 0x1d,
-    0xe1, 0x00, 0x62, 0xd2, 0x35, 0x93, 0x00, 0x62,
-    0xd4, 0x13, 0x5d, 0x00, 0x62, 0xf3, 0x1f, 0x21,
-    0x00, 0x62, 0xf7, 0x34, 0x88, 0x00, 0x63, 0x3a,
-    0x4f, 0x3e, 0x00, 0x63, 0x3d, 0x1e, 0x62, 0x00,
-    0x63, 0x4c, 0x34, 0x5d, 0x00, 0x63, 0x57, 0x1e,
-    0x3f, 0x00, 0x63, 0x67, 0x34, 0xc3, 0x00, 0x63,
-    0x68, 0x35, 0xec, 0x00, 0x63, 0x69, 0x1e, 0x95,
-    0x00, 0x63, 0x6e, 0x34, 0x9d, 0x00, 0x63, 0x72,
-    0x1d, 0xfc, 0x00, 0x63, 0x83, 0x36, 0x43, 0x00,
-    0x63, 0x88, 0x35, 0xf6, 0x00, 0x63, 0x92, 0x34,
-    0xaf, 0x00, 0x63, 0xa1, 0x35, 0xd8, 0x00, 0x63,
-    0xa7, 0x35, 0xc6, 0x00, 0x63, 0xc3, 0x1f, 0x27,
-    0x00, 0x63, 0xc6, 0x37, 0x34, 0x00, 0x63, 0xf4,
-    0x35, 0x57, 0x00, 0x64, 0x06, 0x1e, 0x96, 0x00,
-    0x64, 0x0f, 0x34, 0xe9, 0x00, 0x64, 0x1c, 0x37,
-    0x33, 0x00, 0x64, 0x28, 0x37, 0x35, 0x00, 0x64,
-    0x42, 0x34, 0x9e, 0x00, 0x64, 0x69, 0x36, 0xd7,
-    0x00, 0x64, 0x6f, 0x4f, 0x28, 0x00, 0x64, 0x7a,
-    0x1e, 0x21, 0x00, 0x64, 0xb0, 0x1e, 0x24, 0x00,
-    0x64, 0xe2, 0x1e, 0x45, 0x00, 0x64, 0xf2, 0x34,
-    0xea, 0x00, 0x64, 0xf6, 0x4e, 0x9e, 0x00, 0x65,
-    0x1d, 0x34, 0xe8, 0x00, 0x65, 0x4f, 0x0d, 0xc4,
-    0x00, 0x65, 0x5d, 0x34, 0xeb, 0x00, 0x65, 0x5e,
-    0x4e, 0xa1, 0x00, 0x65, 0x62, 0x34, 0x71, 0x00,
-    0x65, 0x77, 0x36, 0xb3, 0x00, 0x65, 0x83, 0x1e,
-    0x98, 0x00, 0x65, 0x87, 0x4e, 0xa3, 0x00, 0x65,
-    0x89, 0x4e, 0xa4, 0x00, 0x65, 0x8e, 0x4e, 0xa6,
-    0x00, 0x65, 0x90, 0x34, 0xb5, 0x00, 0x65, 0x9c,
-    0x35, 0xed, 0x00, 0x65, 0xa7, 0x4f, 0x41, 0x00,
-    0x65, 0xbc, 0x35, 0x5c, 0x00, 0x65, 0xc5, 0x37,
-    0x08, 0x00, 0x65, 0xdf, 0x54, 0xbe, 0x00, 0x65,
-    0xe1, 0x4e, 0xa9, 0x00, 0x65, 0xe2, 0x06, 0x37,
-    0x00, 0x66, 0x0e, 0x36, 0xe4, 0x00, 0x66, 0x1e,
-    0x21, 0x1c, 0x00, 0x66, 0x5f, 0x34, 0xec, 0x00,
-    0x66, 0x66, 0x1d, 0xe2, 0x00, 0x66, 0x67, 0x4e,
-    0xaa, 0x00, 0x66, 0x69, 0x36, 0xa5, 0x00, 0x66,
-    0x6e, 0x4e, 0xab, 0x00, 0x66, 0x74, 0x0a, 0x56,
-    0x00, 0x66, 0x77, 0x39, 0x11, 0x00, 0x66, 0x81,
-    0x35, 0xa0, 0x00, 0x66, 0x91, 0x34, 0x28, 0x00,
-    0x66, 0x96, 0x36, 0x5e, 0x00, 0x66, 0x97, 0x35,
-    0x46, 0x00, 0x66, 0xb5, 0x54, 0xda, 0x00, 0x66,
-    0xc1, 0x1f, 0x38, 0x00, 0x66, 0xd9, 0x1e, 0x13,
-    0x00, 0x66, 0xdc, 0x36, 0xfd, 0x00, 0x66, 0xf4,
-    0x34, 0x81, 0x00, 0x66, 0xf5, 0x37, 0x3b, 0x00,
-    0x66, 0xf8, 0x36, 0x03, 0x00, 0x66, 0xfb, 0x37,
-    0xcd, 0x00, 0x66, 0xfc, 0x37, 0x20, 0x00, 0x67,
-    0x00, 0x4e, 0xaf, 0x00, 0x67, 0x08, 0x35, 0xb2,
-    0x00, 0x67, 0x09, 0x36, 0xf7, 0x00, 0x67, 0x0b,
-    0x36, 0xc6, 0x00, 0x67, 0x0d, 0x36, 0xb7, 0x00,
-    0x67, 0x15, 0x36, 0x6f, 0x00, 0x67, 0x17, 0x0f,
-    0xd5, 0x00, 0x67, 0x1b, 0x36, 0xd4, 0x00, 0x67,
-    0x1d, 0x36, 0x6b, 0x00, 0x67, 0x1f, 0x35, 0x86,
-    0x00, 0x67, 0x53, 0x1e, 0x0f, 0x00, 0x67, 0x56,
-    0x4f, 0x3a, 0x00, 0x67, 0x5e, 0x37, 0x3c, 0x00,
-    0x67, 0x61, 0x4e, 0xb0, 0x00, 0x67, 0x7e, 0x34,
-    0x95, 0x00, 0x67, 0xa6, 0x1e, 0x99, 0x00, 0x67,
-    0xa9, 0x34, 0xed, 0x00, 0x67, 0xc4, 0x4e, 0xb1,
-    0x00, 0x67, 0xca, 0x1e, 0x65, 0x00, 0x67, 0xd4,
-    0x34, 0x91, 0x00, 0x67, 0xe7, 0x34, 0xee, 0x00,
-    0x67, 0xf1, 0x36, 0x65, 0x00, 0x68, 0x01, 0x21,
-    0x2e, 0x00, 0x68, 0x02, 0x4e, 0xb2, 0x00, 0x68,
-    0x13, 0x1e, 0x25, 0x00, 0x68, 0x1f, 0x4e, 0x61,
-    0x00, 0x68, 0x21, 0x34, 0x82, 0x00, 0x68, 0x43,
-    0x34, 0xac, 0x00, 0x68, 0x52, 0x21, 0x2c, 0x00,
-    0x68, 0x5d, 0x34, 0xc5, 0x00, 0x68, 0x7a, 0x36,
-    0xf0, 0x00, 0x68, 0x81, 0x37, 0x09, 0x00, 0x68,
-    0x85, 0x0d, 0x15, 0x00, 0x68, 0x8d, 0x37, 0x3e,
-    0x00, 0x68, 0x97, 0x4f, 0x37, 0x00, 0x68, 0x9b,
-    0x1e, 0x9b, 0x00, 0x68, 0x9d, 0x37, 0x3d, 0x00,
-    0x68, 0xa2, 0x1e, 0x19, 0x00, 0x68, 0xc8, 0x37,
-    0xcc, 0x00, 0x68, 0xda, 0x1e, 0x38, 0x00, 0x69,
-    0x0d, 0x34, 0x99, 0x00, 0x69, 0x30, 0x34, 0xf0,
-    0x00, 0x69, 0x3d, 0x4e, 0xb3, 0x00, 0x69, 0x5e,
-    0x4e, 0xb4, 0x00, 0x69, 0x62, 0x1e, 0x58, 0x00,
-    0x69, 0x6b, 0x34, 0xef, 0x00, 0x69, 0x6f, 0x34,
-    0x94, 0x00, 0x69, 0x82, 0x34, 0x5b, 0x00, 0x69,
-    0x8a, 0x1e, 0x06, 0x00, 0x69, 0x94, 0x1e, 0x84,
-    0x00, 0x69, 0xa7, 0x34, 0xf1, 0x00, 0x69, 0xbb,
-    0x37, 0x43, 0x00, 0x69, 0xc1, 0x35, 0x9a, 0x00,
-    0x69, 0xcb, 0x35, 0xc7, 0x00, 0x69, 0xcc, 0x1e,
-    0x40, 0x00, 0x69, 0xd9, 0x36, 0xdd, 0x00, 0x69,
-    0xea, 0x35, 0x6f, 0x00, 0x69, 0xfe, 0x55, 0x1f,
-    0x00, 0x6a, 0x0b, 0x1e, 0x64, 0x00, 0x6a, 0x3d,
-    0x1e, 0x3a, 0x00, 0x6a, 0x44, 0x34, 0xf2, 0x00,
-    0x6a, 0x55, 0x55, 0x25, 0x00, 0x6a, 0x5f, 0x35,
-    0x87, 0x00, 0x6a, 0x73, 0x37, 0xd4, 0x00, 0x6a,
-    0x8e, 0x34, 0x7e, 0x00, 0x6a, 0x90, 0x34, 0xf3,
-    0x00, 0x6a, 0x9c, 0x4e, 0xb6, 0x00, 0x6a, 0xdb,
-    0x06, 0xf3, 0x00, 0x6b, 0x04, 0x0f, 0x5d, 0x00,
-    0x6b, 0x1d, 0x1d, 0xd7, 0x00, 0x6b, 0x21, 0x35,
-    0xe7, 0x00, 0x6b, 0x24, 0x3c, 0x22, 0x00, 0x6b,
-    0x4e, 0x36, 0x5b, 0x00, 0x6b, 0x96, 0x36, 0x16,
-    0x00, 0x6b, 0xba, 0x08, 0x74, 0x00, 0x6b, 0xbb,
-    0x34, 0x70, 0x00, 0x6c, 0x08, 0x1f, 0x39, 0x00,
-    0x6c, 0x13, 0x34, 0xf5, 0x00, 0x6c, 0x38, 0x4e,
-    0xba, 0x00, 0x6c, 0x3a, 0x39, 0x62, 0x00, 0x6c,
-    0x72, 0x1f, 0x1e, 0x00, 0x6c, 0xaa, 0x37, 0x48,
-    0x00, 0x6c, 0xbf, 0x05, 0x0a, 0x00, 0x6c, 0xe1,
-    0x1e, 0x71, 0x00, 0x6c, 0xe8, 0x36, 0x66, 0x00,
-    0x6d, 0x3e, 0x34, 0xae, 0x00, 0x6d, 0x69, 0x35,
-    0xc8, 0x00, 0x6d, 0x6e, 0x36, 0xb4, 0x00, 0x6d,
-    0x77, 0x05, 0x82, 0x00, 0x6d, 0x78, 0x36, 0x1d,
-    0x00, 0x6d, 0x88, 0x36, 0x0c, 0x00, 0x6d, 0xe4,
-    0x4e, 0xbd, 0x00, 0x6d, 0xeb, 0x1d, 0xd5, 0x00,
-    0x6d, 0xfb, 0x36, 0x7c, 0x00, 0x6e, 0x08, 0x4e,
-    0xbf, 0x00, 0x6e, 0x1a, 0x09, 0x77, 0x00, 0x6e,
-    0x23, 0x1f, 0x3a, 0x00, 0x6e, 0x2f, 0x35, 0xc9,
-    0x00, 0x6e, 0x6e, 0x1e, 0x9d, 0x00, 0x6e, 0x72,
-    0x4e, 0xc0, 0x00, 0x6e, 0x7e, 0x34, 0xcf, 0x00,
-    0x6e, 0x9d, 0x1e, 0x01, 0x00, 0x6e, 0xa2, 0x1d,
-    0xd3, 0x00, 0x6e, 0xba, 0x1e, 0x46, 0x00, 0x6e,
-    0xcb, 0x35, 0xe9, 0x00, 0x6e, 0xd5, 0x4e, 0xc2,
-    0x00, 0x6e, 0xdb, 0x4e, 0xc3, 0x00, 0x6e, 0xec,
-    0x1f, 0x3b, 0x00, 0x6e, 0xfe, 0x34, 0xf8, 0x00,
-    0x6f, 0x11, 0x34, 0xf7, 0x00, 0x6f, 0x22, 0x34,
-    0x14, 0x00, 0x6f, 0x23, 0x0f, 0xc2, 0x00, 0x6f,
-    0x3e, 0x34, 0xf9, 0x00, 0x6f, 0x51, 0x36, 0x9e,
-    0x00, 0x6f, 0x54, 0x35, 0xb0, 0x00, 0x6f, 0x5b,
-    0x4e, 0xc4, 0x00, 0x6f, 0x64, 0x4e, 0xc6, 0x00,
-    0x6f, 0x6e, 0x0b, 0xc8, 0x00, 0x6f, 0x74, 0x4e,
-    0xc7, 0x00, 0x6f, 0x98, 0x37, 0x47, 0x00, 0x6f,
-    0xef, 0x1e, 0x33, 0x00, 0x6f, 0xf9, 0x39, 0x95,
-    0x00, 0x70, 0x15, 0x1e, 0x6b, 0x00, 0x70, 0x1b,
-    0x37, 0x4a, 0x00, 0x70, 0x1e, 0x1e, 0x51, 0x00,
-    0x70, 0x26, 0x1e, 0x3d, 0x00, 0x70, 0x27, 0x36,
-    0x57, 0x00, 0x70, 0x4a, 0x39, 0x98, 0x00, 0x70,
-    0x58, 0x1e, 0x57, 0x00, 0x70, 0x70, 0x35, 0x6a,
-    0x00, 0x70, 0x78, 0x4f, 0x2e, 0x00, 0x70, 0x7c,
-    0x1e, 0x10, 0x00, 0x70, 0xad, 0x36, 0x5c, 0x00,
-    0x71, 0x49, 0x0f, 0xc3, 0x00, 0x71, 0x4e, 0x1e,
-    0x26, 0x00, 0x71, 0x52, 0x55, 0xad, 0x00, 0x71,
-    0x59, 0x35, 0x59, 0x00, 0x71, 0x62, 0x37, 0x4b,
-    0x00, 0x71, 0x6e, 0x08, 0xfd, 0x00, 0x71, 0x7d,
-    0x1e, 0x27, 0x00, 0x71, 0x94, 0x1e, 0x7d, 0x00,
-    0x71, 0xb3, 0x39, 0xae, 0x00, 0x71, 0xd0, 0x37,
-    0x0a, 0x00, 0x71, 0xff, 0x34, 0xfa, 0x00, 0x72,
-    0x28, 0x15, 0xdf, 0x00, 0x72, 0x2b, 0x3c, 0x26,
-    0x00, 0x72, 0x35, 0x09, 0x0b, 0x00, 0x72, 0x36,
-    0x34, 0xb9, 0x00, 0x72, 0x3a, 0x4f, 0x46, 0x00,
-    0x72, 0x3b, 0x37, 0x4c, 0x00, 0x72, 0x3e, 0x4e,
-    0xc9, 0x00, 0x72, 0x4c, 0x1e, 0x5b, 0x00, 0x72,
-    0x59, 0x1f, 0x1d, 0x00, 0x72, 0xe1, 0x4f, 0x36,
-    0x00, 0x73, 0x1c, 0x37, 0x4e, 0x00, 0x73, 0x2a,
-    0x0b, 0xb4, 0x00, 0x73, 0x36, 0x36, 0xf8, 0x00,
-    0x73, 0x37, 0x1e, 0x7c, 0x00, 0x73, 0x87, 0x37,
-    0x05, 0x00, 0x73, 0x8b, 0x35, 0xa1, 0x00, 0x73,
-    0xca, 0x1e, 0x0b, 0x00, 0x73, 0xce, 0x1e, 0xa0,
-    0x00, 0x73, 0xe5, 0x34, 0xfb, 0x00, 0x73, 0xed,
-    0x34, 0xb1, 0x00, 0x74, 0x22, 0x0b, 0x56, 0x00,
-    0x74, 0x32, 0x34, 0xfc, 0x00, 0x74, 0x5f, 0x34,
-    0xfd, 0x00, 0x74, 0x62, 0x21, 0x71, 0x00, 0x74,
-    0xb0, 0x35, 0x79, 0x00, 0x74, 0xbd, 0x4e, 0xcd,
-    0x00, 0x74, 0xca, 0x37, 0x4f, 0x00, 0x74, 0xd8,
-    0x55, 0xf6, 0x00, 0x74, 0xdc, 0x35, 0x50, 0x00,
-    0x74, 0xe0, 0x34, 0xfe, 0x00, 0x74, 0xef, 0x55,
-    0xfa, 0x00, 0x75, 0x04, 0x1e, 0xa1, 0x00, 0x75,
-    0x0c, 0x34, 0xff, 0x00, 0x75, 0x0d, 0x1e, 0xa2,
-    0x00, 0x75, 0x11, 0x1e, 0x04, 0x00, 0x75, 0x15,
-    0x1e, 0xa3, 0x00, 0x75, 0x26, 0x4f, 0x3b, 0x00,
-    0x75, 0x54, 0x36, 0xa4, 0x00, 0x75, 0x5d, 0x4e,
-    0xce, 0x00, 0x75, 0xbc, 0x4e, 0xcf, 0x00, 0x75,
-    0xc5, 0x36, 0xb1, 0x00, 0x76, 0x08, 0x4e, 0xd1,
-    0x00, 0x76, 0x26, 0x1e, 0x2d, 0x00, 0x76, 0x52,
-    0x1e, 0x7b, 0x00, 0x76, 0x64, 0x4e, 0xd2, 0x00,
-    0x76, 0x69, 0x4e, 0xd3, 0x00, 0x76, 0x72, 0x35,
-    0x00, 0x00, 0x76, 0x84, 0x36, 0x79, 0x00, 0x76,
-    0x93, 0x1e, 0xa4, 0x00, 0x76, 0xc6, 0x34, 0xc4,
-    0x00, 0x76, 0xca, 0x21, 0x7b, 0x00, 0x76, 0xd4,
-    0x56, 0x1d, 0x00, 0x76, 0xdb, 0x36, 0x2c, 0x00,
-    0x76, 0xdf, 0x36, 0xe5, 0x00, 0x76, 0xf2, 0x36,
-    0xe9, 0x00, 0x76, 0xf4, 0x36, 0x6e, 0x00, 0x77,
-    0x1e, 0x16, 0xb8, 0x00, 0x77, 0x1f, 0x36, 0x1e,
-    0x00, 0x77, 0x37, 0x4e, 0xd5, 0x00, 0x77, 0x3a,
-    0x34, 0xa6, 0x00, 0x77, 0x7e, 0x4e, 0xd6, 0x00,
-    0x77, 0x8d, 0x56, 0x2e, 0x00, 0x77, 0xa2, 0x56,
-    0x2f, 0x00, 0x77, 0xa5, 0x1e, 0x6e, 0x00, 0x77,
-    0xac, 0x34, 0x92, 0x00, 0x77, 0xe9, 0x35, 0xa4,
-    0x00, 0x78, 0x32, 0x36, 0xc7, 0x00, 0x78, 0x3a,
-    0x36, 0x7f, 0x00, 0x78, 0x5d, 0x36, 0x0d, 0x00,
-    0x78, 0x6c, 0x34, 0x83, 0x00, 0x78, 0x7c, 0x1e,
-    0xa5, 0x00, 0x78, 0x91, 0x0d, 0x7e, 0x00, 0x78,
-    0xd4, 0x35, 0x02, 0x00, 0x78, 0xe8, 0x36, 0xda,
-    0x00, 0x78, 0xef, 0x35, 0x4b, 0x00, 0x79, 0x2a,
-    0x35, 0x01, 0x00, 0x79, 0x34, 0x3a, 0x38, 0x00,
-    0x79, 0x3a, 0x08, 0xd4, 0x00, 0x79, 0x3c, 0x21,
-    0x83, 0x00, 0x79, 0x3e, 0x34, 0x24, 0x00, 0x79,
-    0x40, 0x37, 0x57, 0x00, 0x79, 0x41, 0x1d, 0xf4,
-    0x00, 0x79, 0x47, 0x1d, 0xeb, 0x00, 0x79, 0x48,
-    0x06, 0x41, 0x00, 0x79, 0x49, 0x34, 0x21, 0x00,
-    0x79, 0x50, 0x0f, 0x1e, 0x00, 0x79, 0x53, 0x37,
-    0x58, 0x00, 0x79, 0x56, 0x34, 0x2f, 0x00, 0x79,
-    0x5d, 0x09, 0x55, 0x00, 0x79, 0x5e, 0x0a, 0x06,
-    0x00, 0x79, 0x62, 0x1f, 0x29, 0x00, 0x79, 0x65,
-    0x09, 0xb5, 0x00, 0x79, 0x8d, 0x05, 0x52, 0x00,
-    0x79, 0x8e, 0x34, 0x3b, 0x00, 0x79, 0x8f, 0x21,
-    0x87, 0x00, 0x79, 0xa7, 0x4e, 0xd7, 0x00, 0x79,
-    0xae, 0x37, 0x5b, 0x00, 0x79, 0xb0, 0x1e, 0x59,
-    0x00, 0x79, 0xb1, 0x4e, 0xd8, 0x00, 0x79, 0xba,
-    0x35, 0x03, 0x00, 0x79, 0xe4, 0x1e, 0x5d, 0x00,
-    0x7a, 0x0b, 0x36, 0x78, 0x00, 0x7a, 0x17, 0x1e,
-    0x66, 0x00, 0x7a, 0x19, 0x35, 0x04, 0x00, 0x7a,
-    0x31, 0x1e, 0xa6, 0x00, 0x7a, 0x40, 0x08, 0x04,
-    0x00, 0x7a, 0x60, 0x3a, 0x4e, 0x00, 0x7a, 0x74,
-    0x34, 0x7a, 0x00, 0x7a, 0x7a, 0x35, 0xa7, 0x00,
-    0x7a, 0x7f, 0x1f, 0x25, 0x00, 0x7a, 0x81, 0x34,
-    0x3d, 0x00, 0x7a, 0x95, 0x35, 0x05, 0x00, 0x7a,
-    0x97, 0x1f, 0x3c, 0x00, 0x7a, 0xae, 0x34, 0x77,
-    0x00, 0x7a, 0xbe, 0x4e, 0xd9, 0x00, 0x7a, 0xc6,
-    0x3c, 0x27, 0x00, 0x7a, 0xc8, 0x4f, 0x3d, 0x00,
-    0x7b, 0x08, 0x1f, 0x1f, 0x00, 0x7b, 0x51, 0x36,
-    0x63, 0x00, 0x7b, 0x75, 0x4f, 0x2a, 0x00, 0x7b,
-    0x99, 0x1e, 0xa8, 0x00, 0x7b, 0xad, 0x1f, 0x26,
-    0x00, 0x7b, 0xb8, 0x1e, 0x5f, 0x00, 0x7b, 0xc0,
-    0x34, 0x2e, 0x00, 0x7b, 0xc7, 0x1f, 0x2b, 0x00,
-    0x7b, 0xc9, 0x36, 0x61, 0x00, 0x7b, 0xdd, 0x1f,
-    0x3d, 0x00, 0x7b, 0xe0, 0x4e, 0xda, 0x00, 0x7c,
-    0x14, 0x37, 0x5f, 0x00, 0x7c, 0x3e, 0x1f, 0x2d,
-    0x00, 0x7c, 0x3f, 0x36, 0xc2, 0x00, 0x7c, 0x4d,
-    0x36, 0x36, 0x00, 0x7c, 0x50, 0x37, 0x61, 0x00,
-    0x7c, 0x58, 0x37, 0x62, 0x00, 0x7c, 0x69, 0x56,
-    0xaa, 0x00, 0x7c, 0x7e, 0x1e, 0x78, 0x00, 0x7c,
-    0x82, 0x4f, 0x30, 0x00, 0x7c, 0x89, 0x34, 0xbe,
-    0x00, 0x7c, 0x90, 0x1e, 0xa9, 0x00, 0x7c, 0xae,
-    0x1e, 0xaa, 0x00, 0x7c, 0xbe, 0x0a, 0x5e, 0x00,
-    0x7c, 0xd6, 0x0c, 0x76, 0x00, 0x7c, 0xf2, 0x35,
-    0x06, 0x00, 0x7d, 0x04, 0x36, 0xee, 0x00, 0x7d,
-    0x09, 0x4e, 0xdc, 0x00, 0x7d, 0x0b, 0x36, 0xec,
-    0x00, 0x7d, 0x0d, 0x36, 0x94, 0x00, 0x7d, 0x1a,
-    0x35, 0x91, 0x00, 0x7d, 0x1b, 0x34, 0xbf, 0x00,
-    0x7d, 0x42, 0x35, 0xf8, 0x00, 0x7d, 0x46, 0x37,
-    0x63, 0x00, 0x7d, 0x5c, 0x21, 0x90, 0x00, 0x7d,
-    0x5e, 0x34, 0x84, 0x00, 0x7d, 0x63, 0x37, 0x64,
-    0x00, 0x7d, 0x73, 0x35, 0x07, 0x00, 0x7d, 0x9b,
-    0x1e, 0xab, 0x00, 0x7d, 0x9f, 0x1e, 0xad, 0x00,
-    0x7d, 0xae, 0x1e, 0xac, 0x00, 0x7d, 0xb2, 0x4e,
-    0xdd, 0x00, 0x7d, 0xcb, 0x34, 0xb6, 0x00, 0x7d,
-    0xcf, 0x34, 0xa0, 0x00, 0x7d, 0xdd, 0x35, 0x08,
-    0x00, 0x7d, 0xe8, 0x36, 0xbf, 0x00, 0x7d, 0xe9,
-    0x35, 0x7a, 0x00, 0x7d, 0xef, 0x34, 0x62, 0x00,
-    0x7d, 0xf4, 0x0f, 0xc5, 0x00, 0x7e, 0x09, 0x47,
-    0xbe, 0x00, 0x7e, 0x1b, 0x36, 0x9b, 0x00, 0x7e,
-    0x22, 0x37, 0x65, 0x00, 0x7e, 0x2b, 0x36, 0xc8,
-    0x00, 0x7e, 0x35, 0x35, 0x09, 0x00, 0x7e, 0x41,
-    0x34, 0x40, 0x00, 0x7e, 0x43, 0x37, 0x69, 0x00,
-    0x7e, 0x6d, 0x36, 0xe1, 0x00, 0x7e, 0x8c, 0x37,
-    0x6a, 0x00, 0x7f, 0x3e, 0x4e, 0xdf, 0x00, 0x7f,
-    0x50, 0x37, 0x6b, 0x00, 0x7f, 0x61, 0x3c, 0x28,
-    0x00, 0x7f, 0x6a, 0x34, 0x89, 0x00, 0x7f, 0x6e,
-    0x36, 0x60, 0x00, 0x7f, 0x72, 0x09, 0x7a, 0x00,
-    0x7f, 0x80, 0x56, 0xda, 0x00, 0x7f, 0x8a, 0x0f,
-    0x3d, 0x00, 0x7f, 0xa1, 0x36, 0x3d, 0x00, 0x7f,
-    0xae, 0x35, 0x0a, 0x00, 0x7f, 0xbd, 0x04, 0xcb,
-    0x00, 0x7f, 0xc1, 0x34, 0x6a, 0x00, 0x7f, 0xc5,
-    0x37, 0x6f, 0x00, 0x7f, 0xc6, 0x37, 0x70, 0x00,
-    0x7f, 0xcc, 0x37, 0x01, 0x00, 0x7f, 0xd2, 0x35,
-    0xf9, 0x00, 0x7f, 0xd4, 0x1e, 0xae, 0x00, 0x7f,
-    0xe0, 0x1e, 0x20, 0x00, 0x7f, 0xe1, 0x35, 0x0b,
-    0x00, 0x7f, 0xe9, 0x1f, 0x3e, 0x00, 0x7f, 0xeb,
-    0x1d, 0xe9, 0x00, 0x7f, 0xf0, 0x1d, 0xe8, 0x00,
-    0x7f, 0xfb, 0x36, 0xd8, 0x00, 0x7f, 0xfc, 0x34,
-    0xc8, 0x00, 0x80, 0x00, 0x1e, 0x7e, 0x00, 0x80,
-    0x03, 0x34, 0x85, 0x00, 0x80, 0x05, 0x34, 0x25,
-    0x00, 0x80, 0x12, 0x4e, 0xe1, 0x00, 0x80, 0x15,
-    0x35, 0xca, 0x00, 0x80, 0x17, 0x36, 0xea, 0x00,
-    0x80, 0x36, 0x34, 0xc7, 0x00, 0x80, 0x56, 0x36,
-    0x2d, 0x00, 0x80, 0x5a, 0x35, 0x0c, 0x00, 0x80,
-    0x5f, 0x35, 0x0d, 0x00, 0x80, 0x61, 0x34, 0xa1,
-    0x00, 0x80, 0x6f, 0x34, 0xcd, 0x00, 0x80, 0x70,
-    0x35, 0x0f, 0x00, 0x80, 0x71, 0x3c, 0x29, 0x00,
-    0x80, 0x73, 0x35, 0x0e, 0x00, 0x80, 0x74, 0x34,
-    0xa7, 0x00, 0x80, 0x76, 0x35, 0x10, 0x00, 0x80,
-    0x77, 0x34, 0x9a, 0x00, 0x80, 0x7e, 0x34, 0xce,
-    0x00, 0x80, 0x87, 0x36, 0x9c, 0x00, 0x80, 0x89,
-    0x36, 0x8f, 0x00, 0x80, 0x96, 0x36, 0x0e, 0x00,
-    0x80, 0x9e, 0x3c, 0x2a, 0x00, 0x80, 0xa9, 0x35,
-    0xb8, 0x00, 0x80, 0xba, 0x36, 0x97, 0x00, 0x80,
-    0xd6, 0x4e, 0xe3, 0x00, 0x80, 0xde, 0x36, 0xc9,
-    0x00, 0x81, 0x06, 0x36, 0x34, 0x00, 0x81, 0x08,
-    0x34, 0xc6, 0x00, 0x81, 0x09, 0x4e, 0xe4, 0x00,
-    0x81, 0x29, 0x4e, 0xe5, 0x00, 0x81, 0x53, 0x35,
-    0x11, 0x00, 0x81, 0x54, 0x35, 0xcb, 0x00, 0x81,
-    0x70, 0x35, 0xd1, 0x00, 0x81, 0x71, 0x4f, 0x33,
-    0x00, 0x81, 0x7f, 0x1e, 0x30, 0x00, 0x81, 0x8a,
-    0x35, 0x12, 0x00, 0x81, 0xb5, 0x35, 0x13, 0x00,
-    0x81, 0xcd, 0x35, 0x14, 0x00, 0x81, 0xed, 0x34,
-    0x26, 0x00, 0x82, 0x00, 0x57, 0x0f, 0x00, 0x82,
-    0x0c, 0x4e, 0xe6, 0x00, 0x82, 0x18, 0x35, 0x7f,
-    0x00, 0x82, 0x1b, 0x4e, 0xe7, 0x00, 0x82, 0x1c,
-    0x34, 0x93, 0x00, 0x82, 0x1f, 0x35, 0xb3, 0x00,
-    0x82, 0x2e, 0x1e, 0xaf, 0x00, 0x82, 0x39, 0x34,
-    0x9f, 0x00, 0x82, 0x40, 0x4e, 0xe8, 0x00, 0x82,
-    0x47, 0x34, 0xab, 0x00, 0x82, 0x58, 0x37, 0x74,
-    0x00, 0x82, 0x79, 0x37, 0x77, 0x00, 0x82, 0x8d,
-    0x1e, 0xb0, 0x00, 0x82, 0x92, 0x4f, 0x44, 0x00,
-    0x82, 0xa6, 0x1f, 0x19, 0x00, 0x82, 0xb1, 0x35,
-    0x62, 0x00, 0x82, 0xbd, 0x05, 0x6a, 0x00, 0x82,
-    0xc5, 0x35, 0x77, 0x00, 0x82, 0xd2, 0x1e, 0xb1,
-    0x00, 0x82, 0xe3, 0x37, 0x78, 0x00, 0x83, 0x23,
-    0x1e, 0xb2, 0x00, 0x83, 0x28, 0x1f, 0x1a, 0x00,
-    0x83, 0x52, 0x35, 0xcc, 0x00, 0x83, 0x75, 0x1e,
-    0xb3, 0x00, 0x83, 0xbd, 0x37, 0x7c, 0x00, 0x83,
-    0xd3, 0x35, 0x63, 0x00, 0x83, 0xd4, 0x4e, 0xea,
-    0x00, 0x83, 0xdc, 0x35, 0xda, 0x00, 0x83, 0xdf,
-    0x1e, 0x4b, 0x00, 0x83, 0xf2, 0x35, 0x15, 0x00,
-    0x84, 0x0c, 0x36, 0xca, 0x00, 0x84, 0x0f, 0x4e,
-    0xeb, 0x00, 0x84, 0x20, 0x37, 0x7b, 0x00, 0x84,
-    0x22, 0x1f, 0x3f, 0x00, 0x84, 0x57, 0x34, 0x37,
-    0x00, 0x84, 0x5b, 0x1d, 0xe4, 0x00, 0x84, 0x5c,
-    0x57, 0x45, 0x00, 0x84, 0x7a, 0x34, 0xba, 0x00,
-    0x84, 0xea, 0x4e, 0xed, 0x00, 0x84, 0xec, 0x1e,
-    0x72, 0x00, 0x84, 0xee, 0x0f, 0xc7, 0x00, 0x84,
-    0xf4, 0x37, 0x7d, 0x00, 0x85, 0x11, 0x36, 0xbd,
-    0x00, 0x85, 0x17, 0x1e, 0xb4, 0x00, 0x85, 0x3d,
-    0x1e, 0x6d, 0x00, 0x85, 0x43, 0x36, 0xa6, 0x00,
-    0x85, 0x51, 0x4e, 0xef, 0x00, 0x85, 0x55, 0x35,
-    0x16, 0x00, 0x85, 0x5d, 0x57, 0x64, 0x00, 0x85,
-    0x63, 0x4e, 0xf0, 0x00, 0x85, 0x84, 0x36, 0x99,
-    0x00, 0x85, 0x87, 0x37, 0x7f, 0x00, 0x85, 0xa9,
-    0x1e, 0x08, 0x00, 0x85, 0xaf, 0x1e, 0x15, 0x00,
-    0x85, 0xcf, 0x4e, 0xf1, 0x00, 0x85, 0xd5, 0x35,
-    0x17, 0x00, 0x85, 0xe4, 0x36, 0x85, 0x00, 0x85,
-    0xf7, 0x1e, 0x16, 0x00, 0x86, 0x12, 0x21, 0xa4,
-    0x00, 0x86, 0x2d, 0x37, 0x04, 0x00, 0x86, 0x4e,
-    0x4e, 0xf2, 0x00, 0x86, 0x50, 0x35, 0x8c, 0x00,
-    0x86, 0x54, 0x4f, 0x32, 0x00, 0x86, 0x5c, 0x0f,
-    0x82, 0x00, 0x86, 0x5e, 0x35, 0xa6, 0x00, 0x86,
-    0x62, 0x4e, 0xf3, 0x00, 0x86, 0x8a, 0x4e, 0xf4,
-    0x00, 0x86, 0xdb, 0x34, 0x5e, 0x00, 0x86, 0xf8,
-    0x1e, 0x35, 0x00, 0x87, 0x03, 0x4f, 0x48, 0x00,
-    0x87, 0x1a, 0x35, 0x18, 0x00, 0x87, 0x37, 0x37,
-    0x82, 0x00, 0x87, 0x3b, 0x37, 0x83, 0x00, 0x87,
-    0x55, 0x1e, 0x1d, 0x00, 0x87, 0x59, 0x1f, 0x40,
-    0x00, 0x87, 0x82, 0x1e, 0xb6, 0x00, 0x87, 0xa3,
-    0x57, 0xaa, 0x00, 0x87, 0xbd, 0x37, 0x85, 0x00,
-    0x87, 0xd2, 0x1e, 0xb7, 0x00, 0x88, 0x03, 0x3b,
-    0x03, 0x00, 0x88, 0x05, 0x37, 0x84, 0x00, 0x88,
-    0x0e, 0x1f, 0x41, 0x00, 0x88, 0x36, 0x35, 0x19,
-    0x00, 0x88, 0x42, 0x4e, 0xf5, 0x00, 0x88, 0x46,
-    0x35, 0xfa, 0x00, 0x88, 0x4b, 0x57, 0xc3, 0x00,
-    0x88, 0x53, 0x35, 0xfd, 0x00, 0x88, 0x5b, 0x34,
-    0x66, 0x00, 0x88, 0x5e, 0x35, 0x53, 0x00, 0x88,
-    0x63, 0x35, 0x48, 0x00, 0x88, 0x70, 0x36, 0x27,
-    0x00, 0x88, 0x77, 0x4e, 0xf6, 0x00, 0x88, 0x9e,
-    0x35, 0x1a, 0x00, 0x88, 0xd8, 0x35, 0x1b, 0x00,
-    0x88, 0xf4, 0x35, 0x1c, 0x00, 0x89, 0x0a, 0x1e,
-    0xb8, 0x00, 0x89, 0x10, 0x34, 0x13, 0x00, 0x89,
-    0x1c, 0x37, 0xcb, 0x00, 0x89, 0x2b, 0x35, 0x1d,
-    0x00, 0x89, 0x3b, 0x35, 0x1e, 0x00, 0x89, 0x41,
-    0x4e, 0xf7, 0x00, 0x89, 0x56, 0x1d, 0xdd, 0x00,
-    0x89, 0x6a, 0x35, 0x1f, 0x00, 0x89, 0x6f, 0x35,
-    0x20, 0x00, 0x89, 0x81, 0x36, 0xff, 0x00, 0x89,
-    0x86, 0x36, 0xb8, 0x00, 0x89, 0x87, 0x36, 0x95,
-    0x00, 0x89, 0x96, 0x34, 0x22, 0x00, 0x89, 0xaa,
-    0x34, 0x9b, 0x00, 0x89, 0xaf, 0x1e, 0xb9, 0x00,
-    0x89, 0xbd, 0x37, 0x8a, 0x00, 0x89, 0xd2, 0x05,
-    0xaf, 0x00, 0x8a, 0x0a, 0x36, 0x24, 0x00, 0x8a,
-    0x12, 0x37, 0xd7, 0x00, 0x8a, 0x1d, 0x35, 0x21,
-    0x00, 0x8a, 0x1f, 0x34, 0x96, 0x00, 0x8a, 0x3b,
-    0x1e, 0x3c, 0x00, 0x8a, 0x55, 0x36, 0xaf, 0x00,
-    0x8a, 0x6e, 0x1e, 0x28, 0x00, 0x8a, 0x8d, 0x36,
-    0x92, 0x00, 0x8a, 0x95, 0x34, 0xa3, 0x00, 0x8a,
-    0xa0, 0x36, 0x2f, 0x00, 0x8a, 0xa4, 0x35, 0xc2,
-    0x00, 0x8a, 0xb9, 0x34, 0xb7, 0x00, 0x8a, 0xbf,
-    0x36, 0x6d, 0x00, 0x8a, 0xcb, 0x36, 0x30, 0x00,
-    0x8a, 0xdb, 0x37, 0x8b, 0x00, 0x8a, 0xde, 0x1e,
-    0xba, 0x00, 0x8a, 0xed, 0x0f, 0x0b, 0x00, 0x8a,
-    0xee, 0x35, 0xe3, 0x00, 0x8a, 0xf8, 0x09, 0x7e,
-    0x00, 0x8a, 0xfa, 0x1d, 0xfe, 0x00, 0x8b, 0x01,
-    0x04, 0xfc, 0x00, 0x8b, 0x04, 0x36, 0x86, 0x00,
-    0x8b, 0x0e, 0x1e, 0x56, 0x00, 0x8b, 0x19, 0x35,
-    0xb9, 0x00, 0x8b, 0x1b, 0x35, 0xcd, 0x00, 0x8b,
-    0x1d, 0x34, 0x8d, 0x00, 0x8b, 0x2c, 0x1e, 0x69,
-    0x00, 0x8b, 0x39, 0x06, 0xd8, 0x00, 0x8b, 0x3e,
-    0x37, 0x8c, 0x00, 0x8b, 0x41, 0x1e, 0xbb, 0x00,
-    0x8b, 0x56, 0x4e, 0xf8, 0x00, 0x8b, 0x5a, 0x37,
-    0x8d, 0x00, 0x8b, 0x5c, 0x4e, 0xfa, 0x00, 0x8b,
-    0x7f, 0x52, 0x52, 0x00, 0x8c, 0x6a, 0x4e, 0xfd,
-    0x00, 0x8c, 0x79, 0x4e, 0xfe, 0x00, 0x8c, 0x9b,
-    0x58, 0x37, 0x00, 0x8c, 0xa0, 0x36, 0xb5, 0x00,
-    0x8c, 0xa7, 0x34, 0xb8, 0x00, 0x8c, 0xa8, 0x35,
-    0x64, 0x00, 0x8c, 0xab, 0x34, 0x72, 0x00, 0x8c,
-    0xc7, 0x35, 0xe5, 0x00, 0x8c, 0xca, 0x36, 0x4c,
-    0x00, 0x8c, 0xd3, 0x0d, 0xc2, 0x00, 0x8c, 0xed,
-    0x1e, 0x4c, 0x00, 0x8c, 0xfc, 0x34, 0x86, 0x00,
-    0x8d, 0x05, 0x35, 0x22, 0x00, 0x8d, 0x08, 0x34,
-    0x34, 0x00, 0x8d, 0x0f, 0x35, 0x23, 0x00, 0x8d,
-    0x67, 0x4f, 0x00, 0x00, 0x8d, 0x70, 0x36, 0x46,
-    0x00, 0x8d, 0x73, 0x37, 0x8e, 0x00, 0x8d, 0x77,
-    0x35, 0x88, 0x00, 0x8d, 0x99, 0x37, 0x8f, 0x00,
-    0x8d, 0xda, 0x1e, 0xbc, 0x00, 0x8d, 0xdd, 0x35,
-    0x94, 0x00, 0x8d, 0xf3, 0x34, 0xa8, 0x00, 0x8e,
-    0x09, 0x1e, 0xbd, 0x00, 0x8e, 0x34, 0x37, 0x91,
-    0x00, 0x8e, 0x4a, 0x37, 0x92, 0x00, 0x8e, 0x8d,
-    0x36, 0xef, 0x00, 0x8e, 0x91, 0x35, 0x25, 0x00,
-    0x8e, 0xa1, 0x35, 0x26, 0x00, 0x8e, 0xcc, 0x34,
-    0x76, 0x00, 0x8e, 0xd4, 0x3b, 0x4a, 0x00, 0x8f,
-    0x03, 0x4f, 0x02, 0x00, 0x8f, 0x13, 0x1e, 0xbe,
-    0x00, 0x8f, 0x29, 0x34, 0xb0, 0x00, 0x8f, 0x2f,
-    0x34, 0x90, 0x00, 0x8f, 0x38, 0x36, 0xf5, 0x00,
-    0x8f, 0x44, 0x35, 0x75, 0x00, 0x8f, 0xb6, 0x3c,
-    0x2b, 0x00, 0x8f, 0xbb, 0x20, 0x4b, 0x00, 0x8f,
-    0xbc, 0x35, 0xd3, 0x00, 0x8f, 0xbf, 0x1e, 0x37,
-    0x00, 0x8f, 0xc2, 0x1d, 0xd6, 0x00, 0x8f, 0xc4,
-    0x1f, 0x2c, 0x00, 0x8f, 0xc5, 0x36, 0x26, 0x00,
-    0x8f, 0xc6, 0x3b, 0x51, 0x00, 0x8f, 0xce, 0x35,
-    0xae, 0x00, 0x8f, 0xd1, 0x35, 0xa2, 0x00, 0x8f,
-    0xd4, 0x36, 0xc0, 0x00, 0x8f, 0xe6, 0x1d, 0xdf,
-    0x00, 0x8f, 0xe9, 0x1e, 0xc0, 0x00, 0x8f, 0xea,
-    0x1e, 0xbf, 0x00, 0x8f, 0xeb, 0x36, 0x9a, 0x00,
-    0x8f, 0xed, 0x36, 0x7b, 0x00, 0x8f, 0xef, 0x37,
-    0x93, 0x00, 0x8f, 0xf0, 0x35, 0xfe, 0x00, 0x8f,
-    0xf6, 0x4f, 0x06, 0x00, 0x8f, 0xf7, 0x36, 0xe6,
-    0x00, 0x8f, 0xfa, 0x37, 0x95, 0x00, 0x8f, 0xfd,
-    0x36, 0x72, 0x00, 0x90, 0x00, 0x36, 0x51, 0x00,
-    0x90, 0x01, 0x36, 0x47, 0x00, 0x90, 0x03, 0x34,
-    0xad, 0x00, 0x90, 0x06, 0x35, 0x8d, 0x00, 0x90,
-    0x0e, 0x35, 0x28, 0x00, 0x90, 0x0f, 0x36, 0x88,
-    0x00, 0x90, 0x10, 0x36, 0x64, 0x00, 0x90, 0x14,
-    0x36, 0x7e, 0x00, 0x90, 0x17, 0x1e, 0x1f, 0x00,
-    0x90, 0x19, 0x1e, 0x5c, 0x00, 0x90, 0x1a, 0x36,
-    0x73, 0x00, 0x90, 0x1d, 0x1e, 0x22, 0x00, 0x90,
-    0x1e, 0x37, 0x96, 0x00, 0x90, 0x1f, 0x36, 0x4b,
-    0x00, 0x90, 0x20, 0x36, 0x49, 0x00, 0x90, 0x22,
-    0x20, 0x4a, 0x00, 0x90, 0x23, 0x0f, 0xc8, 0x00,
-    0x90, 0x2e, 0x36, 0x52, 0x00, 0x90, 0x31, 0x35,
-    0xfb, 0x00, 0x90, 0x32, 0x36, 0x1f, 0x00, 0x90,
-    0x35, 0x37, 0x97, 0x00, 0x90, 0x38, 0x34, 0x08,
-    0x00, 0x90, 0x39, 0x36, 0x58, 0x00, 0x90, 0x3c,
-    0x1e, 0x67, 0x00, 0x90, 0x41, 0x1e, 0x53, 0x00,
-    0x90, 0x42, 0x34, 0x9c, 0x00, 0x90, 0x47, 0x35,
-    0xa8, 0x00, 0x90, 0x4a, 0x36, 0xfc, 0x00, 0x90,
-    0x4b, 0x35, 0x51, 0x00, 0x90, 0x4d, 0x36, 0xc1,
-    0x00, 0x90, 0x4e, 0x35, 0x65, 0x00, 0x90, 0x50,
-    0x37, 0x98, 0x00, 0x90, 0x52, 0x35, 0x27, 0x00,
-    0x90, 0x53, 0x36, 0x8b, 0x00, 0x90, 0x54, 0x36,
-    0x58, 0x00, 0x90, 0x55, 0x34, 0x63, 0x00, 0x90,
-    0x58, 0x1e, 0xc1, 0x00, 0x90, 0x5c, 0x1e, 0x2e,
-    0x00, 0x90, 0x60, 0x35, 0x5a, 0x00, 0x90, 0x61,
-    0x1e, 0x2a, 0x00, 0x90, 0x63, 0x35, 0xba, 0x00,
-    0x90, 0x69, 0x36, 0x7a, 0x00, 0x90, 0x6d, 0x36,
-    0x48, 0x00, 0x90, 0x6e, 0x1e, 0x0e, 0x00, 0x90,
-    0x75, 0x36, 0x00, 0x00, 0x90, 0x77, 0x36, 0x40,
-    0x00, 0x90, 0x78, 0x36, 0x3f, 0x00, 0x90, 0x7a,
-    0x35, 0x4a, 0x00, 0x90, 0x7c, 0x1e, 0x80, 0x00,
-    0x90, 0x7f, 0x36, 0xa7, 0x00, 0x90, 0x81, 0x37,
-    0x9a, 0x00, 0x90, 0x83, 0x37, 0x5c, 0x00, 0x90,
-    0x84, 0x35, 0x7c, 0x00, 0x90, 0x87, 0x37, 0x94,
-    0x00, 0x90, 0x89, 0x34, 0x5f, 0x00, 0x90, 0x8a,
-    0x37, 0x9b, 0x00, 0x90, 0xa3, 0x1e, 0x55, 0x00,
-    0x90, 0xa6, 0x36, 0xcb, 0x00, 0x90, 0xa8, 0x4f,
-    0x0b, 0x00, 0x90, 0xaa, 0x34, 0x8e, 0x00, 0x90,
-    0xf7, 0x35, 0x9d, 0x00, 0x90, 0xfd, 0x0c, 0x4e,
-    0x00, 0x91, 0x2d, 0x1e, 0x44, 0x00, 0x91, 0x30,
-    0x35, 0x29, 0x00, 0x91, 0x4b, 0x1e, 0x12, 0x00,
-    0x91, 0x4c, 0x35, 0xf2, 0x00, 0x91, 0x4d, 0x4f,
-    0x0c, 0x00, 0x91, 0x56, 0x35, 0x2a, 0x00, 0x91,
-    0x58, 0x35, 0x2b, 0x00, 0x91, 0x65, 0x35, 0x2c,
-    0x00, 0x91, 0x72, 0x35, 0x2e, 0x00, 0x91, 0x73,
-    0x35, 0x2d, 0x00, 0x91, 0x77, 0x35, 0xd0, 0x00,
-    0x91, 0xa2, 0x35, 0x2f, 0x00, 0x91, 0xaa, 0x35,
-    0x31, 0x00, 0x91, 0xaf, 0x35, 0x30, 0x00, 0x91,
-    0xb1, 0x36, 0x9f, 0x00, 0x91, 0xb4, 0x35, 0x32,
-    0x00, 0x91, 0xba, 0x35, 0x33, 0x00, 0x91, 0xc1,
-    0x1e, 0xc3, 0x00, 0x91, 0xc7, 0x1e, 0x05, 0x00,
-    0x91, 0xdc, 0x4f, 0x42, 0x00, 0x91, 0xe3, 0x36,
-    0x75, 0x00, 0x91, 0xfc, 0x3c, 0x2c, 0x00, 0x92,
-    0x37, 0x34, 0x7d, 0x00, 0x92, 0x5b, 0x34, 0x69,
-    0x00, 0x92, 0xe9, 0x4f, 0x0d, 0x00, 0x93, 0x06,
-    0x1e, 0x0a, 0x00, 0x93, 0x35, 0x4f, 0x0e, 0x00,
-    0x93, 0x65, 0x3b, 0x86, 0x00, 0x93, 0x75, 0x4f,
-    0x34, 0x00, 0x93, 0x8b, 0x4f, 0x0f, 0x00, 0x93,
-    0x8c, 0x35, 0x76, 0x00, 0x93, 0x96, 0x35, 0xd5,
-    0x00, 0x93, 0x9a, 0x1e, 0x41, 0x00, 0x93, 0xa1,
-    0x59, 0x04, 0x00, 0x93, 0xae, 0x34, 0x3a, 0x00,
-    0x93, 0xdd, 0x37, 0xae, 0x00, 0x94, 0x3a, 0x4f,
-    0x10, 0x00, 0x94, 0x53, 0x1e, 0x79, 0x00, 0x94,
-    0x77, 0x35, 0x34, 0x00, 0x95, 0x92, 0x35, 0x7d,
-    0x00, 0x95, 0xab, 0x3b, 0x9a, 0x00, 0x95, 0xbb,
-    0x1e, 0xc4, 0x00, 0x95, 0xbc, 0x37, 0xaf, 0x00,
-    0x95, 0xcd, 0x4f, 0x11, 0x00, 0x96, 0x2a, 0x4f,
-    0x12, 0x00, 0x96, 0x4d, 0x34, 0x87, 0x00, 0x96,
-    0x86, 0x34, 0x51, 0x00, 0x96, 0x8a, 0x36, 0x53,
-    0x00, 0x96, 0x94, 0x35, 0x73, 0x00, 0x96, 0x98,
-    0x35, 0x35, 0x00, 0x96, 0x99, 0x4f, 0x31, 0x00,
-    0x96, 0xa3, 0x34, 0xca, 0x00, 0x96, 0xa7, 0x4f,
-    0x14, 0x00, 0x96, 0xb2, 0x37, 0xb1, 0x00, 0x96,
-    0xbb, 0x36, 0x35, 0x00, 0x96, 0xc5, 0x34, 0x6c,
-    0x00, 0x96, 0xc7, 0x35, 0xbe, 0x00, 0x96, 0xd9,
-    0x34, 0xd5, 0x00, 0x96, 0xda, 0x59, 0x3b, 0x00,
-    0x96, 0xe3, 0x0c, 0xc9, 0x00, 0x96, 0xe8, 0x35,
-    0x4d, 0x00, 0x96, 0xea, 0x36, 0x39, 0x00, 0x96,
-    0xf0, 0x34, 0xc0, 0x00, 0x97, 0x21, 0x59, 0x41,
-    0x00, 0x97, 0x24, 0x1e, 0xc6, 0x00, 0x97, 0x3d,
-    0x35, 0x36, 0x00, 0x97, 0x55, 0x21, 0xf8, 0x00,
-    0x97, 0x56, 0x21, 0x8b, 0x00, 0x97, 0x59, 0x37,
-    0xb2, 0x00, 0x97, 0x5c, 0x36, 0x31, 0x00, 0x97,
-    0x60, 0x1e, 0xc7, 0x00, 0x97, 0x6d, 0x1e, 0xc8,
-    0x00, 0x97, 0x71, 0x1e, 0x1e, 0x00, 0x97, 0x74,
-    0x1d, 0xf3, 0x00, 0x97, 0x84, 0x1d, 0xe5, 0x00,
-    0x97, 0x98, 0x1e, 0x1c, 0x00, 0x97, 0xad, 0x4f,
-    0x43, 0x00, 0x97, 0xd3, 0x35, 0x7e, 0x00, 0x97,
-    0xde, 0x3c, 0x2e, 0x00, 0x97, 0xf3, 0x35, 0x60,
-    0x00, 0x97, 0xff, 0x34, 0x19, 0x00, 0x98, 0x0c,
-    0x35, 0x39, 0x00, 0x98, 0x11, 0x34, 0x74, 0x00,
-    0x98, 0x12, 0x34, 0xb2, 0x00, 0x98, 0x13, 0x1e,
-    0x54, 0x00, 0x98, 0x24, 0x1e, 0xc9, 0x00, 0x98,
-    0x3b, 0x0d, 0xc3, 0x00, 0x98, 0x5e, 0x0f, 0xa8,
-    0x00, 0x98, 0x67, 0x35, 0xbf, 0x00, 0x98, 0x73,
-    0x35, 0x3a, 0x00, 0x98, 0xc3, 0x35, 0x3b, 0x00,
-    0x98, 0xdf, 0x36, 0x17, 0x00, 0x98, 0xe2, 0x35,
-    0x89, 0x00, 0x98, 0xeb, 0x37, 0xb4, 0x00, 0x98,
-    0xef, 0x0d, 0x67, 0x00, 0x98, 0xf4, 0x1d, 0xd2,
-    0x00, 0x98, 0xfc, 0x21, 0xfc, 0x00, 0x98, 0xfd,
-    0x36, 0xcd, 0x00, 0x98, 0xfe, 0x36, 0x14, 0x00,
-    0x99, 0x03, 0x37, 0xb5, 0x00, 0x99, 0x05, 0x1e,
-    0x77, 0x00, 0x99, 0x09, 0x37, 0xb6, 0x00, 0x99,
-    0x0a, 0x37, 0x00, 0x00, 0x99, 0x0c, 0x1d, 0xdb,
-    0x00, 0x99, 0x10, 0x1f, 0x22, 0x00, 0x99, 0x13,
-    0x35, 0x68, 0x00, 0x99, 0x21, 0x4f, 0x18, 0x00,
-    0x99, 0x28, 0x21, 0xfe, 0x00, 0x99, 0x45, 0x37,
-    0xb7, 0x00, 0x99, 0x4b, 0x37, 0xb9, 0x00, 0x99,
-    0x57, 0x1f, 0x20, 0x00, 0x99, 0xc1, 0x4f, 0x40,
-    0x00, 0x99, 0xd0, 0x36, 0x67, 0x00, 0x9a, 0x19,
-    0x1f, 0x43, 0x00, 0x9a, 0x30, 0x36, 0x89, 0x00,
-    0x9a, 0x45, 0x35, 0x3c, 0x00, 0x9a, 0x4a, 0x59,
-    0x88, 0x00, 0x9a, 0x5f, 0x37, 0xbb, 0x00, 0x9a,
-    0x65, 0x37, 0xbc, 0x00, 0x9a, 0xef, 0x37, 0xbd,
-    0x00, 0x9b, 0x18, 0x37, 0xbe, 0x00, 0x9b, 0x2d,
-    0x34, 0x3c, 0x00, 0x9b, 0x2e, 0x1e, 0xca, 0x00,
-    0x9b, 0x35, 0x59, 0xa4, 0x00, 0x9b, 0x4d, 0x35,
-    0x3d, 0x00, 0x9b, 0x54, 0x36, 0xdb, 0x00, 0x9b,
-    0x58, 0x35, 0x3e, 0x00, 0x9b, 0x97, 0x1e, 0xcb,
-    0x00, 0x9b, 0xa8, 0x4f, 0x1a, 0x00, 0x9b, 0xab,
-    0x4f, 0x38, 0x00, 0x9b, 0xae, 0x4f, 0x1b, 0x00,
-    0x9b, 0xb9, 0x4f, 0x1c, 0x00, 0x9b, 0xc6, 0x35,
-    0x3f, 0x00, 0x9b, 0xd6, 0x1e, 0x09, 0x00, 0x9b,
-    0xdb, 0x36, 0x54, 0x00, 0x9b, 0xe1, 0x35, 0x40,
-    0x00, 0x9b, 0xf1, 0x35, 0x41, 0x00, 0x9b, 0xf2,
-    0x1e, 0xcc, 0x00, 0x9c, 0x08, 0x4f, 0x1d, 0x00,
-    0x9c, 0x24, 0x4f, 0x1e, 0x00, 0x9c, 0x2f, 0x1d,
-    0xd4, 0x00, 0x9c, 0x3b, 0x4f, 0x1f, 0x00, 0x9c,
-    0x48, 0x1e, 0x39, 0x00, 0x9c, 0x52, 0x1e, 0x74,
-    0x00, 0x9c, 0x57, 0x37, 0x0c, 0x00, 0x9c, 0xe6,
-    0x4f, 0x21, 0x00, 0x9d, 0x07, 0x1e, 0x4f, 0x00,
-    0x9d, 0x08, 0x37, 0xc1, 0x00, 0x9d, 0x09, 0x37,
-    0xc0, 0x00, 0x9d, 0x48, 0x35, 0x42, 0x00, 0x9d,
-    0x60, 0x1e, 0x03, 0x00, 0x9d, 0x6c, 0x36, 0xce,
-    0x00, 0x9d, 0xb4, 0x0b, 0xfd, 0x00, 0x9d, 0xbf,
-    0x59, 0xde, 0x00, 0x9d, 0xc0, 0x4f, 0x22, 0x00,
-    0x9d, 0xc2, 0x4f, 0x23, 0x00, 0x9d, 0xcf, 0x35,
-    0x43, 0x00, 0x9e, 0x97, 0x34, 0xcc, 0x00, 0x9e,
-    0x9f, 0x34, 0xcb, 0x00, 0x9e, 0xa5, 0x37, 0xc2,
-    0x00, 0x9e, 0xaa, 0x1e, 0xcd, 0x00, 0x9e, 0xad,
-    0x1f, 0x44, 0x00, 0x9e, 0xbb, 0x36, 0xdc, 0x00,
-    0x9e, 0xbf, 0x36, 0xe2, 0x00, 0x9e, 0xcc, 0x37,
-    0xc3, 0x00, 0x9e, 0xdb, 0x1e, 0x31, 0x00, 0x9f,
-    0x08, 0x35, 0x44, 0x00, 0x9f, 0x3b, 0x36, 0xa9,
-    0x00, 0x9f, 0x4a, 0x37, 0xc5, 0x00, 0x9f, 0x4b,
-    0x37, 0x5a, 0x00, 0x9f, 0x4e, 0x35, 0x24, 0x00,
-    0x9f, 0x67, 0x37, 0xc7, 0x00, 0x9f, 0x8d, 0x37,
-    0x06, 0x00, 0x9f, 0x9c, 0x1e, 0xce, 0x00, 0x9f,
-    0x9d, 0x1e, 0xa7, 0x00, 0xfa, 0x11, 0x20, 0xfb,
-    0x00, 0xfa, 0x24, 0x21, 0xb8, 0x02, 0x35, 0xc4,
-    0x3c, 0x44, 0x02, 0x36, 0x3a, 0x35, 0x9b, 0x02,
-    0x38, 0x3d, 0x4f, 0x26, 0x02, 0x42, 0xee, 0x37,
-    0xc9, 0x02, 0x62, 0x70, 0x37, 0x6e, 0x02, 0x9d,
-    0x4b, 0x35, 0x96, 0x02, 0x9e, 0x3d, 0x3c, 0x4d,
-    0x02, 0xa6, 0x1a, 0x37, 0xc8, 0x00, 0x00, 0x00,
-    0x7f, 0x00, 0x34, 0x02, 0x35, 0x83, 0x00, 0x50,
-    0x91, 0x35, 0xaf, 0x00, 0x50, 0xca, 0x37, 0x15,
-    0x00, 0x51, 0x54, 0x37, 0x17, 0x00, 0x51, 0x95,
-    0x37, 0x18, 0x00, 0x51, 0xb4, 0x35, 0xdb, 0x00,
-    0x51, 0xde, 0x38, 0x10, 0x00, 0x52, 0x72, 0x4e,
-    0x76, 0x00, 0x53, 0x7f, 0x1d, 0xed, 0x00, 0x53,
-    0xa9, 0x1f, 0x1c, 0x00, 0x55, 0x33, 0x37, 0x21,
-    0x00, 0x55, 0xa9, 0x34, 0xd6, 0x00, 0x55, 0xab,
-    0x4e, 0x7c, 0x00, 0x55, 0xe4, 0x37, 0x22, 0x00,
-    0x56, 0xae, 0x4e, 0x7e, 0x00, 0x57, 0xf4, 0x36,
-    0x13, 0x00, 0x58, 0x5a, 0x20, 0xe6, 0x00, 0x59,
-    0x51, 0x4e, 0x88, 0x00, 0x59, 0xff, 0x35, 0xe1,
-    0x00, 0x5a, 0xbe, 0x34, 0xdc, 0x00, 0x5b, 0xb3,
-    0x35, 0x6b, 0x00, 0x5c, 0x0a, 0x36, 0x4e, 0x00,
-    0x5c, 0x0f, 0x36, 0x0a, 0x00, 0x5e, 0xca, 0x34,
-    0x59, 0x00, 0x5e, 0xe3, 0x4e, 0x93, 0x00, 0x5e,
-    0xf6, 0x35, 0x56, 0x00, 0x60, 0x62, 0x4f, 0x2d,
-    0x00, 0x60, 0x97, 0x37, 0x30, 0x00, 0x61, 0x67,
-    0x35, 0xad, 0x00, 0x61, 0x68, 0x34, 0x6e, 0x00,
-    0x61, 0xb2, 0x4e, 0x98, 0x00, 0x61, 0xf2, 0x36,
-    0x6a, 0x00, 0x62, 0x49, 0x34, 0xb4, 0x00, 0x66,
-    0x5f, 0x37, 0x38, 0x00, 0x66, 0xc1, 0x4e, 0xac,
-    0x00, 0x67, 0x15, 0x36, 0x70, 0x00, 0x67, 0x17,
-    0x21, 0x29, 0x00, 0x67, 0x1b, 0x36, 0xd5, 0x00,
-    0x68, 0x5d, 0x36, 0xde, 0x00, 0x68, 0x7a, 0x36,
-    0xf1, 0x00, 0x69, 0x0d, 0x36, 0x15, 0x00, 0x69,
-    0x82, 0x34, 0x6f, 0x00, 0x6a, 0xdb, 0x35, 0xa9,
-    0x00, 0x6b, 0x21, 0x35, 0xe8, 0x00, 0x6c, 0x08,
-    0x34, 0xf4, 0x00, 0x6c, 0xaa, 0x4e, 0xbb, 0x00,
-    0x6c, 0xbf, 0x34, 0x68, 0x00, 0x6c, 0xe8, 0x32,
-    0x45, 0x00, 0x6d, 0x3e, 0x36, 0x96, 0x00, 0x6e,
-    0x23, 0x34, 0xf6, 0x00, 0x6e, 0xa2, 0x52, 0x4f,
-    0x00, 0x6e, 0xcb, 0x4e, 0xc1, 0x00, 0x6f, 0x11,
-    0x37, 0x45, 0x00, 0x6f, 0x5b, 0x4e, 0xc5, 0x00,
-    0x71, 0x7d, 0x1f, 0x24, 0x00, 0x72, 0x35, 0x35,
-    0xf0, 0x00, 0x73, 0x36, 0x36, 0xf9, 0x00, 0x73,
-    0x37, 0x36, 0xfa, 0x00, 0x73, 0xca, 0x4e, 0xcc,
-    0x00, 0x75, 0x11, 0x35, 0xd2, 0x00, 0x75, 0x15,
-    0x4f, 0x2b, 0x00, 0x79, 0x53, 0x37, 0x59, 0x00,
-    0x7a, 0x74, 0x35, 0xb1, 0x00, 0x7b, 0x08, 0x4f,
-    0x27, 0x00, 0x7b, 0xc0, 0x36, 0x37, 0x00, 0x7c,
-    0x3e, 0x4f, 0x29, 0x00, 0x7c, 0x50, 0x4e, 0xdb,
-    0x00, 0x7c, 0x7e, 0x4f, 0x45, 0x00, 0x7d, 0xb2,
-    0x4e, 0xde, 0x00, 0x7e, 0x22, 0x37, 0x66, 0x00,
-    0x7e, 0x35, 0x37, 0x68, 0x00, 0x7f, 0xc1, 0x35,
-    0x5e, 0x00, 0x7f, 0xe1, 0x4e, 0xe0, 0x00, 0x7f,
-    0xe9, 0x37, 0x71, 0x00, 0x7f, 0xfc, 0x37, 0x02,
-    0x00, 0x81, 0x08, 0x36, 0xe3, 0x00, 0x82, 0x39,
-    0x36, 0x3e, 0x00, 0x82, 0x79, 0x37, 0x76, 0x00,
-    0x82, 0xbd, 0x34, 0x6b, 0x00, 0x83, 0xdf, 0x1f,
-    0x28, 0x00, 0x85, 0x3d, 0x34, 0xc1, 0x00, 0x86,
-    0x12, 0x52, 0x51, 0x00, 0x87, 0xd2, 0x1f, 0x42,
-    0x00, 0x88, 0x05, 0x4f, 0x47, 0x00, 0x88, 0x36,
-    0x37, 0x87, 0x00, 0x8a, 0x0a, 0x36, 0x25, 0x00,
-    0x8a, 0x1d, 0x4f, 0x2c, 0x00, 0x8a, 0x95, 0x36,
-    0x5d, 0x00, 0x8a, 0xee, 0x35, 0xe4, 0x00, 0x8b,
-    0x56, 0x4e, 0xf9, 0x00, 0x8c, 0xa0, 0x36, 0xb6,
-    0x00, 0x8c, 0xc7, 0x35, 0xe6, 0x00, 0x8c, 0xca,
-    0x4e, 0xff, 0x00, 0x8c, 0xfc, 0x35, 0xce, 0x00,
-    0x8f, 0x44, 0x4f, 0x03, 0x00, 0x8f, 0xc5, 0x4f,
-    0x04, 0x00, 0x8f, 0xd4, 0x4f, 0x05, 0x00, 0x90,
-    0x03, 0x36, 0x87, 0x00, 0x90, 0x22, 0x34, 0x60,
-    0x00, 0x90, 0x38, 0x21, 0xb9, 0x00, 0x90, 0x41,
-    0x4f, 0x3f, 0x00, 0x90, 0x42, 0x36, 0x28, 0x00,
-    0x90, 0x55, 0x35, 0x49, 0x00, 0x90, 0x75, 0x36,
-    0x01, 0x00, 0x90, 0x77, 0x4f, 0x07, 0x00, 0x90,
-    0x89, 0x37, 0xa1, 0x00, 0x90, 0x8a, 0x37, 0x9c,
-    0x00, 0x90, 0xa6, 0x36, 0xcc, 0x00, 0x90, 0xaa,
-    0x35, 0xee, 0x00, 0x92, 0x5b, 0x35, 0x5b, 0x00,
-    0x96, 0x86, 0x21, 0xee, 0x00, 0x96, 0x98, 0x4f,
-    0x13, 0x00, 0x96, 0xa3, 0x37, 0x0b, 0x00, 0x96,
-    0xc5, 0x35, 0x67, 0x00, 0x97, 0x5c, 0x36, 0x32,
-    0x00, 0x97, 0x60, 0x35, 0x37, 0x00, 0x97, 0x6d,
-    0x1f, 0x23, 0x00, 0x97, 0x71, 0x35, 0x38, 0x00,
-    0x97, 0xff, 0x35, 0x9e, 0x00, 0x98, 0xef, 0x4f,
-    0x25, 0x00, 0x99, 0x0c, 0x34, 0x65, 0x00, 0x99,
-    0x45, 0x37, 0xb8, 0x00, 0x99, 0x57, 0x35, 0x9f,
-    0x00, 0x9e, 0x9f, 0x37, 0x0d, 0x00, 0x9f, 0x08,
-    0x37, 0xc4, 0x00, 0x9f, 0x8d, 0x37, 0x07, 0x02,
-    0x36, 0x3a, 0x35, 0x9c, 0x00, 0x00, 0x00, 0x11,
-    0x00, 0x51, 0xde, 0x4e, 0x71, 0x00, 0x53, 0xa9,
-    0x34, 0x64, 0x00, 0x56, 0xae, 0x4e, 0x7f, 0x00,
-    0x5b, 0xb3, 0x4e, 0x8f, 0x00, 0x61, 0x68, 0x35,
-    0x6c, 0x00, 0x61, 0xf2, 0x52, 0x50, 0x00, 0x66,
-    0x5f, 0x37, 0x39, 0x00, 0x67, 0x17, 0x37, 0x12,
-    0x00, 0x69, 0x82, 0x35, 0x70, 0x00, 0x75, 0x11,
-    0x4f, 0x3c, 0x00, 0x83, 0xdf, 0x4e, 0xe9, 0x00,
-    0x90, 0x77, 0x4f, 0x08, 0x00, 0x90, 0x89, 0x37,
-    0xa2, 0x00, 0x90, 0x8a, 0x37, 0x9d, 0x00, 0x97,
-    0xff, 0x4f, 0x15, 0x00, 0x99, 0x0c, 0x35, 0x52,
-    0x00, 0x99, 0x57, 0x4f, 0x19, 0x00, 0x00, 0x00,
-    0x06, 0x00, 0x51, 0xde, 0x21, 0x5e, 0x00, 0x53,
-    0xa9, 0x35, 0x4f, 0x00, 0x61, 0x68, 0x35, 0x6d,
-    0x00, 0x90, 0x89, 0x37, 0xa3, 0x00, 0x90, 0x8a,
-    0x37, 0x9e, 0x00, 0x97, 0xff, 0x4f, 0x16, 0x00,
-    0x00, 0x00, 0x04, 0x00, 0x53, 0xa9, 0x4f, 0x2f,
-    0x00, 0x61, 0x68, 0x35, 0x6e, 0x00, 0x90, 0x89,
-    0x37, 0xa4, 0x00, 0x90, 0x8a, 0x37, 0x9f, 0x00,
-    0x00, 0x00, 0x02, 0x00, 0x90, 0x89, 0x37, 0xa5,
-    0x00, 0x90, 0x8a, 0x37, 0xa0, 0x00, 0x00, 0x00,
-    0x02, 0x00, 0x90, 0x89, 0x37, 0xa6, 0x00, 0x90,
-    0x8a, 0x4f, 0x0a, 0x00, 0x00, 0x00, 0x01, 0x00,
-    0x90, 0x89, 0x37, 0xa7, 0x00, 0x00, 0x00, 0x01,
-    0x00, 0x90, 0x89, 0x37, 0xa8, 0x00, 0x00, 0x00,
-    0x01, 0x00, 0x90, 0x89, 0x37, 0xa9, 0x00, 0x00,
-    0x00, 0x01, 0x00, 0x90, 0x89, 0x37, 0xaa, 0x00,
-    0x00, 0x00, 0x01, 0x00, 0x90, 0x89, 0x37, 0xab,
+    0x27, 0x02, 0xe6, 0xea, 0x37, 0x92, 0x03, 0x13,
+    0x50, 0x4a, 0xba, 0x00, 0x00, 0x04, 0xb7, 0x00,
+    0x34, 0x02, 0x35, 0x81, 0x00, 0x4e, 0x08, 0x34,
+    0x97, 0x00, 0x4e, 0x0e, 0x4e, 0x69, 0x00, 0x4e,
+    0x19, 0x36, 0xb9, 0x00, 0x4e, 0x26, 0x4e, 0x6a,
+    0x00, 0x4e, 0x30, 0x3c, 0x1a, 0x00, 0x4e, 0x39,
+    0x36, 0x5a, 0x00, 0x4e, 0x3b, 0x35, 0xf4, 0x00,
+    0x4e, 0x73, 0x36, 0x90, 0x00, 0x4e, 0xa1, 0x36,
+    0xcf, 0x00, 0x4e, 0xa4, 0x34, 0x7f, 0x00, 0x4e,
+    0xca, 0x35, 0xd4, 0x00, 0x4f, 0x34, 0x36, 0xa0,
+    0x00, 0x4f, 0x4f, 0x35, 0xfc, 0x00, 0x4f, 0x60,
+    0x3c, 0x1c, 0x00, 0x4f, 0x73, 0x4e, 0x6c, 0x00,
+    0x4f, 0x75, 0x34, 0x47, 0x00, 0x4f, 0x7f, 0x34,
+    0x8a, 0x00, 0x4f, 0xae, 0x34, 0x46, 0x00, 0x4f,
+    0xb5, 0x36, 0x1b, 0x00, 0x4f, 0xbf, 0x34, 0xc2,
+    0x00, 0x50, 0x24, 0x36, 0x5f, 0x00, 0x50, 0x26,
+    0x1d, 0xfa, 0x00, 0x50, 0x49, 0x34, 0x61, 0x00,
+    0x50, 0x4f, 0x36, 0xbe, 0x00, 0x50, 0x56, 0x4e,
+    0x6d, 0x00, 0x50, 0x65, 0x34, 0x7b, 0x00, 0x50,
+    0x85, 0x34, 0xd0, 0x00, 0x50, 0x91, 0x34, 0x79,
+    0x00, 0x50, 0xc5, 0x1d, 0xee, 0x00, 0x50, 0xca,
+    0x1f, 0x2e, 0x00, 0x50, 0xcf, 0x34, 0xa2, 0x00,
+    0x50, 0xe7, 0x34, 0x30, 0x00, 0x50, 0xed, 0x4e,
+    0x6e, 0x00, 0x50, 0xf2, 0x52, 0xac, 0x00, 0x51,
+    0x1a, 0x37, 0x16, 0x00, 0x51, 0x32, 0x1e, 0x76,
+    0x00, 0x51, 0x46, 0x34, 0xa5, 0x00, 0x51, 0x4d,
+    0x34, 0x4d, 0x00, 0x51, 0x4e, 0x36, 0x7d, 0x00,
+    0x51, 0x54, 0x10, 0x74, 0x00, 0x51, 0x68, 0x36,
+    0x42, 0x00, 0x51, 0x6b, 0x4e, 0x6f, 0x00, 0x51,
+    0x6c, 0x34, 0x80, 0x00, 0x51, 0x77, 0x35, 0xa5,
+    0x00, 0x51, 0x7c, 0x35, 0xb4, 0x00, 0x51, 0x89,
+    0x1e, 0x87, 0x00, 0x51, 0x8d, 0x08, 0x36, 0x00,
+    0x51, 0x92, 0x36, 0xd6, 0x00, 0x51, 0x93, 0x34,
+    0xd1, 0x00, 0x51, 0x95, 0x10, 0x82, 0x00, 0x51,
+    0xa4, 0x1e, 0x89, 0x00, 0x51, 0xac, 0x0c, 0x59,
+    0x00, 0x51, 0xb4, 0x34, 0x5c, 0x00, 0x51, 0xcb,
+    0x1e, 0x3e, 0x00, 0x51, 0xdb, 0x34, 0xd2, 0x00,
+    0x51, 0xde, 0x4f, 0x53, 0x00, 0x51, 0xe1, 0x36,
+    0xd9, 0x00, 0x51, 0xfd, 0x4e, 0x72, 0x00, 0x52,
+    0x03, 0x36, 0x22, 0x00, 0x52, 0x06, 0x34, 0xbb,
+    0x00, 0x52, 0x24, 0x36, 0xa1, 0x00, 0x52, 0x38,
+    0x35, 0xb5, 0x00, 0x52, 0x4a, 0x35, 0xdd, 0x00,
+    0x52, 0x4d, 0x36, 0x41, 0x00, 0x52, 0x64, 0x4e,
+    0x74, 0x00, 0x52, 0x71, 0x4e, 0x75, 0x00, 0x52,
+    0x72, 0x05, 0xc2, 0x00, 0x52, 0x75, 0x1e, 0x2b,
+    0x00, 0x52, 0x8d, 0x37, 0x1a, 0x00, 0x52, 0xc7,
+    0x36, 0xf6, 0x00, 0x52, 0xc9, 0x0e, 0x29, 0x00,
+    0x52, 0xd7, 0x37, 0x1b, 0x00, 0x52, 0xdd, 0x36,
+    0x05, 0x00, 0x52, 0xe2, 0x36, 0x2a, 0x00, 0x52,
+    0xe4, 0x34, 0x1a, 0x00, 0x52, 0xfa, 0x09, 0x07,
+    0x00, 0x53, 0x00, 0x37, 0xcf, 0x00, 0x53, 0x05,
+    0x36, 0xc3, 0x00, 0x53, 0x07, 0x20, 0xd4, 0x00,
+    0x53, 0x15, 0x1f, 0x2f, 0x00, 0x53, 0x16, 0x35,
+    0x61, 0x00, 0x53, 0x39, 0x36, 0xaa, 0x00, 0x53,
+    0x3f, 0x4e, 0x77, 0x00, 0x53, 0x40, 0x34, 0xd4,
+    0x00, 0x53, 0x4a, 0x36, 0xa2, 0x00, 0x53, 0x51,
+    0x0d, 0x70, 0x00, 0x53, 0x5a, 0x36, 0x98, 0x00,
+    0x53, 0x65, 0x52, 0xf3, 0x00, 0x53, 0x71, 0x35,
+    0x80, 0x00, 0x53, 0x78, 0x35, 0x5f, 0x00, 0x53,
+    0x7f, 0x06, 0xa2, 0x00, 0x53, 0xa9, 0x1d, 0xd8,
+    0x00, 0x53, 0xc9, 0x4f, 0x39, 0x00, 0x53, 0xca,
+    0x35, 0x8e, 0x00, 0x53, 0xce, 0x34, 0x8f, 0x00,
+    0x53, 0xd7, 0x35, 0xf5, 0x00, 0x53, 0xdb, 0x1f,
+    0x2a, 0x00, 0x53, 0xdf, 0x37, 0x1f, 0x00, 0x53,
+    0xe0, 0x53, 0x00, 0x00, 0x53, 0xf1, 0x1e, 0x0c,
+    0x00, 0x53, 0xf2, 0x34, 0x8b, 0x00, 0x54, 0x0f,
+    0x34, 0xc9, 0x00, 0x54, 0x38, 0x35, 0x8f, 0x00,
+    0x54, 0x40, 0x4e, 0x79, 0x00, 0x54, 0x48, 0x36,
+    0x76, 0x00, 0x54, 0x68, 0x09, 0x2a, 0x00, 0x54,
+    0xac, 0x4f, 0x35, 0x00, 0x54, 0xb2, 0x35, 0xdc,
+    0x00, 0x54, 0xe8, 0x1e, 0x17, 0x00, 0x55, 0x10,
+    0x36, 0x83, 0x00, 0x55, 0x33, 0x1e, 0x8b, 0x00,
+    0x55, 0x39, 0x1e, 0x8a, 0x00, 0x55, 0x44, 0x1e,
+    0x32, 0x00, 0x55, 0x46, 0x36, 0x06, 0x00, 0x55,
+    0x53, 0x35, 0xaa, 0x00, 0x55, 0x61, 0x38, 0x39,
+    0x00, 0x55, 0x84, 0x4e, 0x5e, 0x00, 0x55, 0x9c,
+    0x4e, 0x7b, 0x00, 0x55, 0x9d, 0x1d, 0xe3, 0x00,
+    0x55, 0xa9, 0x1f, 0x30, 0x00, 0x55, 0xab, 0x35,
+    0x8b, 0x00, 0x55, 0xb0, 0x1d, 0xf0, 0x00, 0x55,
+    0xe4, 0x1e, 0x8c, 0x00, 0x56, 0x05, 0x53, 0x2d,
+    0x00, 0x56, 0x06, 0x0b, 0x70, 0x00, 0x56, 0x09,
+    0x4e, 0x7d, 0x00, 0x56, 0x32, 0x1e, 0x8d, 0x00,
+    0x56, 0x42, 0x1d, 0xda, 0x00, 0x56, 0x4c, 0x1e,
+    0x29, 0x00, 0x56, 0x68, 0x34, 0x15, 0x00, 0x56,
+    0x74, 0x34, 0xbc, 0x00, 0x56, 0x78, 0x1e, 0x52,
+    0x00, 0x56, 0xa5, 0x1e, 0x8e, 0x00, 0x56, 0xae,
+    0x1f, 0x31, 0x00, 0x56, 0xc0, 0x37, 0x24, 0x00,
+    0x56, 0xc1, 0x34, 0xd7, 0x00, 0x56, 0xce, 0x4e,
+    0x82, 0x00, 0x56, 0xee, 0x4e, 0x83, 0x00, 0x57,
+    0x0d, 0x34, 0xd8, 0x00, 0x57, 0x47, 0x34, 0x78,
+    0x00, 0x57, 0x6a, 0x36, 0x74, 0x00, 0x57, 0xce,
+    0x09, 0xd3, 0x00, 0x57, 0xd6, 0x4e, 0x84, 0x00,
+    0x57, 0xf4, 0x34, 0x98, 0x00, 0x58, 0x0b, 0x1e,
+    0x8f, 0x00, 0x58, 0x19, 0x1f, 0x32, 0x00, 0x58,
+    0x35, 0x1e, 0x49, 0x00, 0x58, 0x3d, 0x4e, 0x85,
+    0x00, 0x58, 0x40, 0x34, 0x48, 0x00, 0x58, 0x58,
+    0x1e, 0x4d, 0x00, 0x58, 0x59, 0x4e, 0x86, 0x00,
+    0x58, 0x5a, 0x1e, 0x42, 0x00, 0x58, 0x9c, 0x36,
+    0x71, 0x00, 0x58, 0xa8, 0x0e, 0x7d, 0x00, 0x58,
+    0xab, 0x34, 0xd9, 0x00, 0x59, 0x06, 0x53, 0x7b,
+    0x00, 0x59, 0x1b, 0x1f, 0x33, 0x00, 0x59, 0x27,
+    0x36, 0x55, 0x00, 0x59, 0x4f, 0x4e, 0x87, 0x00,
+    0x59, 0x51, 0x35, 0xab, 0x00, 0x59, 0x53, 0x37,
+    0xd1, 0x00, 0x59, 0x60, 0x4e, 0x89, 0x00, 0x59,
+    0x62, 0x4e, 0x8a, 0x00, 0x59, 0x73, 0x36, 0x04,
+    0x00, 0x59, 0x84, 0x36, 0xe7, 0x00, 0x59, 0xa5,
+    0x36, 0x4f, 0x00, 0x59, 0xc9, 0x34, 0x8c, 0x00,
+    0x59, 0xda, 0x34, 0xda, 0x00, 0x59, 0xec, 0x36,
+    0xae, 0x00, 0x59, 0xff, 0x35, 0xe0, 0x00, 0x5a,
+    0x1c, 0x37, 0x26, 0x00, 0x5a, 0x29, 0x1e, 0x6f,
+    0x00, 0x5a, 0x36, 0x34, 0xdb, 0x00, 0x5a, 0x66,
+    0x36, 0xb2, 0x00, 0x5a, 0x9b, 0x1e, 0x68, 0x00,
+    0x5a, 0xbe, 0x1e, 0x90, 0x00, 0x5a, 0xc2, 0x37,
+    0x27, 0x00, 0x5a, 0xcc, 0x1d, 0xfb, 0x00, 0x5a,
+    0xda, 0x4e, 0x8b, 0x00, 0x5b, 0x5a, 0x4e, 0x8c,
+    0x00, 0x5b, 0x73, 0x4e, 0x8d, 0x00, 0x5b, 0x7c,
+    0x4e, 0x8e, 0x00, 0x5b, 0xb3, 0x34, 0x6d, 0x00,
+    0x5b, 0xb5, 0x36, 0x07, 0x00, 0x5b, 0xc3, 0x37,
+    0x29, 0x00, 0x5b, 0xd2, 0x35, 0x78, 0x00, 0x5b,
+    0xdb, 0x20, 0xf4, 0x00, 0x5b, 0xe7, 0x0c, 0xe1,
+    0x00, 0x5b, 0xe8, 0x37, 0x42, 0x00, 0x5c, 0x06,
+    0x09, 0x95, 0x00, 0x5c, 0x0a, 0x36, 0x4d, 0x00,
+    0x5c, 0x0b, 0x36, 0x23, 0x00, 0x5c, 0x0e, 0x36,
+    0x8a, 0x00, 0x5c, 0x0f, 0x36, 0x09, 0x00, 0x5c,
+    0x28, 0x1f, 0x34, 0x00, 0x5c, 0x51, 0x1d, 0xf2,
+    0x00, 0x5c, 0x60, 0x1e, 0x4a, 0x00, 0x5c, 0x64,
+    0x34, 0x31, 0x00, 0x5c, 0x6e, 0x12, 0x32, 0x00,
+    0x5d, 0x29, 0x36, 0xc4, 0x00, 0x5d, 0x4e, 0x34,
+    0xdd, 0x00, 0x5d, 0x87, 0x34, 0xde, 0x00, 0x5d,
+    0xb2, 0x3c, 0x2d, 0x00, 0x5d, 0xc9, 0x34, 0xdf,
+    0x00, 0x5d, 0xcc, 0x34, 0x73, 0x00, 0x5d, 0xd3,
+    0x34, 0xe0, 0x00, 0x5d, 0xe1, 0x35, 0xff, 0x00,
+    0x5d, 0xe5, 0x35, 0xc3, 0x00, 0x5d, 0xe8, 0x35,
+    0x92, 0x00, 0x5d, 0xf7, 0x1d, 0xff, 0x00, 0x5d,
+    0xfd, 0x0b, 0x65, 0x00, 0x5e, 0x06, 0x36, 0xa3,
+    0x00, 0x5e, 0x1d, 0x36, 0x77, 0x00, 0x5e, 0x30,
+    0x34, 0x75, 0x00, 0x5e, 0x3d, 0x36, 0xd0, 0x00,
+    0x5e, 0x43, 0x4e, 0x91, 0x00, 0x5e, 0x54, 0x37,
+    0x2d, 0x00, 0x5e, 0x63, 0x36, 0xba, 0x00, 0x5e,
+    0x64, 0x1e, 0x93, 0x00, 0x5e, 0x73, 0x36, 0xbb,
+    0x00, 0x5e, 0x7e, 0x35, 0x84, 0x00, 0x5e, 0x96,
+    0x1e, 0x70, 0x00, 0x5e, 0xa7, 0x4e, 0x92, 0x00,
+    0x5e, 0xad, 0x34, 0xa9, 0x00, 0x5e, 0xc9, 0x0f,
+    0xbf, 0x00, 0x5e, 0xca, 0x4f, 0x4f, 0x00, 0x5e,
+    0xcb, 0x38, 0xae, 0x00, 0x5e, 0xcf, 0x1f, 0x36,
+    0x00, 0x5e, 0xd0, 0x1f, 0x35, 0x00, 0x5e, 0xdf,
+    0x1e, 0x6a, 0x00, 0x5e, 0xe0, 0x1e, 0x18, 0x00,
+    0x5e, 0xe3, 0x37, 0x2f, 0x00, 0x5e, 0xf6, 0x34,
+    0x67, 0x00, 0x5e, 0xf7, 0x34, 0xaa, 0x00, 0x5e,
+    0xfa, 0x34, 0x7c, 0x00, 0x5e, 0xfb, 0x35, 0x69,
+    0x00, 0x5f, 0x0a, 0x36, 0xbc, 0x00, 0x5f, 0x2d,
+    0x34, 0xe1, 0x00, 0x5f, 0x31, 0x35, 0xf3, 0x00,
+    0x5f, 0x38, 0x4e, 0x94, 0x00, 0x5f, 0x45, 0x37,
+    0xce, 0x00, 0x5f, 0x50, 0x3c, 0x1f, 0x00, 0x5f,
+    0x62, 0x4e, 0x5f, 0x00, 0x5f, 0x69, 0x35, 0xd7,
+    0x00, 0x5f, 0x6b, 0x36, 0x68, 0x00, 0x5f, 0x80,
+    0x35, 0x5d, 0x00, 0x5f, 0x98, 0x34, 0xe2, 0x00,
+    0x5f, 0xa1, 0x4e, 0x95, 0x00, 0x5f, 0xae, 0x36,
+    0xa8, 0x00, 0x5f, 0xb5, 0x36, 0x69, 0x00, 0x5f,
+    0xbd, 0x1d, 0xea, 0x00, 0x5f, 0xcd, 0x36, 0x91,
+    0x00, 0x5f, 0xd8, 0x36, 0xd1, 0x00, 0x5f, 0xd9,
+    0x36, 0xd2, 0x00, 0x5f, 0xdd, 0x4e, 0x96, 0x00,
+    0x60, 0x25, 0x35, 0x90, 0x00, 0x60, 0x50, 0x35,
+    0x99, 0x00, 0x60, 0x62, 0x1d, 0xe0, 0x00, 0x60,
+    0x65, 0x34, 0xa4, 0x00, 0x60, 0x75, 0x35, 0xac,
+    0x00, 0x60, 0x94, 0x05, 0x79, 0x00, 0x60, 0x97,
+    0x1e, 0x94, 0x00, 0x60, 0x9e, 0x54, 0x36, 0x00,
+    0x60, 0xa4, 0x3c, 0x20, 0x00, 0x60, 0xb2, 0x34,
+    0xb3, 0x00, 0x60, 0xc5, 0x36, 0x12, 0x00, 0x60,
+    0xd8, 0x34, 0xe3, 0x00, 0x61, 0x08, 0x1e, 0x7a,
+    0x00, 0x61, 0x09, 0x36, 0xf3, 0x00, 0x61, 0x0f,
+    0x35, 0x47, 0x00, 0x61, 0x3d, 0x34, 0xe4, 0x00,
+    0x61, 0x48, 0x4e, 0x60, 0x00, 0x61, 0x4c, 0x35,
+    0xc4, 0x00, 0x61, 0x4e, 0x34, 0x2c, 0x00, 0x61,
+    0x62, 0x4e, 0x97, 0x00, 0x61, 0x67, 0x1d, 0xf5,
+    0x00, 0x61, 0x68, 0x34, 0x10, 0x00, 0x61, 0x8e,
+    0x0b, 0x00, 0x00, 0x61, 0x90, 0x37, 0x10, 0x00,
+    0x61, 0xa4, 0x34, 0xbd, 0x00, 0x61, 0xb2, 0x35,
+    0xb6, 0x00, 0x61, 0xf2, 0x34, 0x39, 0x00, 0x61,
+    0xf8, 0x4e, 0x99, 0x00, 0x61, 0xfe, 0x34, 0xe5,
+    0x00, 0x62, 0x10, 0x36, 0x2b, 0x00, 0x62, 0x3b,
+    0x36, 0xeb, 0x00, 0x62, 0x3f, 0x36, 0xd3, 0x00,
+    0x62, 0x40, 0x36, 0x02, 0x00, 0x62, 0x41, 0x1f,
+    0x37, 0x00, 0x62, 0x47, 0x36, 0x3b, 0x00, 0x62,
+    0x48, 0x1e, 0xc2, 0x00, 0x62, 0x49, 0x1e, 0x63,
+    0x00, 0x62, 0x68, 0x34, 0xe6, 0x00, 0x62, 0x71,
+    0x35, 0x45, 0x00, 0x62, 0xb1, 0x36, 0xc5, 0x00,
+    0x62, 0xcc, 0x37, 0x32, 0x00, 0x62, 0xcf, 0x34,
+    0xe7, 0x00, 0x62, 0xd0, 0x1d, 0xe1, 0x00, 0x62,
+    0xd2, 0x35, 0x93, 0x00, 0x62, 0xd4, 0x13, 0x5d,
+    0x00, 0x62, 0xf3, 0x1f, 0x21, 0x00, 0x62, 0xf7,
+    0x34, 0x88, 0x00, 0x63, 0x3a, 0x4f, 0x3e, 0x00,
+    0x63, 0x3d, 0x1e, 0x62, 0x00, 0x63, 0x4c, 0x34,
+    0x5d, 0x00, 0x63, 0x57, 0x1e, 0x3f, 0x00, 0x63,
+    0x67, 0x34, 0xc3, 0x00, 0x63, 0x68, 0x35, 0xec,
+    0x00, 0x63, 0x69, 0x1e, 0x95, 0x00, 0x63, 0x6e,
+    0x34, 0x9d, 0x00, 0x63, 0x72, 0x1d, 0xfc, 0x00,
+    0x63, 0x83, 0x36, 0x43, 0x00, 0x63, 0x88, 0x35,
+    0xf6, 0x00, 0x63, 0x92, 0x34, 0xaf, 0x00, 0x63,
+    0xa1, 0x35, 0xd8, 0x00, 0x63, 0xa7, 0x35, 0xc6,
+    0x00, 0x63, 0xc3, 0x1f, 0x27, 0x00, 0x63, 0xc6,
+    0x37, 0x34, 0x00, 0x63, 0xf4, 0x35, 0x57, 0x00,
+    0x64, 0x06, 0x1e, 0x96, 0x00, 0x64, 0x0f, 0x34,
+    0xe9, 0x00, 0x64, 0x1c, 0x37, 0x33, 0x00, 0x64,
+    0x28, 0x37, 0x35, 0x00, 0x64, 0x42, 0x34, 0x9e,
+    0x00, 0x64, 0x69, 0x36, 0xd7, 0x00, 0x64, 0x6f,
+    0x4f, 0x28, 0x00, 0x64, 0x7a, 0x1e, 0x21, 0x00,
+    0x64, 0xb0, 0x1e, 0x24, 0x00, 0x64, 0xe2, 0x1e,
+    0x45, 0x00, 0x64, 0xf2, 0x34, 0xea, 0x00, 0x64,
+    0xf6, 0x4e, 0x9e, 0x00, 0x65, 0x1d, 0x34, 0xe8,
+    0x00, 0x65, 0x4f, 0x0d, 0xc4, 0x00, 0x65, 0x5d,
+    0x34, 0xeb, 0x00, 0x65, 0x5e, 0x4e, 0xa1, 0x00,
+    0x65, 0x62, 0x34, 0x71, 0x00, 0x65, 0x77, 0x36,
+    0xb3, 0x00, 0x65, 0x83, 0x1e, 0x98, 0x00, 0x65,
+    0x87, 0x4e, 0xa3, 0x00, 0x65, 0x89, 0x4e, 0xa4,
+    0x00, 0x65, 0x8e, 0x4e, 0xa6, 0x00, 0x65, 0x90,
+    0x34, 0xb5, 0x00, 0x65, 0x9c, 0x35, 0xed, 0x00,
+    0x65, 0xa7, 0x4f, 0x41, 0x00, 0x65, 0xbc, 0x35,
+    0x5c, 0x00, 0x65, 0xc5, 0x37, 0x08, 0x00, 0x65,
+    0xdf, 0x54, 0xbe, 0x00, 0x65, 0xe1, 0x4e, 0xa9,
+    0x00, 0x65, 0xe2, 0x06, 0x37, 0x00, 0x66, 0x0e,
+    0x36, 0xe4, 0x00, 0x66, 0x1e, 0x21, 0x1c, 0x00,
+    0x66, 0x5f, 0x34, 0xec, 0x00, 0x66, 0x66, 0x1d,
+    0xe2, 0x00, 0x66, 0x67, 0x4e, 0xaa, 0x00, 0x66,
+    0x69, 0x36, 0xa5, 0x00, 0x66, 0x6e, 0x4e, 0xab,
+    0x00, 0x66, 0x74, 0x0a, 0x56, 0x00, 0x66, 0x77,
+    0x39, 0x11, 0x00, 0x66, 0x81, 0x35, 0xa0, 0x00,
+    0x66, 0x91, 0x34, 0x28, 0x00, 0x66, 0x96, 0x36,
+    0x5e, 0x00, 0x66, 0x97, 0x35, 0x46, 0x00, 0x66,
+    0xb5, 0x54, 0xda, 0x00, 0x66, 0xc1, 0x1f, 0x38,
+    0x00, 0x66, 0xd9, 0x1e, 0x13, 0x00, 0x66, 0xdc,
+    0x36, 0xfd, 0x00, 0x66, 0xf4, 0x34, 0x81, 0x00,
+    0x66, 0xf5, 0x37, 0x3b, 0x00, 0x66, 0xf8, 0x36,
+    0x03, 0x00, 0x66, 0xfb, 0x37, 0xcd, 0x00, 0x66,
+    0xfc, 0x37, 0x20, 0x00, 0x67, 0x00, 0x4e, 0xaf,
+    0x00, 0x67, 0x08, 0x35, 0xb2, 0x00, 0x67, 0x09,
+    0x36, 0xf7, 0x00, 0x67, 0x0b, 0x36, 0xc6, 0x00,
+    0x67, 0x0d, 0x36, 0xb7, 0x00, 0x67, 0x15, 0x36,
+    0x6f, 0x00, 0x67, 0x17, 0x0f, 0xd5, 0x00, 0x67,
+    0x1b, 0x36, 0xd4, 0x00, 0x67, 0x1d, 0x36, 0x6b,
+    0x00, 0x67, 0x1f, 0x35, 0x86, 0x00, 0x67, 0x53,
+    0x1e, 0x0f, 0x00, 0x67, 0x56, 0x4f, 0x3a, 0x00,
+    0x67, 0x5e, 0x37, 0x3c, 0x00, 0x67, 0x61, 0x4e,
+    0xb0, 0x00, 0x67, 0x7e, 0x34, 0x95, 0x00, 0x67,
+    0xa6, 0x1e, 0x99, 0x00, 0x67, 0xa9, 0x34, 0xed,
+    0x00, 0x67, 0xc4, 0x4e, 0xb1, 0x00, 0x67, 0xca,
+    0x1e, 0x65, 0x00, 0x67, 0xd4, 0x34, 0x91, 0x00,
+    0x67, 0xe7, 0x34, 0xee, 0x00, 0x67, 0xf1, 0x36,
+    0x65, 0x00, 0x68, 0x01, 0x21, 0x2e, 0x00, 0x68,
+    0x02, 0x4e, 0xb2, 0x00, 0x68, 0x13, 0x1e, 0x25,
+    0x00, 0x68, 0x1f, 0x4e, 0x61, 0x00, 0x68, 0x21,
+    0x34, 0x82, 0x00, 0x68, 0x43, 0x34, 0xac, 0x00,
+    0x68, 0x52, 0x21, 0x2c, 0x00, 0x68, 0x5d, 0x34,
+    0xc5, 0x00, 0x68, 0x7a, 0x36, 0xf0, 0x00, 0x68,
+    0x81, 0x37, 0x09, 0x00, 0x68, 0x85, 0x0d, 0x15,
+    0x00, 0x68, 0x8d, 0x37, 0x3e, 0x00, 0x68, 0x97,
+    0x4f, 0x37, 0x00, 0x68, 0x9b, 0x1e, 0x9b, 0x00,
+    0x68, 0x9d, 0x37, 0x3d, 0x00, 0x68, 0xa2, 0x1e,
+    0x19, 0x00, 0x68, 0xc8, 0x37, 0xcc, 0x00, 0x68,
+    0xda, 0x1e, 0x38, 0x00, 0x69, 0x0d, 0x34, 0x99,
+    0x00, 0x69, 0x30, 0x34, 0xf0, 0x00, 0x69, 0x3d,
+    0x4e, 0xb3, 0x00, 0x69, 0x5e, 0x4e, 0xb4, 0x00,
+    0x69, 0x62, 0x1e, 0x58, 0x00, 0x69, 0x6b, 0x34,
+    0xef, 0x00, 0x69, 0x6f, 0x34, 0x94, 0x00, 0x69,
+    0x82, 0x34, 0x5b, 0x00, 0x69, 0x8a, 0x1e, 0x06,
+    0x00, 0x69, 0x94, 0x1e, 0x84, 0x00, 0x69, 0xa7,
+    0x34, 0xf1, 0x00, 0x69, 0xbb, 0x37, 0x43, 0x00,
+    0x69, 0xc1, 0x35, 0x9a, 0x00, 0x69, 0xcb, 0x35,
+    0xc7, 0x00, 0x69, 0xcc, 0x1e, 0x40, 0x00, 0x69,
+    0xd9, 0x36, 0xdd, 0x00, 0x69, 0xea, 0x35, 0x6f,
+    0x00, 0x69, 0xfe, 0x55, 0x1f, 0x00, 0x6a, 0x0b,
+    0x1e, 0x64, 0x00, 0x6a, 0x3d, 0x1e, 0x3a, 0x00,
+    0x6a, 0x44, 0x34, 0xf2, 0x00, 0x6a, 0x55, 0x55,
+    0x25, 0x00, 0x6a, 0x5f, 0x35, 0x87, 0x00, 0x6a,
+    0x73, 0x37, 0xd4, 0x00, 0x6a, 0x8e, 0x34, 0x7e,
+    0x00, 0x6a, 0x90, 0x34, 0xf3, 0x00, 0x6a, 0x9c,
+    0x4e, 0xb6, 0x00, 0x6a, 0xdb, 0x06, 0xf3, 0x00,
+    0x6b, 0x04, 0x0f, 0x5d, 0x00, 0x6b, 0x1d, 0x1d,
+    0xd7, 0x00, 0x6b, 0x21, 0x35, 0xe7, 0x00, 0x6b,
+    0x24, 0x3c, 0x22, 0x00, 0x6b, 0x4e, 0x36, 0x5b,
+    0x00, 0x6b, 0x96, 0x36, 0x16, 0x00, 0x6b, 0xba,
+    0x08, 0x74, 0x00, 0x6b, 0xbb, 0x34, 0x70, 0x00,
+    0x6c, 0x08, 0x1f, 0x39, 0x00, 0x6c, 0x13, 0x34,
+    0xf5, 0x00, 0x6c, 0x38, 0x4e, 0xba, 0x00, 0x6c,
+    0x3a, 0x39, 0x62, 0x00, 0x6c, 0x72, 0x1f, 0x1e,
+    0x00, 0x6c, 0xaa, 0x37, 0x48, 0x00, 0x6c, 0xbf,
+    0x05, 0x0a, 0x00, 0x6c, 0xe1, 0x1e, 0x71, 0x00,
+    0x6c, 0xe8, 0x36, 0x66, 0x00, 0x6d, 0x3e, 0x34,
+    0xae, 0x00, 0x6d, 0x69, 0x35, 0xc8, 0x00, 0x6d,
+    0x6e, 0x36, 0xb4, 0x00, 0x6d, 0x77, 0x05, 0x82,
+    0x00, 0x6d, 0x78, 0x36, 0x1d, 0x00, 0x6d, 0x88,
+    0x36, 0x0c, 0x00, 0x6d, 0xe4, 0x4e, 0xbd, 0x00,
+    0x6d, 0xeb, 0x1d, 0xd5, 0x00, 0x6d, 0xfb, 0x36,
+    0x7c, 0x00, 0x6e, 0x08, 0x4e, 0xbf, 0x00, 0x6e,
+    0x1a, 0x09, 0x77, 0x00, 0x6e, 0x23, 0x1f, 0x3a,
+    0x00, 0x6e, 0x2f, 0x35, 0xc9, 0x00, 0x6e, 0x6e,
+    0x1e, 0x9d, 0x00, 0x6e, 0x72, 0x4e, 0xc0, 0x00,
+    0x6e, 0x7e, 0x34, 0xcf, 0x00, 0x6e, 0x9d, 0x1e,
+    0x01, 0x00, 0x6e, 0xa2, 0x1d, 0xd3, 0x00, 0x6e,
+    0xba, 0x1e, 0x46, 0x00, 0x6e, 0xcb, 0x35, 0xe9,
+    0x00, 0x6e, 0xd5, 0x4e, 0xc2, 0x00, 0x6e, 0xdb,
+    0x4e, 0xc3, 0x00, 0x6e, 0xec, 0x1f, 0x3b, 0x00,
+    0x6e, 0xfe, 0x34, 0xf8, 0x00, 0x6f, 0x11, 0x34,
+    0xf7, 0x00, 0x6f, 0x22, 0x34, 0x14, 0x00, 0x6f,
+    0x23, 0x0f, 0xc2, 0x00, 0x6f, 0x3e, 0x34, 0xf9,
+    0x00, 0x6f, 0x51, 0x36, 0x9e, 0x00, 0x6f, 0x54,
+    0x35, 0xb0, 0x00, 0x6f, 0x5b, 0x4e, 0xc4, 0x00,
+    0x6f, 0x64, 0x4e, 0xc6, 0x00, 0x6f, 0x6e, 0x0b,
+    0xc8, 0x00, 0x6f, 0x74, 0x4e, 0xc7, 0x00, 0x6f,
+    0x98, 0x37, 0x47, 0x00, 0x6f, 0xef, 0x1e, 0x33,
+    0x00, 0x6f, 0xf9, 0x39, 0x95, 0x00, 0x70, 0x15,
+    0x1e, 0x6b, 0x00, 0x70, 0x1b, 0x37, 0x4a, 0x00,
+    0x70, 0x1e, 0x1e, 0x51, 0x00, 0x70, 0x26, 0x1e,
+    0x3d, 0x00, 0x70, 0x27, 0x36, 0x57, 0x00, 0x70,
+    0x4a, 0x39, 0x98, 0x00, 0x70, 0x58, 0x1e, 0x57,
+    0x00, 0x70, 0x70, 0x35, 0x6a, 0x00, 0x70, 0x78,
+    0x4f, 0x2e, 0x00, 0x70, 0x7c, 0x1e, 0x10, 0x00,
+    0x70, 0xad, 0x36, 0x5c, 0x00, 0x71, 0x49, 0x0f,
+    0xc3, 0x00, 0x71, 0x4e, 0x1e, 0x26, 0x00, 0x71,
+    0x52, 0x55, 0xad, 0x00, 0x71, 0x59, 0x35, 0x59,
+    0x00, 0x71, 0x62, 0x37, 0x4b, 0x00, 0x71, 0x6e,
+    0x08, 0xfd, 0x00, 0x71, 0x7d, 0x1e, 0x27, 0x00,
+    0x71, 0x94, 0x1e, 0x7d, 0x00, 0x71, 0xb3, 0x39,
+    0xae, 0x00, 0x71, 0xd0, 0x37, 0x0a, 0x00, 0x71,
+    0xff, 0x34, 0xfa, 0x00, 0x72, 0x28, 0x15, 0xdf,
+    0x00, 0x72, 0x2b, 0x3c, 0x26, 0x00, 0x72, 0x35,
+    0x09, 0x0b, 0x00, 0x72, 0x36, 0x34, 0xb9, 0x00,
+    0x72, 0x3a, 0x4f, 0x46, 0x00, 0x72, 0x3b, 0x37,
+    0x4c, 0x00, 0x72, 0x3e, 0x4e, 0xc9, 0x00, 0x72,
+    0x4c, 0x1e, 0x5b, 0x00, 0x72, 0x59, 0x1f, 0x1d,
+    0x00, 0x72, 0xe1, 0x4f, 0x36, 0x00, 0x73, 0x1c,
+    0x37, 0x4e, 0x00, 0x73, 0x2a, 0x0b, 0xb4, 0x00,
+    0x73, 0x36, 0x36, 0xf8, 0x00, 0x73, 0x37, 0x1e,
+    0x7c, 0x00, 0x73, 0x87, 0x37, 0x05, 0x00, 0x73,
+    0x8b, 0x35, 0xa1, 0x00, 0x73, 0xca, 0x1e, 0x0b,
+    0x00, 0x73, 0xce, 0x1e, 0xa0, 0x00, 0x73, 0xe5,
+    0x34, 0xfb, 0x00, 0x73, 0xed, 0x34, 0xb1, 0x00,
+    0x74, 0x22, 0x0b, 0x56, 0x00, 0x74, 0x32, 0x34,
+    0xfc, 0x00, 0x74, 0x5f, 0x34, 0xfd, 0x00, 0x74,
+    0x62, 0x21, 0x71, 0x00, 0x74, 0xb0, 0x35, 0x79,
+    0x00, 0x74, 0xbd, 0x4e, 0xcd, 0x00, 0x74, 0xca,
+    0x37, 0x4f, 0x00, 0x74, 0xd8, 0x55, 0xf6, 0x00,
+    0x74, 0xdc, 0x35, 0x50, 0x00, 0x74, 0xe0, 0x34,
+    0xfe, 0x00, 0x74, 0xef, 0x55, 0xfa, 0x00, 0x75,
+    0x04, 0x1e, 0xa1, 0x00, 0x75, 0x0c, 0x34, 0xff,
+    0x00, 0x75, 0x0d, 0x1e, 0xa2, 0x00, 0x75, 0x11,
+    0x1e, 0x04, 0x00, 0x75, 0x15, 0x1e, 0xa3, 0x00,
+    0x75, 0x26, 0x4f, 0x3b, 0x00, 0x75, 0x54, 0x36,
+    0xa4, 0x00, 0x75, 0x5d, 0x4e, 0xce, 0x00, 0x75,
+    0xbc, 0x4e, 0xcf, 0x00, 0x75, 0xc5, 0x36, 0xb1,
+    0x00, 0x76, 0x08, 0x4e, 0xd1, 0x00, 0x76, 0x26,
+    0x1e, 0x2d, 0x00, 0x76, 0x52, 0x1e, 0x7b, 0x00,
+    0x76, 0x64, 0x4e, 0xd2, 0x00, 0x76, 0x69, 0x4e,
+    0xd3, 0x00, 0x76, 0x72, 0x35, 0x00, 0x00, 0x76,
+    0x84, 0x36, 0x79, 0x00, 0x76, 0x93, 0x1e, 0xa4,
+    0x00, 0x76, 0xc6, 0x34, 0xc4, 0x00, 0x76, 0xca,
+    0x21, 0x7b, 0x00, 0x76, 0xd4, 0x56, 0x1d, 0x00,
+    0x76, 0xdb, 0x36, 0x2c, 0x00, 0x76, 0xdf, 0x36,
+    0xe5, 0x00, 0x76, 0xf2, 0x36, 0xe9, 0x00, 0x76,
+    0xf4, 0x36, 0x6e, 0x00, 0x77, 0x1e, 0x16, 0xb8,
+    0x00, 0x77, 0x1f, 0x36, 0x1e, 0x00, 0x77, 0x37,
+    0x4e, 0xd5, 0x00, 0x77, 0x3a, 0x34, 0xa6, 0x00,
+    0x77, 0x7e, 0x4e, 0xd6, 0x00, 0x77, 0x8d, 0x56,
+    0x2e, 0x00, 0x77, 0xa2, 0x56, 0x2f, 0x00, 0x77,
+    0xa5, 0x1e, 0x6e, 0x00, 0x77, 0xac, 0x34, 0x92,
+    0x00, 0x77, 0xe9, 0x35, 0xa4, 0x00, 0x78, 0x32,
+    0x36, 0xc7, 0x00, 0x78, 0x3a, 0x36, 0x7f, 0x00,
+    0x78, 0x5d, 0x36, 0x0d, 0x00, 0x78, 0x6c, 0x34,
+    0x83, 0x00, 0x78, 0x7c, 0x1e, 0xa5, 0x00, 0x78,
+    0x91, 0x0d, 0x7e, 0x00, 0x78, 0xd4, 0x35, 0x02,
+    0x00, 0x78, 0xe8, 0x36, 0xda, 0x00, 0x78, 0xef,
+    0x35, 0x4b, 0x00, 0x79, 0x2a, 0x35, 0x01, 0x00,
+    0x79, 0x34, 0x3a, 0x38, 0x00, 0x79, 0x3a, 0x08,
+    0xd4, 0x00, 0x79, 0x3c, 0x21, 0x83, 0x00, 0x79,
+    0x3e, 0x34, 0x24, 0x00, 0x79, 0x40, 0x37, 0x57,
+    0x00, 0x79, 0x41, 0x1d, 0xf4, 0x00, 0x79, 0x47,
+    0x1d, 0xeb, 0x00, 0x79, 0x48, 0x06, 0x41, 0x00,
+    0x79, 0x49, 0x34, 0x21, 0x00, 0x79, 0x50, 0x0f,
+    0x1e, 0x00, 0x79, 0x53, 0x37, 0x58, 0x00, 0x79,
+    0x56, 0x34, 0x2f, 0x00, 0x79, 0x5d, 0x09, 0x55,
+    0x00, 0x79, 0x5e, 0x0a, 0x06, 0x00, 0x79, 0x62,
+    0x1f, 0x29, 0x00, 0x79, 0x65, 0x09, 0xb5, 0x00,
+    0x79, 0x8d, 0x05, 0x52, 0x00, 0x79, 0x8e, 0x34,
+    0x3b, 0x00, 0x79, 0x8f, 0x21, 0x87, 0x00, 0x79,
+    0xa7, 0x4e, 0xd7, 0x00, 0x79, 0xae, 0x37, 0x5b,
+    0x00, 0x79, 0xb0, 0x1e, 0x59, 0x00, 0x79, 0xb1,
+    0x4e, 0xd8, 0x00, 0x79, 0xba, 0x35, 0x03, 0x00,
+    0x79, 0xe4, 0x1e, 0x5d, 0x00, 0x7a, 0x0b, 0x36,
+    0x78, 0x00, 0x7a, 0x17, 0x1e, 0x66, 0x00, 0x7a,
+    0x19, 0x35, 0x04, 0x00, 0x7a, 0x31, 0x1e, 0xa6,
+    0x00, 0x7a, 0x40, 0x08, 0x04, 0x00, 0x7a, 0x60,
+    0x3a, 0x4e, 0x00, 0x7a, 0x74, 0x34, 0x7a, 0x00,
+    0x7a, 0x7a, 0x35, 0xa7, 0x00, 0x7a, 0x7f, 0x1f,
+    0x25, 0x00, 0x7a, 0x81, 0x34, 0x3d, 0x00, 0x7a,
+    0x95, 0x35, 0x05, 0x00, 0x7a, 0x97, 0x1f, 0x3c,
+    0x00, 0x7a, 0xae, 0x34, 0x77, 0x00, 0x7a, 0xbe,
+    0x4e, 0xd9, 0x00, 0x7a, 0xc6, 0x3c, 0x27, 0x00,
+    0x7a, 0xc8, 0x4f, 0x3d, 0x00, 0x7b, 0x08, 0x1f,
+    0x1f, 0x00, 0x7b, 0x51, 0x36, 0x63, 0x00, 0x7b,
+    0x75, 0x4f, 0x2a, 0x00, 0x7b, 0x99, 0x1e, 0xa8,
+    0x00, 0x7b, 0xad, 0x1f, 0x26, 0x00, 0x7b, 0xb8,
+    0x1e, 0x5f, 0x00, 0x7b, 0xc0, 0x34, 0x2e, 0x00,
+    0x7b, 0xc7, 0x1f, 0x2b, 0x00, 0x7b, 0xc9, 0x36,
+    0x61, 0x00, 0x7b, 0xdd, 0x1f, 0x3d, 0x00, 0x7b,
+    0xe0, 0x4e, 0xda, 0x00, 0x7c, 0x14, 0x37, 0x5f,
+    0x00, 0x7c, 0x3e, 0x1f, 0x2d, 0x00, 0x7c, 0x3f,
+    0x36, 0xc2, 0x00, 0x7c, 0x4d, 0x36, 0x36, 0x00,
+    0x7c, 0x50, 0x37, 0x61, 0x00, 0x7c, 0x58, 0x37,
+    0x62, 0x00, 0x7c, 0x69, 0x56, 0xaa, 0x00, 0x7c,
+    0x7e, 0x1e, 0x78, 0x00, 0x7c, 0x82, 0x4f, 0x30,
+    0x00, 0x7c, 0x89, 0x34, 0xbe, 0x00, 0x7c, 0x90,
+    0x1e, 0xa9, 0x00, 0x7c, 0xae, 0x1e, 0xaa, 0x00,
+    0x7c, 0xbe, 0x0a, 0x5e, 0x00, 0x7c, 0xd6, 0x0c,
+    0x76, 0x00, 0x7c, 0xf2, 0x35, 0x06, 0x00, 0x7d,
+    0x04, 0x36, 0xee, 0x00, 0x7d, 0x09, 0x4e, 0xdc,
+    0x00, 0x7d, 0x0b, 0x36, 0xec, 0x00, 0x7d, 0x0d,
+    0x36, 0x94, 0x00, 0x7d, 0x1a, 0x35, 0x91, 0x00,
+    0x7d, 0x1b, 0x34, 0xbf, 0x00, 0x7d, 0x42, 0x35,
+    0xf8, 0x00, 0x7d, 0x46, 0x37, 0x63, 0x00, 0x7d,
+    0x5c, 0x21, 0x90, 0x00, 0x7d, 0x5e, 0x34, 0x84,
+    0x00, 0x7d, 0x63, 0x37, 0x64, 0x00, 0x7d, 0x73,
+    0x35, 0x07, 0x00, 0x7d, 0x9b, 0x1e, 0xab, 0x00,
+    0x7d, 0x9f, 0x1e, 0xad, 0x00, 0x7d, 0xae, 0x1e,
+    0xac, 0x00, 0x7d, 0xb2, 0x4e, 0xdd, 0x00, 0x7d,
+    0xcb, 0x34, 0xb6, 0x00, 0x7d, 0xcf, 0x34, 0xa0,
+    0x00, 0x7d, 0xdd, 0x35, 0x08, 0x00, 0x7d, 0xe8,
+    0x36, 0xbf, 0x00, 0x7d, 0xe9, 0x35, 0x7a, 0x00,
+    0x7d, 0xef, 0x34, 0x62, 0x00, 0x7d, 0xf4, 0x0f,
+    0xc5, 0x00, 0x7e, 0x09, 0x47, 0xbe, 0x00, 0x7e,
+    0x1b, 0x36, 0x9b, 0x00, 0x7e, 0x22, 0x37, 0x65,
+    0x00, 0x7e, 0x2b, 0x36, 0xc8, 0x00, 0x7e, 0x35,
+    0x35, 0x09, 0x00, 0x7e, 0x41, 0x34, 0x40, 0x00,
+    0x7e, 0x43, 0x37, 0x69, 0x00, 0x7e, 0x6d, 0x36,
+    0xe1, 0x00, 0x7e, 0x8c, 0x37, 0x6a, 0x00, 0x7f,
+    0x3e, 0x4e, 0xdf, 0x00, 0x7f, 0x50, 0x37, 0x6b,
+    0x00, 0x7f, 0x61, 0x3c, 0x28, 0x00, 0x7f, 0x6a,
+    0x34, 0x89, 0x00, 0x7f, 0x6e, 0x36, 0x60, 0x00,
+    0x7f, 0x72, 0x09, 0x7a, 0x00, 0x7f, 0x80, 0x56,
+    0xda, 0x00, 0x7f, 0x8a, 0x0f, 0x3d, 0x00, 0x7f,
+    0xa1, 0x36, 0x3d, 0x00, 0x7f, 0xae, 0x35, 0x0a,
+    0x00, 0x7f, 0xbd, 0x04, 0xcb, 0x00, 0x7f, 0xc1,
+    0x34, 0x6a, 0x00, 0x7f, 0xc5, 0x37, 0x6f, 0x00,
+    0x7f, 0xc6, 0x37, 0x70, 0x00, 0x7f, 0xcc, 0x37,
+    0x01, 0x00, 0x7f, 0xd2, 0x35, 0xf9, 0x00, 0x7f,
+    0xd4, 0x1e, 0xae, 0x00, 0x7f, 0xe0, 0x1e, 0x20,
+    0x00, 0x7f, 0xe1, 0x35, 0x0b, 0x00, 0x7f, 0xe9,
+    0x1f, 0x3e, 0x00, 0x7f, 0xeb, 0x1d, 0xe9, 0x00,
+    0x7f, 0xf0, 0x1d, 0xe8, 0x00, 0x7f, 0xfb, 0x36,
+    0xd8, 0x00, 0x7f, 0xfc, 0x34, 0xc8, 0x00, 0x80,
+    0x00, 0x1e, 0x7e, 0x00, 0x80, 0x03, 0x34, 0x85,
+    0x00, 0x80, 0x05, 0x34, 0x25, 0x00, 0x80, 0x12,
+    0x4e, 0xe1, 0x00, 0x80, 0x15, 0x35, 0xca, 0x00,
+    0x80, 0x17, 0x36, 0xea, 0x00, 0x80, 0x36, 0x34,
+    0xc7, 0x00, 0x80, 0x56, 0x36, 0x2d, 0x00, 0x80,
+    0x5a, 0x35, 0x0c, 0x00, 0x80, 0x5f, 0x35, 0x0d,
+    0x00, 0x80, 0x61, 0x34, 0xa1, 0x00, 0x80, 0x6f,
+    0x34, 0xcd, 0x00, 0x80, 0x70, 0x35, 0x0f, 0x00,
+    0x80, 0x71, 0x3c, 0x29, 0x00, 0x80, 0x73, 0x35,
+    0x0e, 0x00, 0x80, 0x74, 0x34, 0xa7, 0x00, 0x80,
+    0x76, 0x35, 0x10, 0x00, 0x80, 0x77, 0x34, 0x9a,
+    0x00, 0x80, 0x7e, 0x34, 0xce, 0x00, 0x80, 0x87,
+    0x36, 0x9c, 0x00, 0x80, 0x89, 0x36, 0x8f, 0x00,
+    0x80, 0x96, 0x36, 0x0e, 0x00, 0x80, 0x9e, 0x3c,
+    0x2a, 0x00, 0x80, 0xa9, 0x35, 0xb8, 0x00, 0x80,
+    0xba, 0x36, 0x97, 0x00, 0x80, 0xd6, 0x4e, 0xe3,
+    0x00, 0x80, 0xde, 0x36, 0xc9, 0x00, 0x81, 0x06,
+    0x36, 0x34, 0x00, 0x81, 0x08, 0x34, 0xc6, 0x00,
+    0x81, 0x09, 0x4e, 0xe4, 0x00, 0x81, 0x29, 0x4e,
+    0xe5, 0x00, 0x81, 0x53, 0x35, 0x11, 0x00, 0x81,
+    0x54, 0x35, 0xcb, 0x00, 0x81, 0x70, 0x35, 0xd1,
+    0x00, 0x81, 0x71, 0x4f, 0x33, 0x00, 0x81, 0x7f,
+    0x1e, 0x30, 0x00, 0x81, 0x8a, 0x35, 0x12, 0x00,
+    0x81, 0xb5, 0x35, 0x13, 0x00, 0x81, 0xcd, 0x35,
+    0x14, 0x00, 0x81, 0xed, 0x34, 0x26, 0x00, 0x82,
+    0x00, 0x57, 0x0f, 0x00, 0x82, 0x0c, 0x4e, 0xe6,
+    0x00, 0x82, 0x18, 0x35, 0x7f, 0x00, 0x82, 0x1b,
+    0x4e, 0xe7, 0x00, 0x82, 0x1c, 0x34, 0x93, 0x00,
+    0x82, 0x1f, 0x35, 0xb3, 0x00, 0x82, 0x2e, 0x1e,
+    0xaf, 0x00, 0x82, 0x39, 0x34, 0x9f, 0x00, 0x82,
+    0x40, 0x4e, 0xe8, 0x00, 0x82, 0x47, 0x34, 0xab,
+    0x00, 0x82, 0x58, 0x37, 0x74, 0x00, 0x82, 0x79,
+    0x37, 0x77, 0x00, 0x82, 0x8d, 0x1e, 0xb0, 0x00,
+    0x82, 0x92, 0x4f, 0x44, 0x00, 0x82, 0xa6, 0x1f,
+    0x19, 0x00, 0x82, 0xb1, 0x35, 0x62, 0x00, 0x82,
+    0xbd, 0x05, 0x6a, 0x00, 0x82, 0xc5, 0x35, 0x77,
+    0x00, 0x82, 0xd2, 0x1e, 0xb1, 0x00, 0x82, 0xe3,
+    0x37, 0x78, 0x00, 0x83, 0x23, 0x1e, 0xb2, 0x00,
+    0x83, 0x28, 0x1f, 0x1a, 0x00, 0x83, 0x52, 0x35,
+    0xcc, 0x00, 0x83, 0x75, 0x1e, 0xb3, 0x00, 0x83,
+    0xbd, 0x37, 0x7c, 0x00, 0x83, 0xd3, 0x35, 0x63,
+    0x00, 0x83, 0xd4, 0x4e, 0xea, 0x00, 0x83, 0xdc,
+    0x35, 0xda, 0x00, 0x83, 0xdf, 0x1e, 0x4b, 0x00,
+    0x83, 0xf2, 0x35, 0x15, 0x00, 0x84, 0x0c, 0x36,
+    0xca, 0x00, 0x84, 0x0f, 0x4e, 0xeb, 0x00, 0x84,
+    0x20, 0x37, 0x7b, 0x00, 0x84, 0x22, 0x1f, 0x3f,
+    0x00, 0x84, 0x57, 0x34, 0x37, 0x00, 0x84, 0x5b,
+    0x1d, 0xe4, 0x00, 0x84, 0x5c, 0x57, 0x45, 0x00,
+    0x84, 0x7a, 0x34, 0xba, 0x00, 0x84, 0xea, 0x4e,
+    0xed, 0x00, 0x84, 0xec, 0x1e, 0x72, 0x00, 0x84,
+    0xee, 0x0f, 0xc7, 0x00, 0x84, 0xf4, 0x37, 0x7d,
+    0x00, 0x85, 0x11, 0x36, 0xbd, 0x00, 0x85, 0x17,
+    0x1e, 0xb4, 0x00, 0x85, 0x3d, 0x1e, 0x6d, 0x00,
+    0x85, 0x43, 0x36, 0xa6, 0x00, 0x85, 0x51, 0x4e,
+    0xef, 0x00, 0x85, 0x55, 0x35, 0x16, 0x00, 0x85,
+    0x5d, 0x57, 0x64, 0x00, 0x85, 0x63, 0x4e, 0xf0,
+    0x00, 0x85, 0x84, 0x36, 0x99, 0x00, 0x85, 0x87,
+    0x37, 0x7f, 0x00, 0x85, 0xa9, 0x1e, 0x08, 0x00,
+    0x85, 0xaf, 0x1e, 0x15, 0x00, 0x85, 0xcf, 0x4e,
+    0xf1, 0x00, 0x85, 0xd5, 0x35, 0x17, 0x00, 0x85,
+    0xe4, 0x36, 0x85, 0x00, 0x85, 0xf7, 0x1e, 0x16,
+    0x00, 0x86, 0x12, 0x21, 0xa4, 0x00, 0x86, 0x2d,
+    0x37, 0x04, 0x00, 0x86, 0x4e, 0x4e, 0xf2, 0x00,
+    0x86, 0x50, 0x35, 0x8c, 0x00, 0x86, 0x54, 0x4f,
+    0x32, 0x00, 0x86, 0x5c, 0x0f, 0x82, 0x00, 0x86,
+    0x5e, 0x35, 0xa6, 0x00, 0x86, 0x62, 0x4e, 0xf3,
+    0x00, 0x86, 0x8a, 0x4e, 0xf4, 0x00, 0x86, 0xdb,
+    0x34, 0x5e, 0x00, 0x86, 0xf8, 0x1e, 0x35, 0x00,
+    0x87, 0x03, 0x4f, 0x48, 0x00, 0x87, 0x1a, 0x35,
+    0x18, 0x00, 0x87, 0x37, 0x37, 0x82, 0x00, 0x87,
+    0x3b, 0x37, 0x83, 0x00, 0x87, 0x55, 0x1e, 0x1d,
+    0x00, 0x87, 0x59, 0x1f, 0x40, 0x00, 0x87, 0x82,
+    0x1e, 0xb6, 0x00, 0x87, 0xa3, 0x57, 0xaa, 0x00,
+    0x87, 0xbd, 0x37, 0x85, 0x00, 0x87, 0xd2, 0x1e,
+    0xb7, 0x00, 0x88, 0x03, 0x3b, 0x03, 0x00, 0x88,
+    0x05, 0x37, 0x84, 0x00, 0x88, 0x0e, 0x1f, 0x41,
+    0x00, 0x88, 0x36, 0x35, 0x19, 0x00, 0x88, 0x42,
+    0x4e, 0xf5, 0x00, 0x88, 0x46, 0x35, 0xfa, 0x00,
+    0x88, 0x4b, 0x57, 0xc3, 0x00, 0x88, 0x53, 0x35,
+    0xfd, 0x00, 0x88, 0x5b, 0x34, 0x66, 0x00, 0x88,
+    0x5e, 0x35, 0x53, 0x00, 0x88, 0x63, 0x35, 0x48,
+    0x00, 0x88, 0x70, 0x36, 0x27, 0x00, 0x88, 0x77,
+    0x4e, 0xf6, 0x00, 0x88, 0x9e, 0x35, 0x1a, 0x00,
+    0x88, 0xd8, 0x35, 0x1b, 0x00, 0x88, 0xf4, 0x35,
+    0x1c, 0x00, 0x89, 0x0a, 0x1e, 0xb8, 0x00, 0x89,
+    0x10, 0x34, 0x13, 0x00, 0x89, 0x1c, 0x37, 0xcb,
+    0x00, 0x89, 0x2b, 0x35, 0x1d, 0x00, 0x89, 0x3b,
+    0x35, 0x1e, 0x00, 0x89, 0x41, 0x4e, 0xf7, 0x00,
+    0x89, 0x56, 0x1d, 0xdd, 0x00, 0x89, 0x6a, 0x35,
+    0x1f, 0x00, 0x89, 0x6f, 0x35, 0x20, 0x00, 0x89,
+    0x81, 0x36, 0xff, 0x00, 0x89, 0x86, 0x36, 0xb8,
+    0x00, 0x89, 0x87, 0x36, 0x95, 0x00, 0x89, 0x96,
+    0x34, 0x22, 0x00, 0x89, 0xaa, 0x34, 0x9b, 0x00,
+    0x89, 0xaf, 0x1e, 0xb9, 0x00, 0x89, 0xbd, 0x37,
+    0x8a, 0x00, 0x89, 0xd2, 0x05, 0xaf, 0x00, 0x8a,
+    0x0a, 0x36, 0x24, 0x00, 0x8a, 0x12, 0x37, 0xd7,
+    0x00, 0x8a, 0x1d, 0x35, 0x21, 0x00, 0x8a, 0x1f,
+    0x34, 0x96, 0x00, 0x8a, 0x3b, 0x1e, 0x3c, 0x00,
+    0x8a, 0x55, 0x36, 0xaf, 0x00, 0x8a, 0x6e, 0x1e,
+    0x28, 0x00, 0x8a, 0x8d, 0x36, 0x92, 0x00, 0x8a,
+    0x95, 0x34, 0xa3, 0x00, 0x8a, 0xa0, 0x36, 0x2f,
+    0x00, 0x8a, 0xa4, 0x35, 0xc2, 0x00, 0x8a, 0xb9,
+    0x34, 0xb7, 0x00, 0x8a, 0xbf, 0x36, 0x6d, 0x00,
+    0x8a, 0xcb, 0x36, 0x30, 0x00, 0x8a, 0xdb, 0x37,
+    0x8b, 0x00, 0x8a, 0xde, 0x1e, 0xba, 0x00, 0x8a,
+    0xed, 0x0f, 0x0b, 0x00, 0x8a, 0xee, 0x35, 0xe3,
+    0x00, 0x8a, 0xf8, 0x09, 0x7e, 0x00, 0x8a, 0xfa,
+    0x1d, 0xfe, 0x00, 0x8b, 0x01, 0x04, 0xfc, 0x00,
+    0x8b, 0x04, 0x36, 0x86, 0x00, 0x8b, 0x0e, 0x1e,
+    0x56, 0x00, 0x8b, 0x19, 0x35, 0xb9, 0x00, 0x8b,
+    0x1b, 0x35, 0xcd, 0x00, 0x8b, 0x1d, 0x34, 0x8d,
+    0x00, 0x8b, 0x2c, 0x1e, 0x69, 0x00, 0x8b, 0x39,
+    0x06, 0xd8, 0x00, 0x8b, 0x3e, 0x37, 0x8c, 0x00,
+    0x8b, 0x41, 0x1e, 0xbb, 0x00, 0x8b, 0x56, 0x4e,
+    0xf8, 0x00, 0x8b, 0x5a, 0x37, 0x8d, 0x00, 0x8b,
+    0x5c, 0x4e, 0xfa, 0x00, 0x8b, 0x7f, 0x52, 0x52,
+    0x00, 0x8c, 0x6a, 0x4e, 0xfd, 0x00, 0x8c, 0x79,
+    0x4e, 0xfe, 0x00, 0x8c, 0x9b, 0x58, 0x37, 0x00,
+    0x8c, 0xa0, 0x36, 0xb5, 0x00, 0x8c, 0xa7, 0x34,
+    0xb8, 0x00, 0x8c, 0xa8, 0x35, 0x64, 0x00, 0x8c,
+    0xab, 0x34, 0x72, 0x00, 0x8c, 0xc7, 0x35, 0xe5,
+    0x00, 0x8c, 0xca, 0x36, 0x4c, 0x00, 0x8c, 0xd3,
+    0x0d, 0xc2, 0x00, 0x8c, 0xed, 0x1e, 0x4c, 0x00,
+    0x8c, 0xfc, 0x34, 0x86, 0x00, 0x8d, 0x05, 0x35,
+    0x22, 0x00, 0x8d, 0x08, 0x34, 0x34, 0x00, 0x8d,
+    0x0f, 0x35, 0x23, 0x00, 0x8d, 0x67, 0x4f, 0x00,
+    0x00, 0x8d, 0x70, 0x36, 0x46, 0x00, 0x8d, 0x73,
+    0x37, 0x8e, 0x00, 0x8d, 0x77, 0x35, 0x88, 0x00,
+    0x8d, 0x99, 0x37, 0x8f, 0x00, 0x8d, 0xda, 0x1e,
+    0xbc, 0x00, 0x8d, 0xdd, 0x35, 0x94, 0x00, 0x8d,
+    0xf3, 0x34, 0xa8, 0x00, 0x8e, 0x09, 0x1e, 0xbd,
+    0x00, 0x8e, 0x34, 0x37, 0x91, 0x00, 0x8e, 0x4a,
+    0x37, 0x92, 0x00, 0x8e, 0x8d, 0x36, 0xef, 0x00,
+    0x8e, 0x91, 0x35, 0x25, 0x00, 0x8e, 0xa1, 0x35,
+    0x26, 0x00, 0x8e, 0xcc, 0x34, 0x76, 0x00, 0x8e,
+    0xd4, 0x3b, 0x4a, 0x00, 0x8f, 0x03, 0x4f, 0x02,
+    0x00, 0x8f, 0x13, 0x1e, 0xbe, 0x00, 0x8f, 0x29,
+    0x34, 0xb0, 0x00, 0x8f, 0x2f, 0x34, 0x90, 0x00,
+    0x8f, 0x38, 0x36, 0xf5, 0x00, 0x8f, 0x44, 0x35,
+    0x75, 0x00, 0x8f, 0xb6, 0x3c, 0x2b, 0x00, 0x8f,
+    0xbb, 0x20, 0x4b, 0x00, 0x8f, 0xbc, 0x35, 0xd3,
+    0x00, 0x8f, 0xbf, 0x1e, 0x37, 0x00, 0x8f, 0xc2,
+    0x1d, 0xd6, 0x00, 0x8f, 0xc4, 0x1f, 0x2c, 0x00,
+    0x8f, 0xc5, 0x36, 0x26, 0x00, 0x8f, 0xc6, 0x3b,
+    0x51, 0x00, 0x8f, 0xce, 0x35, 0xae, 0x00, 0x8f,
+    0xd1, 0x35, 0xa2, 0x00, 0x8f, 0xd4, 0x36, 0xc0,
+    0x00, 0x8f, 0xe6, 0x1d, 0xdf, 0x00, 0x8f, 0xe9,
+    0x1e, 0xc0, 0x00, 0x8f, 0xea, 0x1e, 0xbf, 0x00,
+    0x8f, 0xeb, 0x36, 0x9a, 0x00, 0x8f, 0xed, 0x36,
+    0x7b, 0x00, 0x8f, 0xef, 0x37, 0x93, 0x00, 0x8f,
+    0xf0, 0x35, 0xfe, 0x00, 0x8f, 0xf6, 0x4f, 0x06,
+    0x00, 0x8f, 0xf7, 0x36, 0xe6, 0x00, 0x8f, 0xfa,
+    0x37, 0x95, 0x00, 0x8f, 0xfd, 0x36, 0x72, 0x00,
+    0x90, 0x00, 0x36, 0x51, 0x00, 0x90, 0x01, 0x36,
+    0x47, 0x00, 0x90, 0x03, 0x34, 0xad, 0x00, 0x90,
+    0x06, 0x35, 0x8d, 0x00, 0x90, 0x0e, 0x35, 0x28,
+    0x00, 0x90, 0x0f, 0x36, 0x88, 0x00, 0x90, 0x10,
+    0x36, 0x64, 0x00, 0x90, 0x14, 0x36, 0x7e, 0x00,
+    0x90, 0x17, 0x1e, 0x1f, 0x00, 0x90, 0x19, 0x1e,
+    0x5c, 0x00, 0x90, 0x1a, 0x36, 0x73, 0x00, 0x90,
+    0x1d, 0x1e, 0x22, 0x00, 0x90, 0x1e, 0x37, 0x96,
+    0x00, 0x90, 0x1f, 0x36, 0x4b, 0x00, 0x90, 0x20,
+    0x36, 0x49, 0x00, 0x90, 0x22, 0x20, 0x4a, 0x00,
+    0x90, 0x23, 0x0f, 0xc8, 0x00, 0x90, 0x2e, 0x36,
+    0x52, 0x00, 0x90, 0x31, 0x35, 0xfb, 0x00, 0x90,
+    0x32, 0x36, 0x1f, 0x00, 0x90, 0x35, 0x37, 0x97,
+    0x00, 0x90, 0x38, 0x34, 0x08, 0x00, 0x90, 0x39,
+    0x36, 0x58, 0x00, 0x90, 0x3c, 0x1e, 0x67, 0x00,
+    0x90, 0x41, 0x1e, 0x53, 0x00, 0x90, 0x42, 0x34,
+    0x9c, 0x00, 0x90, 0x47, 0x35, 0xa8, 0x00, 0x90,
+    0x4a, 0x36, 0xfc, 0x00, 0x90, 0x4b, 0x35, 0x51,
+    0x00, 0x90, 0x4d, 0x36, 0xc1, 0x00, 0x90, 0x4e,
+    0x35, 0x65, 0x00, 0x90, 0x50, 0x37, 0x98, 0x00,
+    0x90, 0x52, 0x35, 0x27, 0x00, 0x90, 0x53, 0x36,
+    0x8b, 0x00, 0x90, 0x54, 0x36, 0x58, 0x00, 0x90,
+    0x55, 0x34, 0x63, 0x00, 0x90, 0x58, 0x1e, 0xc1,
+    0x00, 0x90, 0x5c, 0x1e, 0x2e, 0x00, 0x90, 0x60,
+    0x35, 0x5a, 0x00, 0x90, 0x61, 0x1e, 0x2a, 0x00,
+    0x90, 0x63, 0x35, 0xba, 0x00, 0x90, 0x69, 0x36,
+    0x7a, 0x00, 0x90, 0x6d, 0x36, 0x48, 0x00, 0x90,
+    0x6e, 0x1e, 0x0e, 0x00, 0x90, 0x75, 0x36, 0x00,
+    0x00, 0x90, 0x77, 0x36, 0x40, 0x00, 0x90, 0x78,
+    0x36, 0x3f, 0x00, 0x90, 0x7a, 0x35, 0x4a, 0x00,
+    0x90, 0x7c, 0x1e, 0x80, 0x00, 0x90, 0x7f, 0x36,
+    0xa7, 0x00, 0x90, 0x81, 0x37, 0x9a, 0x00, 0x90,
+    0x83, 0x37, 0x5c, 0x00, 0x90, 0x84, 0x35, 0x7c,
+    0x00, 0x90, 0x87, 0x37, 0x94, 0x00, 0x90, 0x89,
+    0x34, 0x5f, 0x00, 0x90, 0x8a, 0x37, 0x9b, 0x00,
+    0x90, 0xa3, 0x1e, 0x55, 0x00, 0x90, 0xa6, 0x36,
+    0xcb, 0x00, 0x90, 0xa8, 0x4f, 0x0b, 0x00, 0x90,
+    0xaa, 0x34, 0x8e, 0x00, 0x90, 0xf7, 0x35, 0x9d,
+    0x00, 0x90, 0xfd, 0x0c, 0x4e, 0x00, 0x91, 0x2d,
+    0x1e, 0x44, 0x00, 0x91, 0x30, 0x35, 0x29, 0x00,
+    0x91, 0x4b, 0x1e, 0x12, 0x00, 0x91, 0x4c, 0x35,
+    0xf2, 0x00, 0x91, 0x4d, 0x4f, 0x0c, 0x00, 0x91,
+    0x56, 0x35, 0x2a, 0x00, 0x91, 0x58, 0x35, 0x2b,
+    0x00, 0x91, 0x65, 0x35, 0x2c, 0x00, 0x91, 0x72,
+    0x35, 0x2e, 0x00, 0x91, 0x73, 0x35, 0x2d, 0x00,
+    0x91, 0x77, 0x35, 0xd0, 0x00, 0x91, 0xa2, 0x35,
+    0x2f, 0x00, 0x91, 0xaa, 0x35, 0x31, 0x00, 0x91,
+    0xaf, 0x35, 0x30, 0x00, 0x91, 0xb1, 0x36, 0x9f,
+    0x00, 0x91, 0xb4, 0x35, 0x32, 0x00, 0x91, 0xba,
+    0x35, 0x33, 0x00, 0x91, 0xc1, 0x1e, 0xc3, 0x00,
+    0x91, 0xc7, 0x1e, 0x05, 0x00, 0x91, 0xdc, 0x4f,
+    0x42, 0x00, 0x91, 0xe3, 0x36, 0x75, 0x00, 0x91,
+    0xfc, 0x3c, 0x2c, 0x00, 0x92, 0x37, 0x34, 0x7d,
+    0x00, 0x92, 0x5b, 0x34, 0x69, 0x00, 0x92, 0xe9,
+    0x4f, 0x0d, 0x00, 0x93, 0x06, 0x1e, 0x0a, 0x00,
+    0x93, 0x35, 0x4f, 0x0e, 0x00, 0x93, 0x65, 0x3b,
+    0x86, 0x00, 0x93, 0x75, 0x4f, 0x34, 0x00, 0x93,
+    0x8b, 0x4f, 0x0f, 0x00, 0x93, 0x8c, 0x35, 0x76,
+    0x00, 0x93, 0x96, 0x35, 0xd5, 0x00, 0x93, 0x9a,
+    0x1e, 0x41, 0x00, 0x93, 0xa1, 0x59, 0x04, 0x00,
+    0x93, 0xae, 0x34, 0x3a, 0x00, 0x93, 0xdd, 0x37,
+    0xae, 0x00, 0x94, 0x3a, 0x4f, 0x10, 0x00, 0x94,
+    0x53, 0x1e, 0x79, 0x00, 0x94, 0x77, 0x35, 0x34,
+    0x00, 0x95, 0x92, 0x35, 0x7d, 0x00, 0x95, 0xab,
+    0x3b, 0x9a, 0x00, 0x95, 0xbb, 0x1e, 0xc4, 0x00,
+    0x95, 0xbc, 0x37, 0xaf, 0x00, 0x95, 0xcd, 0x4f,
+    0x11, 0x00, 0x96, 0x2a, 0x4f, 0x12, 0x00, 0x96,
+    0x4d, 0x34, 0x87, 0x00, 0x96, 0x86, 0x34, 0x51,
+    0x00, 0x96, 0x8a, 0x36, 0x53, 0x00, 0x96, 0x94,
+    0x35, 0x73, 0x00, 0x96, 0x98, 0x35, 0x35, 0x00,
+    0x96, 0x99, 0x4f, 0x31, 0x00, 0x96, 0xa3, 0x34,
+    0xca, 0x00, 0x96, 0xa7, 0x4f, 0x14, 0x00, 0x96,
+    0xb2, 0x37, 0xb1, 0x00, 0x96, 0xbb, 0x36, 0x35,
+    0x00, 0x96, 0xc5, 0x34, 0x6c, 0x00, 0x96, 0xc7,
+    0x35, 0xbe, 0x00, 0x96, 0xd9, 0x34, 0xd5, 0x00,
+    0x96, 0xda, 0x59, 0x3b, 0x00, 0x96, 0xe3, 0x0c,
+    0xc9, 0x00, 0x96, 0xe8, 0x35, 0x4d, 0x00, 0x96,
+    0xea, 0x36, 0x39, 0x00, 0x96, 0xf0, 0x34, 0xc0,
+    0x00, 0x97, 0x21, 0x59, 0x41, 0x00, 0x97, 0x24,
+    0x1e, 0xc6, 0x00, 0x97, 0x3d, 0x35, 0x36, 0x00,
+    0x97, 0x55, 0x21, 0xf8, 0x00, 0x97, 0x56, 0x21,
+    0x8b, 0x00, 0x97, 0x59, 0x37, 0xb2, 0x00, 0x97,
+    0x5c, 0x36, 0x31, 0x00, 0x97, 0x60, 0x1e, 0xc7,
+    0x00, 0x97, 0x6d, 0x1e, 0xc8, 0x00, 0x97, 0x71,
+    0x1e, 0x1e, 0x00, 0x97, 0x74, 0x1d, 0xf3, 0x00,
+    0x97, 0x84, 0x1d, 0xe5, 0x00, 0x97, 0x98, 0x1e,
+    0x1c, 0x00, 0x97, 0xad, 0x4f, 0x43, 0x00, 0x97,
+    0xd3, 0x35, 0x7e, 0x00, 0x97, 0xde, 0x3c, 0x2e,
+    0x00, 0x97, 0xf3, 0x35, 0x60, 0x00, 0x97, 0xff,
+    0x34, 0x19, 0x00, 0x98, 0x0c, 0x35, 0x39, 0x00,
+    0x98, 0x11, 0x34, 0x74, 0x00, 0x98, 0x12, 0x34,
+    0xb2, 0x00, 0x98, 0x13, 0x1e, 0x54, 0x00, 0x98,
+    0x24, 0x1e, 0xc9, 0x00, 0x98, 0x3b, 0x0d, 0xc3,
+    0x00, 0x98, 0x5e, 0x0f, 0xa8, 0x00, 0x98, 0x67,
+    0x35, 0xbf, 0x00, 0x98, 0x73, 0x35, 0x3a, 0x00,
+    0x98, 0xc3, 0x35, 0x3b, 0x00, 0x98, 0xdf, 0x36,
+    0x17, 0x00, 0x98, 0xe2, 0x35, 0x89, 0x00, 0x98,
+    0xeb, 0x37, 0xb4, 0x00, 0x98, 0xef, 0x0d, 0x67,
+    0x00, 0x98, 0xf4, 0x1d, 0xd2, 0x00, 0x98, 0xfc,
+    0x21, 0xfc, 0x00, 0x98, 0xfd, 0x36, 0xcd, 0x00,
+    0x98, 0xfe, 0x36, 0x14, 0x00, 0x99, 0x03, 0x37,
+    0xb5, 0x00, 0x99, 0x05, 0x1e, 0x77, 0x00, 0x99,
+    0x09, 0x37, 0xb6, 0x00, 0x99, 0x0a, 0x37, 0x00,
+    0x00, 0x99, 0x0c, 0x1d, 0xdb, 0x00, 0x99, 0x10,
+    0x1f, 0x22, 0x00, 0x99, 0x13, 0x35, 0x68, 0x00,
+    0x99, 0x21, 0x4f, 0x18, 0x00, 0x99, 0x28, 0x21,
+    0xfe, 0x00, 0x99, 0x45, 0x37, 0xb7, 0x00, 0x99,
+    0x4b, 0x37, 0xb9, 0x00, 0x99, 0x57, 0x1f, 0x20,
+    0x00, 0x99, 0xc1, 0x4f, 0x40, 0x00, 0x99, 0xd0,
+    0x36, 0x67, 0x00, 0x9a, 0x19, 0x1f, 0x43, 0x00,
+    0x9a, 0x30, 0x36, 0x89, 0x00, 0x9a, 0x45, 0x35,
+    0x3c, 0x00, 0x9a, 0x4a, 0x59, 0x88, 0x00, 0x9a,
+    0x5f, 0x37, 0xbb, 0x00, 0x9a, 0x65, 0x37, 0xbc,
+    0x00, 0x9a, 0xef, 0x37, 0xbd, 0x00, 0x9b, 0x18,
+    0x37, 0xbe, 0x00, 0x9b, 0x2d, 0x34, 0x3c, 0x00,
+    0x9b, 0x2e, 0x1e, 0xca, 0x00, 0x9b, 0x35, 0x59,
+    0xa4, 0x00, 0x9b, 0x4d, 0x35, 0x3d, 0x00, 0x9b,
+    0x54, 0x36, 0xdb, 0x00, 0x9b, 0x58, 0x35, 0x3e,
+    0x00, 0x9b, 0x97, 0x1e, 0xcb, 0x00, 0x9b, 0xa8,
+    0x4f, 0x1a, 0x00, 0x9b, 0xab, 0x4f, 0x38, 0x00,
+    0x9b, 0xae, 0x4f, 0x1b, 0x00, 0x9b, 0xb9, 0x4f,
+    0x1c, 0x00, 0x9b, 0xc6, 0x35, 0x3f, 0x00, 0x9b,
+    0xd6, 0x1e, 0x09, 0x00, 0x9b, 0xdb, 0x36, 0x54,
+    0x00, 0x9b, 0xe1, 0x35, 0x40, 0x00, 0x9b, 0xf1,
+    0x35, 0x41, 0x00, 0x9b, 0xf2, 0x1e, 0xcc, 0x00,
+    0x9c, 0x08, 0x4f, 0x1d, 0x00, 0x9c, 0x24, 0x4f,
+    0x1e, 0x00, 0x9c, 0x2f, 0x1d, 0xd4, 0x00, 0x9c,
+    0x3b, 0x4f, 0x1f, 0x00, 0x9c, 0x48, 0x1e, 0x39,
+    0x00, 0x9c, 0x52, 0x1e, 0x74, 0x00, 0x9c, 0x57,
+    0x37, 0x0c, 0x00, 0x9c, 0xe6, 0x4f, 0x21, 0x00,
+    0x9d, 0x07, 0x1e, 0x4f, 0x00, 0x9d, 0x08, 0x37,
+    0xc1, 0x00, 0x9d, 0x09, 0x37, 0xc0, 0x00, 0x9d,
+    0x48, 0x35, 0x42, 0x00, 0x9d, 0x60, 0x1e, 0x03,
+    0x00, 0x9d, 0x6c, 0x36, 0xce, 0x00, 0x9d, 0xb4,
+    0x0b, 0xfd, 0x00, 0x9d, 0xbf, 0x59, 0xde, 0x00,
+    0x9d, 0xc0, 0x4f, 0x22, 0x00, 0x9d, 0xc2, 0x4f,
+    0x23, 0x00, 0x9d, 0xcf, 0x35, 0x43, 0x00, 0x9e,
+    0x97, 0x34, 0xcc, 0x00, 0x9e, 0x9f, 0x34, 0xcb,
+    0x00, 0x9e, 0xa5, 0x37, 0xc2, 0x00, 0x9e, 0xaa,
+    0x1e, 0xcd, 0x00, 0x9e, 0xad, 0x1f, 0x44, 0x00,
+    0x9e, 0xbb, 0x36, 0xdc, 0x00, 0x9e, 0xbf, 0x36,
+    0xe2, 0x00, 0x9e, 0xcc, 0x37, 0xc3, 0x00, 0x9e,
+    0xdb, 0x1e, 0x31, 0x00, 0x9f, 0x08, 0x35, 0x44,
+    0x00, 0x9f, 0x3b, 0x36, 0xa9, 0x00, 0x9f, 0x4a,
+    0x37, 0xc5, 0x00, 0x9f, 0x4b, 0x37, 0x5a, 0x00,
+    0x9f, 0x4e, 0x35, 0x24, 0x00, 0x9f, 0x67, 0x37,
+    0xc7, 0x00, 0x9f, 0x8d, 0x37, 0x06, 0x00, 0x9f,
+    0x9c, 0x1e, 0xce, 0x00, 0x9f, 0x9d, 0x1e, 0xa7,
+    0x00, 0xfa, 0x11, 0x20, 0xfb, 0x00, 0xfa, 0x24,
+    0x21, 0xb8, 0x02, 0x35, 0xc4, 0x3c, 0x44, 0x02,
+    0x36, 0x3a, 0x35, 0x9b, 0x02, 0x38, 0x3d, 0x4f,
+    0x26, 0x02, 0x42, 0xee, 0x37, 0xc9, 0x02, 0x62,
+    0x70, 0x37, 0x6e, 0x02, 0x9d, 0x4b, 0x35, 0x96,
+    0x02, 0x9e, 0x3d, 0x3c, 0x4d, 0x02, 0xa6, 0x1a,
+    0x37, 0xc8, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x34,
+    0x02, 0x35, 0x83, 0x00, 0x50, 0x91, 0x35, 0xaf,
+    0x00, 0x50, 0xca, 0x37, 0x15, 0x00, 0x51, 0x54,
+    0x37, 0x17, 0x00, 0x51, 0x95, 0x37, 0x18, 0x00,
+    0x51, 0xb4, 0x35, 0xdb, 0x00, 0x51, 0xde, 0x38,
+    0x10, 0x00, 0x52, 0x72, 0x4e, 0x76, 0x00, 0x53,
+    0x7f, 0x1d, 0xed, 0x00, 0x53, 0xa9, 0x1f, 0x1c,
+    0x00, 0x55, 0x33, 0x37, 0x21, 0x00, 0x55, 0xa9,
+    0x34, 0xd6, 0x00, 0x55, 0xab, 0x4e, 0x7c, 0x00,
+    0x55, 0xe4, 0x37, 0x22, 0x00, 0x56, 0xae, 0x4e,
+    0x7e, 0x00, 0x57, 0xf4, 0x36, 0x13, 0x00, 0x58,
+    0x5a, 0x20, 0xe6, 0x00, 0x59, 0x51, 0x4e, 0x88,
+    0x00, 0x59, 0xff, 0x35, 0xe1, 0x00, 0x5a, 0xbe,
+    0x34, 0xdc, 0x00, 0x5b, 0xb3, 0x35, 0x6b, 0x00,
+    0x5c, 0x0a, 0x36, 0x4e, 0x00, 0x5c, 0x0f, 0x36,
+    0x0a, 0x00, 0x5e, 0xca, 0x34, 0x59, 0x00, 0x5e,
+    0xe3, 0x4e, 0x93, 0x00, 0x5e, 0xf6, 0x35, 0x56,
+    0x00, 0x60, 0x62, 0x4f, 0x2d, 0x00, 0x60, 0x97,
+    0x37, 0x30, 0x00, 0x61, 0x67, 0x35, 0xad, 0x00,
+    0x61, 0x68, 0x34, 0x6e, 0x00, 0x61, 0xb2, 0x4e,
+    0x98, 0x00, 0x61, 0xf2, 0x36, 0x6a, 0x00, 0x62,
+    0x49, 0x34, 0xb4, 0x00, 0x66, 0x5f, 0x37, 0x38,
+    0x00, 0x66, 0xc1, 0x4e, 0xac, 0x00, 0x67, 0x15,
+    0x36, 0x70, 0x00, 0x67, 0x17, 0x21, 0x29, 0x00,
+    0x67, 0x1b, 0x36, 0xd5, 0x00, 0x68, 0x5d, 0x36,
+    0xde, 0x00, 0x68, 0x7a, 0x36, 0xf1, 0x00, 0x69,
+    0x0d, 0x36, 0x15, 0x00, 0x69, 0x82, 0x34, 0x6f,
+    0x00, 0x6a, 0xdb, 0x35, 0xa9, 0x00, 0x6b, 0x21,
+    0x35, 0xe8, 0x00, 0x6c, 0x08, 0x34, 0xf4, 0x00,
+    0x6c, 0xaa, 0x4e, 0xbb, 0x00, 0x6c, 0xbf, 0x34,
+    0x68, 0x00, 0x6c, 0xe8, 0x32, 0x45, 0x00, 0x6d,
+    0x3e, 0x36, 0x96, 0x00, 0x6e, 0x23, 0x34, 0xf6,
+    0x00, 0x6e, 0xa2, 0x52, 0x4f, 0x00, 0x6e, 0xcb,
+    0x4e, 0xc1, 0x00, 0x6f, 0x11, 0x37, 0x45, 0x00,
+    0x6f, 0x5b, 0x4e, 0xc5, 0x00, 0x71, 0x7d, 0x1f,
+    0x24, 0x00, 0x72, 0x35, 0x35, 0xf0, 0x00, 0x73,
+    0x36, 0x36, 0xf9, 0x00, 0x73, 0x37, 0x36, 0xfa,
+    0x00, 0x73, 0xca, 0x4e, 0xcc, 0x00, 0x75, 0x11,
+    0x35, 0xd2, 0x00, 0x75, 0x15, 0x4f, 0x2b, 0x00,
+    0x79, 0x53, 0x37, 0x59, 0x00, 0x7a, 0x74, 0x35,
+    0xb1, 0x00, 0x7b, 0x08, 0x4f, 0x27, 0x00, 0x7b,
+    0xc0, 0x36, 0x37, 0x00, 0x7c, 0x3e, 0x4f, 0x29,
+    0x00, 0x7c, 0x50, 0x4e, 0xdb, 0x00, 0x7c, 0x7e,
+    0x4f, 0x45, 0x00, 0x7d, 0xb2, 0x4e, 0xde, 0x00,
+    0x7e, 0x22, 0x37, 0x66, 0x00, 0x7e, 0x35, 0x37,
+    0x68, 0x00, 0x7f, 0xc1, 0x35, 0x5e, 0x00, 0x7f,
+    0xe1, 0x4e, 0xe0, 0x00, 0x7f, 0xe9, 0x37, 0x71,
+    0x00, 0x7f, 0xfc, 0x37, 0x02, 0x00, 0x81, 0x08,
+    0x36, 0xe3, 0x00, 0x82, 0x39, 0x36, 0x3e, 0x00,
+    0x82, 0x79, 0x37, 0x76, 0x00, 0x82, 0xbd, 0x34,
+    0x6b, 0x00, 0x83, 0xdf, 0x1f, 0x28, 0x00, 0x85,
+    0x3d, 0x34, 0xc1, 0x00, 0x86, 0x12, 0x52, 0x51,
+    0x00, 0x87, 0xd2, 0x1f, 0x42, 0x00, 0x88, 0x05,
+    0x4f, 0x47, 0x00, 0x88, 0x36, 0x37, 0x87, 0x00,
+    0x8a, 0x0a, 0x36, 0x25, 0x00, 0x8a, 0x1d, 0x4f,
+    0x2c, 0x00, 0x8a, 0x95, 0x36, 0x5d, 0x00, 0x8a,
+    0xee, 0x35, 0xe4, 0x00, 0x8b, 0x56, 0x4e, 0xf9,
+    0x00, 0x8c, 0xa0, 0x36, 0xb6, 0x00, 0x8c, 0xc7,
+    0x35, 0xe6, 0x00, 0x8c, 0xca, 0x4e, 0xff, 0x00,
+    0x8c, 0xfc, 0x35, 0xce, 0x00, 0x8f, 0x44, 0x4f,
+    0x03, 0x00, 0x8f, 0xc5, 0x4f, 0x04, 0x00, 0x8f,
+    0xd4, 0x4f, 0x05, 0x00, 0x90, 0x03, 0x36, 0x87,
+    0x00, 0x90, 0x22, 0x34, 0x60, 0x00, 0x90, 0x38,
+    0x21, 0xb9, 0x00, 0x90, 0x41, 0x4f, 0x3f, 0x00,
+    0x90, 0x42, 0x36, 0x28, 0x00, 0x90, 0x55, 0x35,
+    0x49, 0x00, 0x90, 0x75, 0x36, 0x01, 0x00, 0x90,
+    0x77, 0x4f, 0x07, 0x00, 0x90, 0x89, 0x37, 0xa1,
+    0x00, 0x90, 0x8a, 0x37, 0x9c, 0x00, 0x90, 0xa6,
+    0x36, 0xcc, 0x00, 0x90, 0xaa, 0x35, 0xee, 0x00,
+    0x92, 0x5b, 0x35, 0x5b, 0x00, 0x96, 0x86, 0x21,
+    0xee, 0x00, 0x96, 0x98, 0x4f, 0x13, 0x00, 0x96,
+    0xa3, 0x37, 0x0b, 0x00, 0x96, 0xc5, 0x35, 0x67,
+    0x00, 0x97, 0x5c, 0x36, 0x32, 0x00, 0x97, 0x60,
+    0x35, 0x37, 0x00, 0x97, 0x6d, 0x1f, 0x23, 0x00,
+    0x97, 0x71, 0x35, 0x38, 0x00, 0x97, 0xff, 0x35,
+    0x9e, 0x00, 0x98, 0xef, 0x4f, 0x25, 0x00, 0x99,
+    0x0c, 0x34, 0x65, 0x00, 0x99, 0x45, 0x37, 0xb8,
+    0x00, 0x99, 0x57, 0x35, 0x9f, 0x00, 0x9e, 0x9f,
+    0x37, 0x0d, 0x00, 0x9f, 0x08, 0x37, 0xc4, 0x00,
+    0x9f, 0x8d, 0x37, 0x07, 0x02, 0x36, 0x3a, 0x35,
+    0x9c, 0x00, 0x00, 0x00, 0x11, 0x00, 0x51, 0xde,
+    0x4e, 0x71, 0x00, 0x53, 0xa9, 0x34, 0x64, 0x00,
+    0x56, 0xae, 0x4e, 0x7f, 0x00, 0x5b, 0xb3, 0x4e,
+    0x8f, 0x00, 0x61, 0x68, 0x35, 0x6c, 0x00, 0x61,
+    0xf2, 0x52, 0x50, 0x00, 0x66, 0x5f, 0x37, 0x39,
+    0x00, 0x67, 0x17, 0x37, 0x12, 0x00, 0x69, 0x82,
+    0x35, 0x70, 0x00, 0x75, 0x11, 0x4f, 0x3c, 0x00,
+    0x83, 0xdf, 0x4e, 0xe9, 0x00, 0x90, 0x77, 0x4f,
+    0x08, 0x00, 0x90, 0x89, 0x37, 0xa2, 0x00, 0x90,
+    0x8a, 0x37, 0x9d, 0x00, 0x97, 0xff, 0x4f, 0x15,
+    0x00, 0x99, 0x0c, 0x35, 0x52, 0x00, 0x99, 0x57,
+    0x4f, 0x19, 0x00, 0x00, 0x00, 0x06, 0x00, 0x51,
+    0xde, 0x21, 0x5e, 0x00, 0x53, 0xa9, 0x35, 0x4f,
+    0x00, 0x61, 0x68, 0x35, 0x6d, 0x00, 0x90, 0x89,
+    0x37, 0xa3, 0x00, 0x90, 0x8a, 0x37, 0x9e, 0x00,
+    0x97, 0xff, 0x4f, 0x16, 0x00, 0x00, 0x00, 0x04,
+    0x00, 0x53, 0xa9, 0x4f, 0x2f, 0x00, 0x61, 0x68,
+    0x35, 0x6e, 0x00, 0x90, 0x89, 0x37, 0xa4, 0x00,
+    0x90, 0x8a, 0x37, 0x9f, 0x00, 0x00, 0x00, 0x02,
+    0x00, 0x90, 0x89, 0x37, 0xa5, 0x00, 0x90, 0x8a,
+    0x37, 0xa0, 0x00, 0x00, 0x00, 0x02, 0x00, 0x90,
+    0x89, 0x37, 0xa6, 0x00, 0x90, 0x8a, 0x4f, 0x0a,
     0x00, 0x00, 0x00, 0x01, 0x00, 0x90, 0x89, 0x37,
-    0xac, 0x00, 0x00, 0x00, 0x01, 0x00, 0x90, 0x89,
-    0x4f, 0x09
+    0xa7, 0x00, 0x00, 0x00, 0x01, 0x00, 0x90, 0x89,
+    0x37, 0xa8, 0x00, 0x00, 0x00, 0x01, 0x00, 0x90,
+    0x89, 0x37, 0xa9, 0x00, 0x00, 0x00, 0x01, 0x00,
+    0x90, 0x89, 0x37, 0xaa, 0x00, 0x00, 0x00, 0x01,
+    0x00, 0x90, 0x89, 0x37, 0xab, 0x00, 0x00, 0x00,
+    0x01, 0x00, 0x90, 0x89, 0x37, 0xac, 0x00, 0x00,
+    0x00, 0x01, 0x00, 0x90, 0x89, 0x4f, 0x09
   };
diff --git a/src/marker.c b/src/marker.c
index 9727586f42..0ed1e55ddc 100644
--- a/src/marker.c
+++ b/src/marker.c
@@ -759,23 +759,6 @@ If TYPE is nil, it means the marker stays behind when you 
insert text at it.  */
   return type;
 }
 
-DEFUN ("buffer-has-markers-at", Fbuffer_has_markers_at, Sbuffer_has_markers_at,
-       1, 1, 0,
-       doc: /* Return t if there are markers pointing at POSITION in the 
current buffer.  */)
-  (Lisp_Object position)
-{
-  register struct Lisp_Marker *tail;
-  register ptrdiff_t charpos;
-
-  charpos = clip_to_bounds (BEG, XFIXNUM (position), Z);
-
-  for (tail = BUF_MARKERS (current_buffer); tail; tail = tail->next)
-    if (tail->charpos == charpos)
-      return Qt;
-
-  return Qnil;
-}
-
 #ifdef MARKER_DEBUG
 
 /* For debugging -- count the markers in buffer BUF.  */
@@ -821,5 +804,4 @@ syms_of_marker (void)
   defsubr (&Scopy_marker);
   defsubr (&Smarker_insertion_type);
   defsubr (&Sset_marker_insertion_type);
-  defsubr (&Sbuffer_has_markers_at);
 }
diff --git a/src/menu.c b/src/menu.c
index eeb0c9a7e5..c52e9258a1 100644
--- a/src/menu.c
+++ b/src/menu.c
@@ -32,10 +32,6 @@ along with GNU Emacs.  If not, see 
<https://www.gnu.org/licenses/>.  */
 #include "blockinput.h"
 #include "buffer.h"
 
-#ifdef USE_X_TOOLKIT
-#include "../lwlib/lwlib.h"
-#endif
-
 #ifdef HAVE_WINDOW_SYSTEM
 #include TERM_HEADER
 #endif /* HAVE_WINDOW_SYSTEM */
diff --git a/src/msdos.c b/src/msdos.c
index 1608245904..1d3fdd528d 100644
--- a/src/msdos.c
+++ b/src/msdos.c
@@ -1794,7 +1794,6 @@ internal_terminal_init (void)
        }
 
       Vinitial_window_system = Qpc;
-      Vwindow_system_version = make_fixnum (29); /* RE Emacs version */
       tty->terminal->type = output_msdos_raw;
 
       /* If Emacs was dumped on DOS/V machine, forget the stale VRAM
diff --git a/src/nsfont.m b/src/nsfont.m
index b54118afe5..d072b5ce77 100644
--- a/src/nsfont.m
+++ b/src/nsfont.m
@@ -324,106 +324,98 @@ ns_get_family (Lisp_Object font_spec)
 static NSFontDescriptor *
 ns_spec_to_descriptor (Lisp_Object font_spec)
 {
-    NSFontDescriptor *fdesc;
-    NSMutableDictionary *fdAttrs = [NSMutableDictionary new];
-    NSString *family = ns_get_family (font_spec);
-    NSMutableDictionary *tdict = [NSMutableDictionary new];
+  NSFontDescriptor *fdesc;
+  NSMutableDictionary *fdAttrs = [NSMutableDictionary new];
+  NSString *family = ns_get_family (font_spec);
+  NSMutableDictionary *tdict = [NSMutableDictionary new];
 
-    Lisp_Object tem;
+  Lisp_Object tem;
 
-    tem = FONT_SLANT_SYMBOLIC (font_spec);
-    if (!NILP (tem))
-      {
-       if (EQ (tem, Qitalic) || EQ (tem, Qoblique))
-         [tdict setObject: [NSNumber numberWithFloat: 1.0]
-                   forKey: NSFontSlantTrait];
-       else if (EQ (tem, intern ("reverse-italic")) ||
-                EQ (tem, intern ("reverse-oblique")))
-         [tdict setObject: [NSNumber numberWithFloat: -1.0]
-                   forKey: NSFontSlantTrait];
-       else
-         [tdict setObject: [NSNumber numberWithFloat: 0.0]
-                   forKey: NSFontSlantTrait];
-      }
+  tem = FONT_SLANT_SYMBOLIC (font_spec);
+  if (!NILP (tem))
+    {
+      if (EQ (tem, Qitalic) || EQ (tem, Qoblique))
+       [tdict setObject: [NSNumber numberWithFloat: 1.0]
+                 forKey: NSFontSlantTrait];
+      else if (EQ (tem, intern ("reverse-italic"))
+              || EQ (tem, intern ("reverse-oblique")))
+       [tdict setObject: [NSNumber numberWithFloat: -1.0]
+                 forKey: NSFontSlantTrait];
+      else
+       [tdict setObject: [NSNumber numberWithFloat: 0.0]
+                 forKey: NSFontSlantTrait];
+    }
 
-    tem = FONT_WIDTH_SYMBOLIC (font_spec);
-    if (!NILP (tem))
-      {
-       if (EQ (tem, Qcondensed))
-         [tdict setObject: [NSNumber numberWithFloat: -1.0]
-                   forKey: NSFontWidthTrait];
-       else if (EQ (tem, Qexpanded))
-         [tdict setObject: [NSNumber numberWithFloat: 1.0]
-                   forKey: NSFontWidthTrait];
-       else
-         [tdict setObject: [NSNumber numberWithFloat: 0.0]
-                   forKey: NSFontWidthTrait];
-      }
+  tem = FONT_WIDTH_SYMBOLIC (font_spec);
+  if (!NILP (tem))
+    {
+      if (EQ (tem, Qcondensed))
+       [tdict setObject: [NSNumber numberWithFloat: -1.0]
+                 forKey: NSFontWidthTrait];
+      else if (EQ (tem, Qexpanded))
+       [tdict setObject: [NSNumber numberWithFloat: 1.0]
+                 forKey: NSFontWidthTrait];
+      else
+       [tdict setObject: [NSNumber numberWithFloat: 0.0]
+                 forKey: NSFontWidthTrait];
+    }
 
-    tem = FONT_WEIGHT_SYMBOLIC (font_spec);
+  tem = FONT_WEIGHT_SYMBOLIC (font_spec);
 
-    if (!NILP (tem))
-      {
-       if (EQ (tem, Qbold))
-         {
-           [tdict setObject: [NSNumber numberWithFloat: 1.0]
-                     forKey: NSFontWeightTrait];
-         }
-       else if (EQ (tem, Qlight))
-         {
-           [tdict setObject: [NSNumber numberWithFloat: -1.0]
-                     forKey: NSFontWeightTrait];
-         }
-       else
-         {
-           [tdict setObject: [NSNumber numberWithFloat: 0.0]
-                     forKey: NSFontWeightTrait];
-         }
-      }
+  if (!NILP (tem))
+    {
+      if (EQ (tem, Qbold))
+       {
+         [tdict setObject: [NSNumber numberWithFloat: 1.0]
+                   forKey: NSFontWeightTrait];
+       }
+      else if (EQ (tem, Qlight))
+       {
+         [tdict setObject: [NSNumber numberWithFloat: -1.0]
+                   forKey: NSFontWeightTrait];
+       }
+      else
+       {
+         [tdict setObject: [NSNumber numberWithFloat: 0.0]
+                   forKey: NSFontWeightTrait];
+       }
+    }
 
-    tem = AREF (font_spec, FONT_SPACING_INDEX);
+  tem = AREF (font_spec, FONT_SPACING_INDEX);
 
-    if (family != nil)
-      {
-       [fdAttrs setObject: family
-                   forKey: NSFontFamilyAttribute];
-      }
+  if (family != nil)
+    [fdAttrs setObject: family
+               forKey: NSFontFamilyAttribute];
 
-    if (FIXNUMP (tem))
-      {
-       if (XFIXNUM (tem) != FONT_SPACING_PROPORTIONAL)
-         {
-           [fdAttrs setObject: [NSNumber numberWithBool:YES]
-                       forKey: NSFontFixedAdvanceAttribute];
-         }
-       else
-         {
-           [fdAttrs setObject: [NSNumber numberWithBool:NO]
-                       forKey: NSFontFixedAdvanceAttribute];
-         }
-      }
+  if (FIXNUMP (tem))
+    {
+      if (XFIXNUM (tem) != FONT_SPACING_PROPORTIONAL)
+       [fdAttrs setObject: [NSNumber numberWithBool: YES]
+                   forKey: NSFontFixedAdvanceAttribute];
+      else
+       [fdAttrs setObject: [NSNumber numberWithBool: NO]
+                   forKey: NSFontFixedAdvanceAttribute];
+    }
 
-    /* Handle special families such as ``fixed'' or ``Sans Serif''.  */
+  /* Handle special families such as ``fixed'', ``monospace'' or
+     ``Sans Serif''.  */
 
-    if ([family isEqualToString: @"fixed"])
-      {
-       [fdAttrs setObject: [[NSFont userFixedPitchFontOfSize: 0] familyName]
-                   forKey: NSFontFamilyAttribute];
-      }
-    else if ([family isEqualToString: @"Sans Serif"])
-      {
-       [fdAttrs setObject: [[NSFont userFontOfSize: 0] familyName]
-                   forKey: NSFontFamilyAttribute];
-      }
+  if ([family isEqualToString: @"fixed"]
+      || [family isEqualToString: @"monospace"])
+    [fdAttrs setObject: [[NSFont userFixedPitchFontOfSize: 0] familyName]
+               forKey: NSFontFamilyAttribute];
+  else if ([family isEqualToString: @"Sans Serif"])
+    [fdAttrs setObject: [[NSFont userFontOfSize: 0] familyName]
+               forKey: NSFontFamilyAttribute];
 
-    [fdAttrs setObject: tdict forKey: NSFontTraitsAttribute];
+  [fdAttrs setObject: tdict forKey: NSFontTraitsAttribute];
 
-    fdesc = [[[NSFontDescriptor fontDescriptorWithFontAttributes: fdAttrs]
-               retain] autorelease];
+  fdesc = [[[NSFontDescriptor fontDescriptorWithFontAttributes: fdAttrs]
+            retain] autorelease];
 
-    [tdict release];
-    [fdAttrs release];
-    return fdesc;
+  [tdict release];
+  [fdAttrs release];
+  return fdesc;
 }
 
 
@@ -477,7 +469,7 @@ ns_descriptor_to_entity (NSFontDescriptor *desc,
   ASET (font_entity, FONT_SIZE_INDEX, make_fixnum (0));
   ASET (font_entity, FONT_AVGWIDTH_INDEX, make_fixnum (0));
   ASET (font_entity, FONT_SPACING_INDEX,
-       make_fixnum ((data.specified & GS_SPECIFIED_WIDTH && data.monospace_p)
+       make_fixnum ((data.specified & GS_SPECIFIED_SPACING && data.monospace_p)
                     ? FONT_SPACING_MONO : FONT_SPACING_PROPORTIONAL));
 
   ASET (font_entity, FONT_EXTRA_INDEX, extra);
@@ -792,53 +784,53 @@ static NSSet
 static Lisp_Object
 ns_findfonts (Lisp_Object font_spec, BOOL isMatch)
 {
-    Lisp_Object tem, list = Qnil;
-    NSFontDescriptor *fdesc;
-    NSArray *all_descs;
-    GSFontEnumerator *enumerator = [GSFontEnumerator sharedEnumerator];
+  Lisp_Object tem, list = Qnil;
+  NSFontDescriptor *fdesc;
+  NSArray *all_descs;
+  GSFontEnumerator *enumerator = [GSFontEnumerator sharedEnumerator];
 
-    NSSet *cFamilies;
+  NSSet *cFamilies;
 
-    block_input ();
-    if (NSFONT_TRACE)
-      {
-       fprintf (stderr, "nsfont: %s for fontspec:\n    ",
-                (isMatch ? "match" : "list"));
-       debug_print (font_spec);
-      }
+  block_input ();
+  if (NSFONT_TRACE)
+    {
+      fprintf (stderr, "nsfont: %s for fontspec:\n    ",
+              (isMatch ? "match" : "list"));
+      debug_print (font_spec);
+    }
 
-    cFamilies = ns_get_covering_families (ns_get_req_script (font_spec), 0.90);
+  cFamilies = ns_get_covering_families (ns_get_req_script (font_spec), 0.90);
 
-    fdesc = ns_spec_to_descriptor (font_spec);
-    all_descs = [enumerator availableFontDescriptors];
+  fdesc = ns_spec_to_descriptor (font_spec);
+  all_descs = [enumerator availableFontDescriptors];
 
-    for (NSFontDescriptor *desc in all_descs)
-      {
-       if (![cFamilies containsObject:
-                [desc objectForKey: NSFontFamilyAttribute]])
-           continue;
-       if (!ns_font_descs_match_p (fdesc, desc))
-         continue;
-
-        tem = ns_descriptor_to_entity (desc,
-                                      AREF (font_spec, FONT_EXTRA_INDEX),
-                                       NULL);
-        if (isMatch)
-          return tem;
-       list = Fcons (tem, list);
-      }
+  for (NSFontDescriptor *desc in all_descs)
+    {
+      if (![cFamilies containsObject:
+                 [desc objectForKey: NSFontFamilyAttribute]])
+       continue;
+      if (!ns_font_descs_match_p (fdesc, desc))
+       continue;
+
+      tem = ns_descriptor_to_entity (desc,
+                                    AREF (font_spec, FONT_EXTRA_INDEX),
+                                    NULL);
+      if (isMatch)
+       return tem;
+      list = Fcons (tem, list);
+    }
 
-    unblock_input ();
+  unblock_input ();
 
-    /* Return something if was a match and nothing found.  */
-    if (isMatch)
-      return ns_fallback_entity ();
+  /* Return something if was a match and nothing found.  */
+  if (isMatch)
+    return ns_fallback_entity ();
 
-    if (NSFONT_TRACE)
-       fprintf (stderr, "    Returning %"pD"d entities.\n",
-                list_length (list));
+  if (NSFONT_TRACE)
+    fprintf (stderr, "    Returning %"pD"d entities.\n",
+            list_length (list));
 
-    return list;
+  return list;
 }
 
 
diff --git a/src/nsterm.m b/src/nsterm.m
index 6c6151701b..82fe58e90e 100644
--- a/src/nsterm.m
+++ b/src/nsterm.m
@@ -37,7 +37,6 @@ GNUstep port and post-20 update by Adrian Robert 
(arobert@cogsci.ucsd.edu)
 #include <time.h>
 #include <signal.h>
 #include <unistd.h>
-#include <stdbool.h>
 
 #include <c-ctype.h>
 #include <c-strcase.h>
@@ -3996,6 +3995,7 @@ static void
 ns_draw_stretch_glyph_string (struct glyph_string *s)
 {
   struct face *face;
+  NSColor *fg_color;
 
   if (s->hl == DRAW_CURSOR
       && !x_stretch_cursor_p)
@@ -4092,8 +4092,20 @@ ns_draw_stretch_glyph_string (struct glyph_string *s)
          NSRectFill (NSMakeRect (x, s->y, background_width, s->height));
        }
     }
-}
 
+  /* Draw overlining, etc. on the stretch glyph (or the part of the
+     stretch glyph after the cursor).  If the glyph has a box, then
+     decorations will be drawn after drawing the box in
+     ns_draw_glyph_string, in order to prevent them from being
+     overwritten by the box.  */
+  if (s->face->box == FACE_NO_BOX)
+    {
+      fg_color = [NSColor colorWithUnsignedLong:
+                           NS_FACE_FOREGROUND (s->face)];
+      ns_draw_text_decoration (s, s->face, fg_color,
+                              s->background_width, s->x);
+    }
+}
 
 static void
 ns_draw_glyph_string_foreground (struct glyph_string *s)
@@ -4411,7 +4423,8 @@ ns_draw_glyph_string (struct glyph_string *s)
     {
       NSColor *fg_color;
 
-      fg_color = [NSColor colorWithUnsignedLong:NS_FACE_FOREGROUND (s->face)];
+      fg_color = [NSColor colorWithUnsignedLong: NS_FACE_FOREGROUND (s->face)];
+
       ns_draw_text_decoration (s, s->face, fg_color,
                               s->background_width, s->x);
     }
@@ -5607,17 +5620,6 @@ ns_term_init (Lisp_Object display_name)
 
   NSTRACE_MSG ("Versions");
 
-  {
-#ifdef NS_IMPL_GNUSTEP
-    Vwindow_system_version = build_string (gnustep_base_version);
-#else
-    /* PSnextrelease (128, c); */
-    char c[DBL_BUFSIZE_BOUND];
-    int len = dtoastr (c, sizeof c, 0, 0, NSAppKitVersionNumber);
-    Vwindow_system_version = make_unibyte_string (c, len);
-#endif
-  }
-
   delete_keyboard_wait_descriptor (0);
 
   ns_app_name = [[NSProcessInfo processInfo] processName];
@@ -7917,17 +7919,24 @@ ns_create_font_panel_buttons (id target, SEL select, 
SEL cancel_action)
 
   if (!emacsframe->output_data.ns)
     return;
+
   if (screen != nil)
     {
-      emacsframe->left_pos = NSMinX (r) - NS_PARENT_WINDOW_LEFT_POS 
(emacsframe);
-      emacsframe->top_pos = NS_PARENT_WINDOW_TOP_POS (emacsframe) - NSMaxY (r);
+      emacsframe->left_pos = (NSMinX (r)
+                             - NS_PARENT_WINDOW_LEFT_POS (emacsframe));
+      emacsframe->top_pos = (NS_PARENT_WINDOW_TOP_POS (emacsframe)
+                            - NSMaxY (r));
 
-      // FIXME: after event part below didExitFullScreen is not received
-      // if (emacs_event)
-      //   {
-      //     emacs_event->kind = MOVE_FRAME_EVENT;
-      //     EV_TRAILER ((id)nil);
-      //   }
+      if (emacs_event)
+       {
+         struct input_event ie;
+         EVENT_INIT (ie);
+         ie.kind = MOVE_FRAME_EVENT;
+         XSETFRAME (ie.frame_or_window, emacsframe);
+         XSETINT (ie.x, emacsframe->left_pos);
+         XSETINT (ie.y, emacsframe->top_pos);
+         kbd_buffer_store_event (&ie);
+       }
     }
 }
 
diff --git a/src/pgtkfns.c b/src/pgtkfns.c
index beaf28f69d..9473e14f5c 100644
--- a/src/pgtkfns.c
+++ b/src/pgtkfns.c
@@ -164,8 +164,6 @@ pgtk_display_info_for_name (Lisp_Object name)
   if (dpyinfo == 0)
     error ("Cannot connect to display server %s", SDATA (name));
 
-  XSETFASTINT (Vwindow_system_version, 11);
-
   return dpyinfo;
 }
 
diff --git a/src/process.c b/src/process.c
index 7a133cda00..358899cded 100644
--- a/src/process.c
+++ b/src/process.c
@@ -7391,7 +7391,8 @@ child_signal_notify (void)
 }
 
 /* LIB_CHILD_HANDLER is a SIGCHLD handler that Emacs calls while doing
-   its own SIGCHLD handling.  On POSIXish systems, glib needs this to
+   its own SIGCHLD handling.  On POSIXish systems lacking
+   pidfd_open+waitid or using Glib 2.73.1-, Glib needs this to
    keep track of its own children.  GNUstep is similar.  */
 
 static void dummy_handler (int sig) {}
@@ -8358,7 +8359,7 @@ DEFUN ("signal-names", Fsignal_names, Ssignal_names, 0, 
0, 0,
 
 #ifdef subprocesses
 /* Arrange to catch SIGCHLD if this hasn't already been arranged.
-   Invoke this after init_process_emacs, and after glib and/or GNUstep
+   Invoke this after init_process_emacs, and after Glib and/or GNUstep
    futz with the SIGCHLD handler, but before Emacs forks any children.
    This function's caller should block SIGCHLD.  */
 
@@ -8423,26 +8424,35 @@ init_process_emacs (int sockfd)
   if (!will_dump_with_unexec_p ())
     {
 #if defined HAVE_GLIB && !defined WINDOWSNT
-      /* Tickle glib's child-handling code.  Ask glib to install a
+      /* Tickle Glib's child-handling code.  Ask Glib to install a
         watch source for Emacs itself which will initialize glib's
         private SIGCHLD handler, allowing catch_child_signal to copy
-        it into lib_child_handler.
+        it into lib_child_handler.  This is a hacky workaround to get
+        glib's g_unix_signal_handler into lib_child_handler.
 
-         Unfortunately in glib commit 2e471acf, the behavior changed to
+        In Glib 2.37.5 (2013), commit 2e471acf changed Glib to
          always install a signal handler when g_child_watch_source_new
-         is called and not just the first time it's called.  Glib also
-         now resets signal handlers to SIG_DFL when it no longer has a
-         watcher on that signal.  This is a hackey work around to get
-         glib's g_unix_signal_handler into lib_child_handler.  */
+        is called and not just the first time it's called, and to
+        reset signal handlers to SIG_DFL when it no longer has a
+        watcher on that signal.  Arrange for Emacs's signal handler
+        to be reinstalled even if this happens.
+
+        In Glib 2.73.2 (2022), commit f615eef4 changed Glib again,
+        to not install a signal handler if the system supports
+        pidfd_open and waitid (as in Linux kernel 5.3+).  The hacky
+        workaround is not needed in this case.  */
       GSource *source = g_child_watch_source_new (getpid ());
       catch_child_signal ();
       g_source_unref (source);
 
-      eassert (lib_child_handler != dummy_handler);
-      signal_handler_t lib_child_handler_glib = lib_child_handler;
-      catch_child_signal ();
-      eassert (lib_child_handler == dummy_handler);
-      lib_child_handler = lib_child_handler_glib;
+      if (lib_child_handler != dummy_handler)
+       {
+         /* The hacky workaround is needed on this platform.  */
+         signal_handler_t lib_child_handler_glib = lib_child_handler;
+         catch_child_signal ();
+         eassert (lib_child_handler == dummy_handler);
+         lib_child_handler = lib_child_handler_glib;
+       }
 #else
       catch_child_signal ();
 #endif
diff --git a/src/sysdep.c b/src/sysdep.c
index efd9638b07..abb385d138 100644
--- a/src/sysdep.c
+++ b/src/sysdep.c
@@ -1304,7 +1304,10 @@ init_sys_modes (struct tty_display_info *tty_out)
     }
 #endif /* F_GETOWN */
 
-  setvbuf (tty_out->output, NULL, _IOFBF, BUFSIZ);
+  const size_t buffer_size = (tty_out->output_buffer_size
+                             ? tty_out->output_buffer_size
+                             : BUFSIZ);
+  setvbuf (tty_out->output, NULL, _IOFBF, buffer_size);
 
   if (tty_out->terminal->set_terminal_modes_hook)
     tty_out->terminal->set_terminal_modes_hook (tty_out->terminal);
diff --git a/src/systhread.h b/src/systhread.h
index bf4e0306cd..10d6237f27 100644
--- a/src/systhread.h
+++ b/src/systhread.h
@@ -19,8 +19,6 @@ along with GNU Emacs.  If not, see 
<https://www.gnu.org/licenses/>.  */
 #ifndef SYSTHREAD_H
 #define SYSTHREAD_H
 
-#include <stdbool.h>
-
 #include <attribute.h>
 
 #ifdef THREADS_ENABLED
diff --git a/src/term.c b/src/term.c
index 2e43d89232..f8104e0304 100644
--- a/src/term.c
+++ b/src/term.c
@@ -2400,6 +2400,44 @@ frame's terminal). */)
   return Qnil;
 }
 
+DEFUN ("tty--set-output-buffer-size", Ftty__set_output_buffer_size,
+       Stty__set_output_buffer_size, 1, 2, 0, doc:
+       /* Set the output buffer size for a TTY.
+
+SIZE zero means use the system's default value.  If SIZE is
+non-zero, this also avoids flushing the output stream.
+
+TTY may be a terminal object, a frame, or nil (meaning the selected
+frame's terminal).
+
+This function temporarily suspends and resumes the terminal
+device.  */)
+  (Lisp_Object size, Lisp_Object tty)
+{
+  if (!TYPE_RANGED_FIXNUMP (size_t, size))
+    error ("Invalid output buffer size");
+  Fsuspend_tty (tty);
+  struct terminal *terminal = decode_tty_terminal (tty);
+  terminal->display_info.tty->output_buffer_size = XFIXNUM (size);
+  return Fresume_tty (tty);
+}
+
+DEFUN ("tty--output-buffer-size", Ftty__output_buffer_size,
+       Stty__output_buffer_size, 0, 1, 0, doc:
+       /* Return the output buffer size of TTY.
+
+TTY may be a terminal object, a frame, or nil (meaning the selected
+frame's terminal).
+
+A value of zero means TTY uses the system's default value.  */)
+  (Lisp_Object tty)
+{
+  struct terminal *terminal = decode_tty_terminal (tty);
+  if (terminal)
+    return make_fixnum (terminal->display_info.tty->output_buffer_size);
+  error ("Not a tty terminal");
+}
+
 
 /***********************************************************************
                               Mouse
@@ -4556,6 +4594,8 @@ trigger redisplay.  */);
   defsubr (&Stty_top_frame);
   defsubr (&Ssuspend_tty);
   defsubr (&Sresume_tty);
+  defsubr (&Stty__set_output_buffer_size);
+  defsubr (&Stty__output_buffer_size);
 #ifdef HAVE_GPM
   defsubr (&Sgpm_mouse_start);
   defsubr (&Sgpm_mouse_stop);
diff --git a/src/termchar.h b/src/termchar.h
index 49560dbc2a..0f17246411 100644
--- a/src/termchar.h
+++ b/src/termchar.h
@@ -53,6 +53,11 @@ struct tty_display_info
   FILE *output;                 /* The stream to be used for terminal output.
                                    NULL if the terminal is suspended. */
 
+  /* Size of output buffer.  A value of zero means use the default of
+     BUFIZE.  If non-zero, also minimize writes to the tty by avoiding
+     calls to flush.  */
+  size_t output_buffer_size;
+
   FILE *termscript;             /* If nonzero, send all terminal output
                                    characters to this stream also.  */
 
diff --git a/src/w32.c b/src/w32.c
index 44c279602c..9c7d536ada 100644
--- a/src/w32.c
+++ b/src/w32.c
@@ -6480,6 +6480,17 @@ chase_symlinks (const char *file)
   return target;
 }
 
+/* Return non-zero if FILE's filesystem supports symlinks.  */
+bool
+symlinks_supported (const char *file)
+{
+  if (is_windows_9x () != TRUE
+      && get_volume_info (file, NULL)
+      && (volume_info.flags & FILE_SUPPORTS_REPARSE_POINTS) != 0)
+    return true;
+  return false;
+}
+
 
 /* Posix ACL emulation.  */
 
diff --git a/src/w32.h b/src/w32.h
index dc91c595c4..b914aa9baf 100644
--- a/src/w32.h
+++ b/src/w32.h
@@ -228,6 +228,8 @@ extern int sys_link (const char *, const char *);
 extern int openat (int, const char *, int, int);
 extern int fchmodat (int, char const *, mode_t, int);
 extern int lchmod (char const *, mode_t);
+extern bool symlinks_supported (const char *);
+
 
 /* Return total and free memory info.  */
 extern int w32_memory_info (unsigned long long *, unsigned long long *,
diff --git a/src/w32fns.c b/src/w32fns.c
index 28d13a68d4..5f652ae9e4 100644
--- a/src/w32fns.c
+++ b/src/w32fns.c
@@ -6699,8 +6699,6 @@ w32_display_info_for_name (Lisp_Object name)
   if (dpyinfo == 0)
     error ("Cannot connect to server %s", SDATA (name));
 
-  XSETFASTINT (Vwindow_system_version, w32_major_version);
-
   return dpyinfo;
 }
 
@@ -6781,7 +6779,6 @@ DEFUN ("x-open-connection", Fx_open_connection, 
Sx_open_connection,
        error ("Cannot connect to server %s", SDATA (display));
     }
 
-  XSETFASTINT (Vwindow_system_version, w32_major_version);
   return Qnil;
 }
 
@@ -10450,6 +10447,66 @@ w32_get_resource (const char *key, const char *name, 
LPDWORD lpdwtype)
   return (NULL);
 }
 
+#ifdef WINDOWSNT
+
+/***********************************************************************
+                           Wallpaper
+ ***********************************************************************/
+
+typedef BOOL (WINAPI * SystemParametersInfoW_Proc) (UINT,UINT,PVOID,UINT);
+SystemParametersInfoW_Proc system_parameters_info_w_fn = NULL;
+
+DEFUN ("w32-set-wallpaper", Fw32_set_wallpaper, Sw32_set_wallpaper, 1, 1, 0,
+       doc: /* Set the desktop wallpaper image to IMAGE-FILE.  */)
+  (Lisp_Object image_file)
+{
+  Lisp_Object encoded = ENCODE_FILE (Fexpand_file_name (image_file, Qnil));
+  char *fname = SSDATA (encoded);
+  BOOL result = false;
+  DWORD err = 0;
+
+  /* UNICOWS.DLL seems to have SystemParametersInfoW, but it doesn't
+     seem to be worth the hassle to support that on Windows 9X for the
+     benefit of this minor feature.  Let them use on Windows 9X only
+     image file names that can be encoded by the system codepage.  */
+  if (w32_unicode_filenames && system_parameters_info_w_fn)
+    {
+      wchar_t fname_w[MAX_PATH];
+
+      if (filename_to_utf16 (fname, fname_w) != 0)
+       err = ERROR_FILE_NOT_FOUND;
+      else
+       result = SystemParametersInfoW (SPI_SETDESKWALLPAPER, 0, fname_w,
+                                       SPIF_SENDCHANGE);
+    }
+  else
+    {
+      char fname_a[MAX_PATH];
+
+      if (filename_to_ansi (fname, fname_a) != 0)
+       err = ERROR_FILE_NOT_FOUND;
+      else
+       result = SystemParametersInfoA (SPI_SETDESKWALLPAPER, 0, fname_a,
+                                       SPIF_SENDCHANGE);
+    }
+  if (!result)
+    {
+      if (err == ERROR_FILE_NOT_FOUND)
+       error ("Wallpaper file %s does not exist or cannot be accessed", fname);
+      else
+       {
+         err = GetLastError ();
+         if (err)
+           error ("Could not set desktop wallpaper: %s", w32_strerror (err));
+         else
+           error ("Could not set desktop wallpaper (wrong image type?)");
+       }
+    }
+
+  return Qnil;
+}
+#endif
+
 /***********************************************************************
                            Initialization
  ***********************************************************************/
@@ -10929,6 +10986,7 @@ keys when IME input is received.  */);
   defsubr (&Sx_file_dialog);
 #ifdef WINDOWSNT
   defsubr (&Ssystem_move_file_to_trash);
+  defsubr (&Sw32_set_wallpaper);
 #endif
 }
 
@@ -11182,6 +11240,10 @@ globals_of_w32fns (void)
     get_proc_addr (user32_lib, "EnumDisplayMonitors");
   get_title_bar_info_fn = (GetTitleBarInfo_Proc)
     get_proc_addr (user32_lib, "GetTitleBarInfo");
+#ifndef CYGWIN
+  system_parameters_info_w_fn = (SystemParametersInfoW_Proc)
+    get_proc_addr (user32_lib, "SystemParametersInfoW");
+#endif
 
   {
     HMODULE imm32_lib = GetModuleHandle ("imm32.dll");
diff --git a/src/w32image.c b/src/w32image.c
index da748b8dab..af10d2bd26 100644
--- a/src/w32image.c
+++ b/src/w32image.c
@@ -256,7 +256,7 @@ w32_can_use_native_image_api (Lisp_Object type)
        || EQ (type, Qbmp)
        || EQ (type, Qnative_image)))
     {
-      /* GDI+ can also display BMP, Exif, ICON, WMF, and EMF images.
+      /* GDI+ can also display Exif, ICON, WMF, and EMF images.
         But we don't yet support these in image.c.  */
       return false;
     }
diff --git a/src/w32notify.c b/src/w32notify.c
index 72e634f77c..6b5fce9f92 100644
--- a/src/w32notify.c
+++ b/src/w32notify.c
@@ -367,6 +367,12 @@ add_watch (const char *parent_dir, const char *file, BOOL 
subdirs, DWORD flags)
   if (!file)
     return NULL;
 
+  /* Do not follow symlinks, so that the caller could watch symlink
+     files.  */
+  DWORD crflags = FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED;
+  if (symlinks_supported (parent_dir))
+    crflags |= FILE_FLAG_OPEN_REPARSE_POINT;
+
   if (w32_unicode_filenames)
     {
       wchar_t dir_w[MAX_PATH], file_w[MAX_PATH];
@@ -383,8 +389,7 @@ add_watch (const char *parent_dir, const char *file, BOOL 
subdirs, DWORD flags)
                             processes from deleting files inside
                             parent_dir.  */
                          FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
-                         NULL, OPEN_EXISTING,
-                         FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED,
+                         NULL, OPEN_EXISTING, crflags,
                          NULL);
     }
   else
@@ -400,8 +405,7 @@ add_watch (const char *parent_dir, const char *file, BOOL 
subdirs, DWORD flags)
       hdir = CreateFileA (dir_a,
                          FILE_LIST_DIRECTORY,
                          FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
-                         NULL, OPEN_EXISTING,
-                         FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED,
+                         NULL, OPEN_EXISTING, crflags,
                          NULL);
     }
   if (hdir == INVALID_HANDLE_VALUE)
diff --git a/src/widget.c b/src/widget.c
index b125b4caee..5a75cdaca8 100644
--- a/src/widget.c
+++ b/src/widget.c
@@ -292,18 +292,20 @@ update_wm_hints (Widget wmshell, EmacsFrame ew)
   base_height = (wmshell->core.height - ew->core.height
                 + (rounded_height - (char_height * ch)));
 
-  /* This is kind of sleazy, but I can't see how else to tell it to
-     make it mark the WM_SIZE_HINTS size as user specified.
-   */
-/*  ((WMShellWidget) wmshell)->wm.size_hints.flags |= USSize;*/
+  /* Ensure that Xt actually sets window manager hint flags specified
+     by the caller by making sure XtNminWidth (a relatively harmless
+     resource) always changes each time this function is invoked.  */
+  ew->emacs_frame.size_switch = !ew->emacs_frame.size_switch;
 
   XtVaSetValues (wmshell,
                 XtNbaseWidth, (XtArgVal) base_width,
                 XtNbaseHeight, (XtArgVal) base_height,
                 XtNwidthInc, (XtArgVal) (frame_resize_pixelwise ? 1 : cw),
                 XtNheightInc, (XtArgVal) (frame_resize_pixelwise ? 1 : ch),
-                XtNminWidth, (XtArgVal) base_width,
-                XtNminHeight, (XtArgVal) base_height,
+                XtNminWidth, (XtArgVal) (base_width
+                                         + ew->emacs_frame.size_switch),
+                XtNminHeight, (XtArgVal) (base_height
+                                          + ew->emacs_frame.size_switch),
                 NULL);
 }
 
@@ -355,6 +357,8 @@ EmacsFrameInitialize (Widget request, Widget new,
       exit (1);
     }
 
+  ew->emacs_frame.size_switch = 1;
+
   update_from_various_frame_slots (ew);
   set_frame_size (ew);
 }
diff --git a/src/widgetprv.h b/src/widgetprv.h
index 960f814e16..fe960326b0 100644
--- a/src/widgetprv.h
+++ b/src/widgetprv.h
@@ -49,6 +49,8 @@ typedef struct {
 
   Boolean      visual_bell;            /* flash instead of beep */
   int          bell_volume;            /* how loud is beep */
+  int          size_switch;            /* hack to make setting size
+                                          hints work correctly */
 
   /* private state */
 
diff --git a/src/window.c b/src/window.c
index 2bce4c9723..12a212a85a 100644
--- a/src/window.c
+++ b/src/window.c
@@ -8363,7 +8363,8 @@ on their symbols to be controlled by this variable.  */);
   Vscroll_preserve_screen_position = Qnil;
 
   DEFVAR_LISP ("window-point-insertion-type", Vwindow_point_insertion_type,
-              doc: /* Type of marker to use for `window-point'.  */);
+              doc: /* Insertion type of marker to use for `window-point'.
+See `marker-insertion-type' for the meaning of the possible values.  */);
   Vwindow_point_insertion_type = Qnil;
   DEFSYM (Qwindow_point_insertion_type, "window-point-insertion-type");
 
diff --git a/src/xdisp.c b/src/xdisp.c
index 70f6936dd0..9534e27843 100644
--- a/src/xdisp.c
+++ b/src/xdisp.c
@@ -1960,15 +1960,18 @@ pos_visible_p (struct window *w, ptrdiff_t charpos, int 
*x, int *y,
                  int top_x_before_string = it3.current_x;
                  /* Finally, advance the iterator until we hit the
                     first display element whose character position is
-                    CHARPOS, or until the first newline from the
-                    display string, which signals the end of the
-                    display line.  */
+                    at or beyond CHARPOS, or until the first newline
+                    from the display string, which signals the end of
+                    the display line.  */
                  while (get_next_display_element (&it3))
                    {
                      if (!EQ (it3.object, string))
                        top_x_before_string = it3.current_x;
                      PRODUCE_GLYPHS (&it3);
-                     if (IT_CHARPOS (it3) == charpos
+                     if ((it3.bidi_it.scan_dir == 1
+                          && IT_CHARPOS (it3) >= charpos)
+                         || (it3.bidi_it.scan_dir == -1
+                             && IT_CHARPOS (it3) <= charpos)
                          || ITERATOR_AT_END_OF_LINE_P (&it3))
                        break;
                      it3_moved = true;
@@ -6309,7 +6312,10 @@ handle_composition_prop (struct it *it)
       pos_byte = IT_STRING_BYTEPOS (*it);
       string = it->string;
       s = SDATA (string) + pos_byte;
-      it->c = STRING_CHAR (s);
+      if (STRING_MULTIBYTE (string))
+       it->c = STRING_CHAR (s);
+      else
+       it->c = *s;
     }
   else
     {
@@ -7040,7 +7046,14 @@ pop_it (struct it *it)
               || (STRINGP (it->object)
                   && IT_STRING_CHARPOS (*it) == it->bidi_it.charpos
                   && IT_STRING_BYTEPOS (*it) == it->bidi_it.bytepos)
-              || (CONSP (it->object) && it->method == GET_FROM_STRETCH));
+              || (CONSP (it->object) && it->method == GET_FROM_STRETCH)
+              /* We could be in the middle of handling a list or a
+                 vector of several 'display' properties, in which
+                 case we should only verify the above conditions when
+                 we pop the iterator stack the last time, because
+                 higher stack levels cannot "iterate out of the
+                 display property".  */
+              || it->sp > 0);
     }
   /* If we move the iterator over text covered by a display property
      to a new buffer position, any info about previously seen overlays
@@ -10710,11 +10723,6 @@ move_it_vertically_backward (struct it *it, int dy)
   while (nlines-- && IT_CHARPOS (*it) > pos_limit)
     back_to_previous_visible_line_start (it);
 
-  /* Move one line more back, for the (rare) situation where we have
-     bidi-reordered continued lines, and we start from the top-most
-     screen line, which is the last in logical order.  */
-  if (it->bidi_p && dy == 0)
-    back_to_previous_visible_line_start (it);
   /* Reseat the iterator here.  When moving backward, we don't want
      reseat to skip forward over invisible text, set up the iterator
      to deliver from overlay strings at the new position etc.  So,
@@ -10956,7 +10964,7 @@ move_it_by_lines (struct it *it, ptrdiff_t dvpos)
     {
       struct it it2;
       void *it2data = NULL;
-      ptrdiff_t start_charpos, i;
+      ptrdiff_t start_charpos, orig_charpos, i;
       int nchars_per_row
        = (it->last_visible_x - it->first_visible_x) / FRAME_COLUMN_WIDTH 
(it->f);
       bool hit_pos_limit = false;
@@ -10966,7 +10974,7 @@ move_it_by_lines (struct it *it, ptrdiff_t dvpos)
         position.  This may actually move vertically backwards,
          in case of overlays, so adjust dvpos accordingly.  */
       dvpos += it->vpos;
-      start_charpos = IT_CHARPOS (*it);
+      orig_charpos = IT_CHARPOS (*it);
       move_it_vertically_backward (it, 0);
       dvpos -= it->vpos;
 
@@ -11019,8 +11027,9 @@ move_it_by_lines (struct it *it, ptrdiff_t dvpos)
          RESTORE_IT (&it2, &it2, it2data);
          SAVE_IT (it2, *it, it2data);
          move_it_to (it, -1, -1, -1, it->vpos + delta, MOVE_TO_VPOS);
-         /* Move back again if we got too far ahead.  */
-         if (it->vpos - it2.vpos > delta)
+         /* Move back again if we got too far ahead,
+            or didn't move at all.  */
+         if (it->vpos - it2.vpos > delta || IT_CHARPOS (*it) == orig_charpos)
            RESTORE_IT (it, &it2, it2data);
          else
            bidi_unshelve_cache (it2data, true);
@@ -31773,9 +31782,9 @@ gui_produce_glyphs (struct it *it)
          /* When no suitable font is found, display this character by
             the method specified in the first extra slot of
             Vglyphless_char_display.  */
-             Lisp_Object acronym = lookup_glyphless_char_display (-1, it);
+         Lisp_Object acronym = lookup_glyphless_char_display (-1, it);
 
-             eassert (it->what == IT_GLYPHLESS);
+         eassert (it->what == IT_GLYPHLESS);
          produce_glyphless_glyph (it, true,
                                   STRINGP (acronym) ? acronym : Qnil);
          goto done;
@@ -37109,16 +37118,20 @@ Each element, if non-nil, should be one of the 
following:
   `empty-box':  display as an empty box
   `thin-space': display as 1-pixel width space
   `zero-width': don't display
+Any other value is interpreted as `empty-box'.
 An element may also be a cons cell (GRAPHICAL . TEXT), which specifies the
 display method for graphical terminals and text terminals respectively.
 GRAPHICAL and TEXT should each have one of the values listed above.
 
-The char-table has one extra slot to control the display of a character for
-which no font is found.  This slot only takes effect on graphical terminals.
-Its value should be an ASCII acronym string, `hex-code', `empty-box', or
-`thin-space'.  It could also be a cons cell of any two of these, to specify
-separate values for graphical and text terminals.
-The default is `empty-box'.
+The char-table has one extra slot to control the display of characters
+for which no font is found on graphical terminals, and characters that
+cannot be displayed by text-mode terminals.  Its value should be an
+ASCII acronym string, `hex-code', `empty-box', or `thin-space'.  It
+could also be a cons cell of any two of these, to specify separate
+values for graphical and text terminals.  The default is `empty-box'.
+
+With the obvious exception of `zero-width', all the other representations
+are displayed using the face `glyphless-char'.
 
 If a character has a non-nil entry in an active display table, the
 display table takes effect; in this case, Emacs does not consult
diff --git a/src/xfaces.c b/src/xfaces.c
index 70d5cbeb4c..5e3a47d7f8 100644
--- a/src/xfaces.c
+++ b/src/xfaces.c
@@ -3052,6 +3052,15 @@ The value is TO.  */)
 }
 
 
+#define HANDLE_INVALID_NIL_VALUE(A,F)                                  \
+  if (NILP (value))                                                    \
+    {                                                                  \
+      add_to_log ("Warning: setting attribute `%s' of face `%s': nil " \
+                 "value is invalid, use `unspecified' instead.", A, F); \
+      /* Compatibility with 20.x.  */                                  \
+      value = Qunspecified;                                            \
+    }
+
 DEFUN ("internal-set-lisp-face-attribute", Finternal_set_lisp_face_attribute,
        Sinternal_set_lisp_face_attribute, 3, 4, 0,
        doc: /* Set attribute ATTR of FACE to VALUE.
@@ -3390,9 +3399,7 @@ FRAME 0 means change the face on all frames, and change 
the default
     }
   else if (EQ (attr, QCforeground))
     {
-      /* Compatibility with 20.x.  */
-      if (NILP (value))
-       value = Qunspecified;
+      HANDLE_INVALID_NIL_VALUE (QCforeground, face);
       if (!UNSPECIFIEDP (value)
          && !IGNORE_DEFFACE_P (value)
          && !RESET_P (value))
@@ -3409,9 +3416,7 @@ FRAME 0 means change the face on all frames, and change 
the default
     }
   else if (EQ (attr, QCdistant_foreground))
     {
-      /* Compatibility with 20.x.  */
-      if (NILP (value))
-       value = Qunspecified;
+      HANDLE_INVALID_NIL_VALUE (QCdistant_foreground, face);
       if (!UNSPECIFIEDP (value)
          && !IGNORE_DEFFACE_P (value)
          && !RESET_P (value))
@@ -3428,9 +3433,7 @@ FRAME 0 means change the face on all frames, and change 
the default
     }
   else if (EQ (attr, QCbackground))
     {
-      /* Compatibility with 20.x.  */
-      if (NILP (value))
-       value = Qunspecified;
+      HANDLE_INVALID_NIL_VALUE (QCbackground, face);
       if (!UNSPECIFIEDP (value)
          && !IGNORE_DEFFACE_P (value)
          && !RESET_P (value))
diff --git a/src/xfns.c b/src/xfns.c
index 0b1f707e9f..8cea93c669 100644
--- a/src/xfns.c
+++ b/src/xfns.c
@@ -2423,14 +2423,33 @@ x_set_use_frame_synchronization (struct frame *f, 
Lisp_Object arg,
 {
 #if defined HAVE_XSYNC && !defined USE_GTK && defined HAVE_CLOCK_GETTIME
   struct x_display_info *dpyinfo;
+  unsigned long bypass_compositor;
 
   dpyinfo = FRAME_DISPLAY_INFO (f);
 
   if (!NILP (arg) && FRAME_X_EXTENDED_COUNTER (f))
-    FRAME_X_OUTPUT (f)->use_vsync_p
-      = x_wm_supports (f, dpyinfo->Xatom_net_wm_frame_drawn);
+    {
+      FRAME_X_OUTPUT (f)->use_vsync_p
+       = x_wm_supports (f, dpyinfo->Xatom_net_wm_frame_drawn);
+
+      /* At the same time, write the bypass compositor property to the
+        outer window.  2 means to never bypass the compositor, as we
+        need its cooperation for frame synchronization.  */
+      bypass_compositor = 2;
+      XChangeProperty (dpyinfo->display, FRAME_OUTER_WINDOW (f),
+                      dpyinfo->Xatom_net_wm_bypass_compositor,
+                      XA_CARDINAL, 32, PropModeReplace,
+                      (unsigned char *) &bypass_compositor, 1);
+    }
   else
-    FRAME_X_OUTPUT (f)->use_vsync_p = false;
+    {
+      FRAME_X_OUTPUT (f)->use_vsync_p = false;
+
+      /* Remove the compositor bypass property from the outer
+        window.  */
+      XDeleteProperty (dpyinfo->display, FRAME_OUTER_WINDOW (f),
+                      dpyinfo->Xatom_net_wm_bypass_compositor);
+    }
 
   store_frame_param (f, Quse_frame_synchronization,
                     FRAME_X_OUTPUT (f)->use_vsync_p ? Qt : Qnil);
@@ -3335,22 +3354,30 @@ struct x_xim_text_conversion_data
 {
   struct coding_system *coding;
   char *source;
+  struct x_display_info *dpyinfo;
 };
 
 static Lisp_Object
-x_xim_text_to_utf8_unix_1 (ptrdiff_t nargs,
-                          Lisp_Object *args)
+x_xim_text_to_utf8_unix_1 (ptrdiff_t nargs, Lisp_Object *args)
 {
   struct x_xim_text_conversion_data *data;
   ptrdiff_t nbytes;
+  Lisp_Object coding_system;
 
   data = xmint_pointer (args[0]);
+
+  if (SYMBOLP (Vx_input_coding_system))
+    coding_system = Vx_input_coding_system;
+  else if (!NILP (data->dpyinfo->xim_coding))
+    coding_system = data->dpyinfo->xim_coding;
+  else
+    coding_system = Vlocale_coding_system;
+
   nbytes = strlen (data->source);
 
   data->coding->destination = NULL;
 
-  setup_coding_system (Vlocale_coding_system,
-                      data->coding);
+  setup_coding_system (coding_system, data->coding);
   data->coding->mode |= (CODING_MODE_LAST_BLOCK
                         | CODING_MODE_SAFE_ENCODING);
   data->coding->source = (const unsigned char *) data->source;
@@ -3363,8 +3390,7 @@ x_xim_text_to_utf8_unix_1 (ptrdiff_t nargs,
 }
 
 static Lisp_Object
-x_xim_text_to_utf8_unix_2 (Lisp_Object val,
-                          ptrdiff_t nargs,
+x_xim_text_to_utf8_unix_2 (Lisp_Object val, ptrdiff_t nargs,
                           Lisp_Object *args)
 {
   struct x_xim_text_conversion_data *data;
@@ -3381,7 +3407,8 @@ x_xim_text_to_utf8_unix_2 (Lisp_Object val,
 
 /* The string returned is not null-terminated.  */
 static char *
-x_xim_text_to_utf8_unix (XIMText *text, ptrdiff_t *length)
+x_xim_text_to_utf8_unix (struct x_display_info *dpyinfo,
+                        XIMText *text, ptrdiff_t *length)
 {
   unsigned char *wchar_buf;
   ptrdiff_t wchar_actual_length, i;
@@ -3405,6 +3432,7 @@ x_xim_text_to_utf8_unix (XIMText *text, ptrdiff_t *length)
 
   data.coding = &coding;
   data.source = text->string.multi_byte;
+  data.dpyinfo = dpyinfo;
 
   was_waiting_for_input_p = waiting_for_input;
   /* Otherwise Fsignal will crash.  */
@@ -3422,18 +3450,21 @@ static void
 xic_preedit_draw_callback (XIC xic, XPointer client_data,
                           XIMPreeditDrawCallbackStruct *call_data)
 {
-  struct frame *f = x_xic_to_frame (xic);
+  struct frame *f;
   struct x_output *output;
-  ptrdiff_t text_length = 0;
+  ptrdiff_t text_length;
   ptrdiff_t charpos;
   ptrdiff_t original_size;
   char *text;
   char *chg_start, *chg_end;
   struct input_event ie;
+
+  f = x_xic_to_frame (xic);
   EVENT_INIT (ie);
 
   if (f)
     {
+      text_length = 0;
       output = FRAME_X_OUTPUT (f);
 
       if (!output->preedit_active)
@@ -3441,7 +3472,8 @@ xic_preedit_draw_callback (XIC xic, XPointer client_data,
 
       if (call_data->text)
        {
-         text = x_xim_text_to_utf8_unix (call_data->text, &text_length);
+         text = x_xim_text_to_utf8_unix (FRAME_DISPLAY_INFO (f),
+                                         call_data->text, &text_length);
 
          if (!text)
            /* Decoding the IM text failed.  */
@@ -3955,10 +3987,6 @@ x_window (struct frame *f, long window_prompting)
   XtManageChild (pane_widget);
   XtRealizeWidget (shell_widget);
 
-  if (FRAME_X_EMBEDDED_P (f))
-    XReparentWindow (FRAME_X_DISPLAY (f), XtWindow (shell_widget),
-                    f->output_data.x->parent_desc, 0, 0);
-
   FRAME_X_WINDOW (f) = XtWindow (frame_widget);
   initial_set_up_x_back_buffer (f);
   validate_x_resource_name ();
@@ -4132,7 +4160,7 @@ x_window (struct frame *f)
   block_input ();
   FRAME_X_WINDOW (f)
     = XCreateWindow (FRAME_X_DISPLAY (f),
-                    f->output_data.x->parent_desc,
+                    FRAME_DISPLAY_INFO (f)->root_window,
                     f->left_pos,
                     f->top_pos,
                     FRAME_PIXEL_WIDTH (f), FRAME_PIXEL_HEIGHT (f),
@@ -4958,6 +4986,12 @@ This function is an internal primitive--use `make-frame' 
instead.  */)
   x_window (f);
 #endif
 
+#ifndef USE_GTK
+  if (FRAME_X_EMBEDDED_P (f)
+      && !x_embed_frame (dpyinfo, f))
+    error ("The frame could not be embedded; does the embedder exist?");
+#endif
+
   x_icon (f, parms);
   x_make_gc (f);
 
@@ -5519,15 +5553,15 @@ On MS Windows, this returns nothing useful.  */)
   switch (DoesBackingStore (dpyinfo->screen))
     {
     case Always:
-      result = intern ("always");
+      result = Qalways;
       break;
 
     case WhenMapped:
-      result = intern ("when-mapped");
+      result = Qwhen_mapped;
       break;
 
     case NotUseful:
-      result = intern ("not-useful");
+      result = Qnot_useful;
       break;
 
     default:
@@ -5556,22 +5590,22 @@ If omitted or nil, that stands for the selected frame's 
display.
   switch (dpyinfo->visual_info.class)
     {
     case StaticGray:
-      result = intern ("static-gray");
+      result = Qstatic_gray;
       break;
     case GrayScale:
-      result = intern ("gray-scale");
+      result = Qgray_scale;
       break;
     case StaticColor:
-      result = intern ("static-color");
+      result = Qstatic_color;
       break;
     case PseudoColor:
-      result = intern ("pseudo-color");
+      result = Qpseudo_color;
       break;
     case TrueColor:
-      result = intern ("true-color");
+      result = Qtrue_color;
       break;
     case DirectColor:
-      result = intern ("direct-color");
+      result = Qdirect_color;
       break;
     default:
       error ("Display has an unknown visual class");
@@ -6223,8 +6257,8 @@ In addition to the standard attribute keys listed in
 the attributes:
 
  source -- String describing the source from which multi-monitor
-          information is obtained, one of \"Gdk\", \"XRandr\",
-          \"Xinerama\", or \"fallback\"
+          information is obtained, one of \"Gdk\", \"XRandR 1.5\",
+          \"XRandr\", \"Xinerama\", or \"fallback\"
 
 Internal use only, use `display-monitor-attributes-list' instead.  */)
   (Lisp_Object terminal)
@@ -7229,8 +7263,6 @@ x_display_info_for_name (Lisp_Object name)
   if (dpyinfo == 0)
     error ("Cannot connect to X server %s", SDATA (name));
 
-  XSETFASTINT (Vwindow_system_version, 11);
-
   return dpyinfo;
 }
 
@@ -7274,7 +7306,6 @@ An insecure way to solve the problem may be to use 
`xhost'.\n",
        error ("Cannot connect to X server %s", SDATA (display));
     }
 
-  XSETFASTINT (Vwindow_system_version, 11);
   return Qnil;
 }
 
@@ -7693,17 +7724,40 @@ DEFUN ("x-window-property", Fx_window_property, 
Sx_window_property,
        doc: /* Value is the value of window property PROP on FRAME.
 If FRAME is nil or omitted, use the selected frame.
 
-On X Windows, the following optional arguments are also accepted:
-If TYPE is nil or omitted, get the property as a string.
- Otherwise TYPE is the name of the atom that denotes the expected type.
+On X Windows, the following optional arguments are also accepted: If
+TYPE is nil or omitted, get the property as a string.  Otherwise TYPE
+is the name of the atom that denotes the expected type.
+
+If TYPE is the string "AnyPropertyType", decode and return the data
+regardless of what the type really is.
+
+The format of the data returned is the same as a selection conversion
+to the given type.  For example, if `x-get-selection-internal' returns
+an integer when the selection data is a given type,
+`x-window-property' will do the same for that type.
+
 If WINDOW-ID is non-nil, get the property of that window instead of
- FRAME's X window; the number 0 denotes the root window.  This argument
- is separate from FRAME because window IDs are not unique across X
- displays or screens on the same display, so FRAME provides context
- for the window ID.
+FRAME's X window; the number 0 denotes the root window.  This argument
+is separate from FRAME because window IDs are not unique across X
+displays, so FRAME provides context for the window ID.
+
 If DELETE-P is non-nil, delete the property after retrieving it.
 If VECTOR-RET-P is non-nil, return a vector of values instead of a string.
 
+X allows an arbitrary number of properties to be set on any window.
+However, properties are most often set by the window manager or other
+programs on the root window or FRAME's X window in order to
+communicate information to Emacs and other programs.  Most of these
+properties are specified as part of the Extended Window Manager Hints
+and the Inter-Client Communication Conventions Manual, which are
+located here:
+
+  https://specifications.freedesktop.org/wm-spec/wm-spec-latest.html
+
+and
+
+  https://x.org/releases/X11R7.6/doc/xorg-docs/specs/ICCCM/icccm.html
+
 Return value is nil if FRAME doesn't have a property with name PROP or
 if PROP has no value of TYPE (always a string in the MS Windows case). */)
   (Lisp_Object prop, Lisp_Object frame, Lisp_Object type,
@@ -8291,9 +8345,9 @@ x_create_tip_frame (struct x_display_info *dpyinfo, 
Lisp_Object parms)
       disptype = Qmono;
     else if (FRAME_X_VISUAL_INFO (f)->class == GrayScale
              || FRAME_X_VISUAL_INFO (f)->class == StaticGray)
-      disptype = intern ("grayscale");
+      disptype = Qgrayscale;
     else
-      disptype = intern ("color");
+      disptype = Qcolor;
 
     if (NILP (Fframe_parameter (frame, Qdisplay_type)))
       {
@@ -8955,8 +9009,8 @@ Text larger than the specified size is clipped.  */)
 
  start_timer:
   /* Let the tip disappear after timeout seconds.  */
-  tip_timer = call3 (intern ("run-at-time"), timeout, Qnil,
-                    intern ("x-hide-tip"));
+  tip_timer = call3 (Qrun_at_time, timeout, Qnil,
+                    Qx_hide_tip);
 
   return unbind_to (count, Qnil);
 }
@@ -10054,6 +10108,23 @@ eliminated in future versions of Emacs.  */);
   /* Tell Emacs about this window system.  */
   Fprovide (Qx, Qnil);
 
+  /* Used by Fx_show_tip.  */
+  DEFSYM (Qrun_at_time, "run-at-time");
+  DEFSYM (Qx_hide_tip, "x-hide-tip");
+
+  /* Used by display class and backing store reporting functions.  */
+  DEFSYM (Qalways, "always");
+  DEFSYM (Qwhen_mapped, "when-mapped");
+  DEFSYM (Qnot_useful, "not-useful");
+  DEFSYM (Qstatic_gray, "static-gray");
+  DEFSYM (Qgray_scale, "gray-scale");
+  DEFSYM (Qstatic_color, "static-color");
+  DEFSYM (Qpseudo_color, "pseudo-color");
+  DEFSYM (Qtrue_color, "true-color");
+  DEFSYM (Qdirect_color, "direct-color");
+  DEFSYM (Qgrayscale, "grayscale");
+  DEFSYM (Qcolor, "color");
+
 #ifdef HAVE_XINPUT2
   DEFSYM (Qxinput2, "xinput2");
 
diff --git a/src/xfont.c b/src/xfont.c
index 74237e8aa8..951446b44d 100644
--- a/src/xfont.c
+++ b/src/xfont.c
@@ -253,9 +253,9 @@ xfont_supported_scripts (Display *display, char *fontname, 
Lisp_Object props,
 
   /* Two special cases to avoid opening rather big fonts.  */
   if (EQ (AREF (props, 2), Qja))
-    return list2 (intern ("kana"), intern ("han"));
+    return list2 (Qkana, Qhan);
   if (EQ (AREF (props, 2), Qko))
-    return list1 (intern ("hangul"));
+    return list1 (Qhangul);
   scripts = Fgethash (props, xfont_scripts_cache, Qt);
   if (EQ (scripts, Qt))
     {
@@ -1130,19 +1130,19 @@ static void syms_of_xfont_for_pdumper (void);
 
 struct font_driver const xfont_driver =
   {
-  .type = LISPSYM_INITIALLY (Qx),
-  .get_cache = xfont_get_cache,
-  .list = xfont_list,
-  .match = xfont_match,
-  .list_family = xfont_list_family,
-  .open_font = xfont_open,
-  .close_font = xfont_close,
-  .prepare_face = xfont_prepare_face,
-  .has_char = xfont_has_char,
-  .encode_char = xfont_encode_char,
-  .text_extents = xfont_text_extents,
-  .draw = xfont_draw,
-  .check = xfont_check,
+    .type = LISPSYM_INITIALLY (Qx),
+    .get_cache = xfont_get_cache,
+    .list = xfont_list,
+    .match = xfont_match,
+    .list_family = xfont_list_family,
+    .open_font = xfont_open,
+    .close_font = xfont_close,
+    .prepare_face = xfont_prepare_face,
+    .has_char = xfont_has_char,
+    .encode_char = xfont_encode_char,
+    .text_extents = xfont_text_extents,
+    .draw = xfont_draw,
+    .check = xfont_check,
   };
 
 void
@@ -1153,6 +1153,10 @@ syms_of_xfont (void)
   staticpro (&xfont_scratch_props);
   xfont_scratch_props = make_nil_vector (8);
   pdumper_do_now_and_after_load (syms_of_xfont_for_pdumper);
+
+  DEFSYM (Qkana, "kana");
+  DEFSYM (Qhan, "han");
+  DEFSYM (Qhangul, "hangul");
 }
 
 static void
diff --git a/src/xrdb.c b/src/xrdb.c
index faeea04a53..01c9ff5558 100644
--- a/src/xrdb.c
+++ b/src/xrdb.c
@@ -511,107 +511,3 @@ x_get_string_resource (void *v_rdb, const char *name, 
const char *class)
 
   return NULL;
 }
-
-/* Stand-alone test facilities.  */
-
-#ifdef TESTRM
-
-typedef char **List;
-#define arg_listify(len, list) (list)
-#define car(list) (*(list))
-#define cdr(list) (list + 1)
-#define NIL(list) (! *(list))
-#define free_arglist(list)
-
-static List
-member (char *elt, List list)
-{
-  List p;
-
-  for (p = list; ! NIL (p); p = cdr (p))
-    if (! strcmp (elt, car (p)))
-      return p;
-
-  return p;
-}
-
-static void
-fatal (char *msg, char *prog)
-{
-  fprintf (stderr, msg, prog);
-  exit (1);
-}
-
-int
-main (int argc, char **argv)
-{
-  Display *display;
-  char *displayname, *resource_string, *class, *name;
-  XrmDatabase xdb;
-  List arg_list, lp;
-
-  arg_list = arg_listify (argc, argv);
-
-  lp = member ("-d", arg_list);
-  if (!NIL (lp))
-    displayname = car (cdr (lp));
-  else
-    displayname = "localhost:0.0";
-
-  lp = member ("-xrm", arg_list);
-  resource_string = NIL (lp) ? 0 : car (cdr (lp));
-
-  lp = member ("-c", arg_list);
-  if (! NIL (lp))
-    class = car (cdr (lp));
-  else
-    class = "Emacs";
-
-  lp = member ("-n", arg_list);
-  if (! NIL (lp))
-    name = car (cdr (lp));
-  else
-    name = "emacs";
-
-  free_arglist (arg_list);
-
-  if (!(display = XOpenDisplay (displayname)))
-    fatal ("Can't open display '%s'\n", XDisplayName (displayname));
-
-  xdb = x_load_resources (display, resource_string, name, class);
-
-  /* In a real program, you'd want to also do this: */
-  display->db = xdb;
-
-  while (true)
-    {
-      char query_name[90];
-      char query_class[90];
-
-      printf ("Name: ");
-      gets (query_name);
-
-      if (strlen (query_name))
-       {
-         char *value;
-
-         printf ("Class: ");
-         gets (query_class);
-
-         value = x_get_string_resource (&xdb, query_name, query_class);
-
-         if (value != NULL)
-           printf ("\t%s(%s):  %s\n\n", query_name, query_class, value);
-         else
-           printf ("\tNo Value.\n\n");
-       }
-      else
-       break;
-    }
-  printf ("\tExit.\n\n");
-
-  XCloseDisplay (display);
-
-  return 0;
-}
-#endif /* TESTRM */
diff --git a/src/xselect.c b/src/xselect.c
index bab0400540..66782d4172 100644
--- a/src/xselect.c
+++ b/src/xselect.c
@@ -1567,7 +1567,8 @@ receive_incremental_selection (struct x_display_info 
*dpyinfo,
                               unsigned char **data_ret,
                               ptrdiff_t *size_bytes_ret,
                               Atom *type_ret, int *format_ret,
-                              unsigned long *size_ret)
+                              unsigned long *size_ret,
+                              ptrdiff_t *real_bytes_ret)
 {
   ptrdiff_t offset = 0;
   struct prop_location *wait_object;
@@ -1622,7 +1623,8 @@ receive_incremental_selection (struct x_display_info 
*dpyinfo,
 
       if (tmp_size_bytes == 0) /* we're done */
        {
-         TRACE0 ("Done reading incrementally");
+         TRACE1 ("Done reading incrementally; total bytes: %"pD"d",
+                 *size_bytes_ret);
 
          if (! waiting_for_other_props_on_window (display, window))
            XSelectInput (display, window, STANDARD_EVENT_SET);
@@ -1652,6 +1654,19 @@ receive_incremental_selection (struct x_display_info 
*dpyinfo,
       memcpy ((*data_ret) + offset, tmp_data, tmp_size_bytes);
       offset += tmp_size_bytes;
 
+      /* *size_bytes_ret is not really the size of the data inside the
+        buffer; it is the size of the buffer allocated by xpalloc.
+
+        This matters when the cardinal specified in the INCR property
+        (a _lower bound_ on the size of the selection data) is
+        smaller than the actual selection contents, which can happen
+        when programs are streaming selection data from a file
+        descriptor.  In that case, we used to return junk if xpalloc
+        decided to grow the buffer by more than the provided
+        increment; to avoid that, store the actual size of the
+        selection data in *real_bytes_ret.  */
+      *real_bytes_ret += tmp_size_bytes;
+
       /* Use xfree, not XFree, because x_get_window_property
         calls xmalloc itself.  */
       xfree (tmp_data);
@@ -1674,10 +1689,14 @@ x_get_window_property_as_lisp_data (struct 
x_display_info *dpyinfo,
   int actual_format;
   unsigned long actual_size;
   unsigned char *data = 0;
-  ptrdiff_t bytes = 0;
+  ptrdiff_t bytes = 0, array_bytes;
   Lisp_Object val;
   Display *display = dpyinfo->display;
 
+  /* array_bytes is only used as an argument to xpalloc.  The actual
+     size of the data inside the buffer is inside bytes.  */
+  array_bytes = 0;
+
   TRACE0 ("Reading selection data");
 
   x_get_window_property (display, window, property, &data, &bytes,
@@ -1718,10 +1737,15 @@ x_get_window_property_as_lisp_data (struct 
x_display_info *dpyinfo,
         calls xmalloc itself.  */
       xfree (data);
       unblock_input ();
+
+      /* Clear bytes again.  Previously, receive_incremental_selection
+        would set this to min_size_bytes, but that is now done to
+        array_bytes instead.  */
+      bytes = 0;
       receive_incremental_selection (dpyinfo, window, property, target_type,
-                                    min_size_bytes, &data, &bytes,
+                                    min_size_bytes, &data, &array_bytes,
                                     &actual_type, &actual_format,
-                                    &actual_size);
+                                    &actual_size, &bytes);
     }
 
   if (!for_multiple)
@@ -1993,7 +2017,17 @@ lisp_data_to_selection_data (struct x_display_info 
*dpyinfo,
       ptrdiff_t i;
       ptrdiff_t size = ASIZE (obj);
 
-      if (SYMBOLP (AREF (obj, 0)))
+      if (!size)
+       {
+         /* This vector is empty and of unknown type.  Assume that it
+            is a vector of integers.  */
+
+         cs->data = NULL;
+         cs->format = 32;
+         cs->size = 0;
+         type = QINTEGER;
+       }
+      else if (SYMBOLP (AREF (obj, 0)))
        /* This vector is an ATOM set */
        {
          void *data;
diff --git a/src/xsettings.c b/src/xsettings.c
index 9c60ff825a..e4a9865d68 100644
--- a/src/xsettings.c
+++ b/src/xsettings.c
@@ -1225,7 +1225,8 @@ xsettings_get_font_options (void)
 DEFUN ("font-get-system-normal-font", Ffont_get_system_normal_font,
        Sfont_get_system_normal_font,
        0, 0, 0,
-       doc: /* Get the system default application font. */)
+       doc: /* Get the system default application font.
+The font is returned as either a font-spec or font name.  */)
   (void)
 {
   return current_font ? build_string (current_font) : Qnil;
@@ -1233,7 +1234,8 @@ DEFUN ("font-get-system-normal-font", 
Ffont_get_system_normal_font,
 
 DEFUN ("font-get-system-font", Ffont_get_system_font, Sfont_get_system_font,
        0, 0, 0,
-       doc: /* Get the system default fixed width font. */)
+       doc: /* Get the system default fixed width font.
+The font is returned as either a font-spec or font name.  */)
   (void)
 {
   return current_mono_font ? build_string (current_mono_font) : Qnil;
@@ -1282,6 +1284,10 @@ syms_of_xsettings (void)
   DEFSYM (Qmonospace_font_name, "monospace-font-name");
   DEFSYM (Qfont_name, "font-name");
   DEFSYM (Qfont_render, "font-render");
+  DEFSYM (Qdynamic_setting, "dynamic-setting");
+  DEFSYM (Qfont_render_setting, "font-render-setting");
+  DEFSYM (Qsystem_font_setting, "system-font-setting");
+
   defsubr (&Sfont_get_system_font);
   defsubr (&Sfont_get_system_normal_font);
 
@@ -1297,9 +1303,9 @@ If this variable is nil, Emacs ignores system font 
changes.  */);
   Vxft_settings = empty_unibyte_string;
 
 #if defined USE_CAIRO || defined HAVE_XFT
-  Fprovide (intern_c_string ("font-render-setting"), Qnil);
+  Fprovide (Qfont_render_setting, Qnil);
 #if defined (HAVE_GCONF) || defined (HAVE_GSETTINGS)
-  Fprovide (intern_c_string ("system-font-setting"), Qnil);
+  Fprovide (Qsystem_font_setting, Qnil);
 #endif
 #endif
 
@@ -1307,5 +1313,5 @@ If this variable is nil, Emacs ignores system font 
changes.  */);
   DEFSYM (Qtool_bar_style, "tool-bar-style");
   defsubr (&Stool_bar_get_system_style);
 
-  Fprovide (intern_c_string ("dynamic-setting"), Qnil);
+  Fprovide (Qdynamic_setting, Qnil);
 }
diff --git a/src/xsmfns.c b/src/xsmfns.c
index 7015a8eb63..7a17e6dbd8 100644
--- a/src/xsmfns.c
+++ b/src/xsmfns.c
@@ -511,7 +511,7 @@ Do not call this function yourself. */)
      this at the wrong time. */
   if (doing_interact && ! kill_emacs)
     {
-      bool cancel_shutdown = ! NILP (call0 (intern ("emacs-session-save")));
+      bool cancel_shutdown = ! NILP (call0 (Qemacs_session_save));
 
       SmcInteractDone (smc_conn, cancel_shutdown);
       SmcSaveYourselfDone (smc_conn, True);
@@ -542,6 +542,8 @@ Do not call this function yourself. */)
 void
 syms_of_xsmfns (void)
 {
+  DEFSYM (Qemacs_session_save, "emacs-session-save");
+
   DEFVAR_LISP ("x-session-id", Vx_session_id,
     doc: /* The session id Emacs got from the session manager for this session.
 Changing the value does not change the session id used by Emacs.
diff --git a/src/xterm.c b/src/xterm.c
index 7a0a21b136..f3bfae457b 100644
--- a/src/xterm.c
+++ b/src/xterm.c
@@ -970,8 +970,8 @@ static const struct x_atom_ref x_atom_refs[] =
     /* Ghostscript support.  */
     ATOM_REFS_INIT ("DONE", Xatom_DONE)
     ATOM_REFS_INIT ("PAGE", Xatom_PAGE)
-    ATOM_REFS_INIT ("SCROLLBAR", Xatom_Scrollbar)
-    ATOM_REFS_INIT ("HORIZONTAL_SCROLLBAR", Xatom_Horizontal_Scrollbar)
+    ATOM_REFS_INIT ("_EMACS_SCROLLBAR", Xatom_Scrollbar)
+    ATOM_REFS_INIT ("_EMACS_HORIZONTAL_SCROLLBAR", Xatom_Horizontal_Scrollbar)
     ATOM_REFS_INIT ("_XEMBED", Xatom_XEMBED)
     /* EWMH */
     ATOM_REFS_INIT ("_NET_WM_STATE", Xatom_net_wm_state)
@@ -998,6 +998,7 @@ static const struct x_atom_ref x_atom_refs[] =
     ATOM_REFS_INIT ("_NET_WM_SYNC_REQUEST", Xatom_net_wm_sync_request)
     ATOM_REFS_INIT ("_NET_WM_SYNC_REQUEST_COUNTER", 
Xatom_net_wm_sync_request_counter)
     ATOM_REFS_INIT ("_NET_WM_SYNC_FENCES", Xatom_net_wm_sync_fences)
+    ATOM_REFS_INIT ("_NET_WM_BYPASS_COMPOSITOR", 
Xatom_net_wm_bypass_compositor)
     ATOM_REFS_INIT ("_NET_WM_FRAME_DRAWN", Xatom_net_wm_frame_drawn)
     ATOM_REFS_INIT ("_NET_WM_FRAME_TIMINGS", Xatom_net_wm_frame_timings)
     ATOM_REFS_INIT ("_NET_WM_USER_TIME", Xatom_net_wm_user_time)
@@ -1142,6 +1143,7 @@ static Window x_get_window_below (Display *, Window, int, 
int, int *, int *);
 #ifndef USE_TOOLKIT_SCROLL_BARS
 static void x_scroll_bar_redraw (struct scroll_bar *);
 #endif
+static void x_translate_coordinates (struct frame *, int, int, int *, int *);
 
 /* Global state maintained during a drag-and-drop operation.  */
 
@@ -1970,6 +1972,10 @@ xm_get_drag_window_1 (struct x_display_info *dpyinfo)
       && tmp_data)
     {
       drag_window = *(Window *) tmp_data;
+
+      /* This has the side effect of selecting for
+        StructureNotifyMask, meaning that we will get notifications
+        once it is deleted.  */
       rc = x_special_window_exists_p (dpyinfo, drag_window);
 
       if (!rc)
@@ -3977,12 +3983,10 @@ x_dnd_do_unsupported_drop (struct x_display_info 
*dpyinfo,
   x_ignore_errors_for_next_request (dpyinfo);
   XSendEvent (dpyinfo->display, child,
              True, ButtonPressMask, &event);
-  x_stop_ignoring_errors (dpyinfo);
 
   event.xbutton.type = ButtonRelease;
   event.xbutton.time = before + 2;
 
-  x_ignore_errors_for_next_request (dpyinfo);
   XSendEvent (dpyinfo->display, child,
              True, ButtonReleaseMask, &event);
   x_stop_ignoring_errors (dpyinfo);
@@ -4456,7 +4460,8 @@ x_dnd_get_window_proto (struct x_display_info *dpyinfo, 
Window wdesc)
 }
 
 static void
-x_dnd_send_enter (struct frame *f, Window target, int supported)
+x_dnd_send_enter (struct frame *f, Window target, Window toplevel,
+                 int supported)
 {
   struct x_display_info *dpyinfo = FRAME_DISPLAY_INFO (f);
   int i;
@@ -4465,7 +4470,7 @@ x_dnd_send_enter (struct frame *f, Window target, int 
supported)
   msg.xclient.type = ClientMessage;
   msg.xclient.message_type = dpyinfo->Xatom_XdndEnter;
   msg.xclient.format = 32;
-  msg.xclient.window = target;
+  msg.xclient.window = toplevel;
   msg.xclient.data.l[0] = FRAME_X_WINDOW (f);
   msg.xclient.data.l[1] = (((unsigned int) min (X_DND_SUPPORTED_VERSION,
                                                supported) << 24)
@@ -4493,10 +4498,10 @@ x_dnd_send_enter (struct frame *f, Window target, int 
supported)
 }
 
 static void
-x_dnd_send_position (struct frame *f, Window target, int supported,
-                    unsigned short root_x, unsigned short root_y,
-                    Time timestamp, Atom action, int button,
-                    unsigned state)
+x_dnd_send_position (struct frame *f, Window target, Window toplevel,
+                    int supported, unsigned short root_x,
+                    unsigned short root_y, Time timestamp, Atom action,
+                    int button, unsigned state)
 {
   struct x_display_info *dpyinfo = FRAME_DISPLAY_INFO (f);
   XEvent msg;
@@ -4504,7 +4509,7 @@ x_dnd_send_position (struct frame *f, Window target, int 
supported,
   msg.xclient.type = ClientMessage;
   msg.xclient.message_type = dpyinfo->Xatom_XdndPosition;
   msg.xclient.format = 32;
-  msg.xclient.window = target;
+  msg.xclient.window = toplevel;
   msg.xclient.data.l[0] = FRAME_X_WINDOW (f);
   msg.xclient.data.l[1] = 0;
 
@@ -4568,7 +4573,7 @@ x_dnd_send_position (struct frame *f, Window target, int 
supported,
 }
 
 static void
-x_dnd_send_leave (struct frame *f, Window target)
+x_dnd_send_leave (struct frame *f, Window target, Window toplevel)
 {
   struct x_display_info *dpyinfo = FRAME_DISPLAY_INFO (f);
   XEvent msg;
@@ -4576,7 +4581,7 @@ x_dnd_send_leave (struct frame *f, Window target)
   msg.xclient.type = ClientMessage;
   msg.xclient.message_type = dpyinfo->Xatom_XdndLeave;
   msg.xclient.format = 32;
-  msg.xclient.window = target;
+  msg.xclient.window = toplevel;
   msg.xclient.data.l[0] = FRAME_X_WINDOW (f);
   msg.xclient.data.l[1] = 0;
   msg.xclient.data.l[2] = 0;
@@ -4592,15 +4597,15 @@ x_dnd_send_leave (struct frame *f, Window target)
 }
 
 static bool
-x_dnd_send_drop (struct frame *f, Window target, Time timestamp,
-                int supported)
+x_dnd_send_drop (struct frame *f, Window target, Window toplevel,
+                Time timestamp, int supported)
 {
   struct x_display_info *dpyinfo;
   XEvent msg;
 
   if (x_dnd_action == None)
     {
-      x_dnd_send_leave (f, target);
+      x_dnd_send_leave (f, target, toplevel);
       return false;
     }
 
@@ -4609,7 +4614,7 @@ x_dnd_send_drop (struct frame *f, Window target, Time 
timestamp,
   msg.xclient.type = ClientMessage;
   msg.xclient.message_type = dpyinfo->Xatom_XdndDrop;
   msg.xclient.format = 32;
-  msg.xclient.window = target;
+  msg.xclient.window = toplevel;
   msg.xclient.data.l[0] = FRAME_X_WINDOW (f);
   msg.xclient.data.l[1] = 0;
   msg.xclient.data.l[2] = 0;
@@ -4626,10 +4631,10 @@ x_dnd_send_drop (struct frame *f, Window target, Time 
timestamp,
 }
 
 static bool
-x_dnd_do_drop (Window target, int supported)
+x_dnd_do_drop (Window target, Window toplevel, int supported)
 {
   if (x_dnd_waiting_for_status_window != target)
-    return x_dnd_send_drop (x_dnd_frame, target,
+    return x_dnd_send_drop (x_dnd_frame, target, toplevel,
                            x_dnd_selection_timestamp, supported);
 
   x_dnd_need_send_drop = true;
@@ -4734,7 +4739,8 @@ x_dnd_cancel_dnd_early (void)
   if (x_dnd_last_seen_window != None
       && x_dnd_last_protocol_version != -1)
     x_dnd_send_leave (x_dnd_frame,
-                     x_dnd_last_seen_window);
+                     x_dnd_last_seen_window,
+                     x_dnd_last_seen_toplevel);
   else if (x_dnd_last_seen_window != None
           && !XM_DRAG_STYLE_IS_DROP_ONLY (x_dnd_last_motif_style)
           && x_dnd_last_motif_style != XM_DRAG_STYLE_NONE
@@ -4792,7 +4798,8 @@ x_dnd_cleanup_drag_and_drop (void *frame)
       if (x_dnd_last_seen_window != None
          && x_dnd_last_protocol_version != -1)
        x_dnd_send_leave (x_dnd_frame,
-                         x_dnd_last_seen_window);
+                         x_dnd_last_seen_window,
+                         x_dnd_last_seen_toplevel);
       else if (x_dnd_last_seen_window != None
               && !XM_DRAG_STYLE_IS_DROP_ONLY (x_dnd_last_motif_style)
               && x_dnd_last_motif_style != XM_DRAG_STYLE_NONE
@@ -4846,16 +4853,13 @@ x_dnd_note_self_position (struct x_display_info 
*dpyinfo, Window target,
 {
   struct frame *f;
   int dest_x, dest_y;
-  Window child_return;
 
   f = x_top_window_to_frame (dpyinfo, target);
 
-  if (f && XTranslateCoordinates (dpyinfo->display,
-                                 dpyinfo->root_window,
-                                 FRAME_X_WINDOW (f),
-                                 root_x, root_y, &dest_x,
-                                 &dest_y, &child_return))
+  if (f)
     {
+      x_translate_coordinates (f, root_x, root_y, &dest_x, &dest_y);
+
       x_dnd_movement_frame = f;
       x_dnd_movement_x = dest_x;
       x_dnd_movement_y = dest_y;
@@ -4871,19 +4875,16 @@ x_dnd_note_self_wheel (struct x_display_info *dpyinfo, 
Window target,
 {
   struct frame *f;
   int dest_x, dest_y;
-  Window child_return;
 
   if (button < 4 || button > 7)
     return;
 
   f = x_top_window_to_frame (dpyinfo, target);
 
-  if (f && XTranslateCoordinates (dpyinfo->display,
-                                 dpyinfo->root_window,
-                                 FRAME_X_WINDOW (f),
-                                 root_x, root_y, &dest_x,
-                                 &dest_y, &child_return))
+  if (f)
     {
+      x_translate_coordinates (f, root_x, root_y, &dest_x, &dest_y);
+
       x_dnd_wheel_frame = f;
       x_dnd_wheel_x = dest_x;
       x_dnd_wheel_y = dest_y;
@@ -4906,7 +4907,6 @@ x_dnd_note_self_drop (struct x_display_info *dpyinfo, 
Window target,
   char **atom_names;
   char *name;
   int win_x, win_y, i;
-  Window dummy;
 
   if (!x_dnd_allow_current_frame
       && (FRAME_OUTER_WINDOW (x_dnd_frame)
@@ -4921,10 +4921,7 @@ x_dnd_note_self_drop (struct x_display_info *dpyinfo, 
Window target,
   if (NILP (Vx_dnd_native_test_function))
     return;
 
-  if (!XTranslateCoordinates (dpyinfo->display, dpyinfo->root_window,
-                             FRAME_X_WINDOW (f), root_x, root_y,
-                             &win_x, &win_y, &dummy))
-    return;
+  x_translate_coordinates (f, root_x, root_y, &win_x, &win_y);
 
   /* Emacs can't respond to DND events inside the nested event loop,
      so when dragging items to itself, call the test function
@@ -6125,7 +6122,7 @@ x_cr_export_frames (Lisp_Object frames, 
cairo_surface_type_t surface_type)
 
   unbind_to (count, Qnil);
 
-  return CALLN (Fapply, intern ("concat"), Fnreverse (acc));
+  return CALLN (Fapply, Qconcat, Fnreverse (acc));
 }
 
 #endif /* USE_CAIRO */
@@ -6629,22 +6626,21 @@ x_set_frame_alpha (struct frame *f)
      Do this unconditionally as this function is called on reparent when
      alpha has not changed on the frame.  */
 
+  x_ignore_errors_for_next_request (dpyinfo);
+
   if (!FRAME_PARENT_FRAME (f))
     {
       parent = x_find_topmost_parent (f);
 
       if (parent != None)
        {
-         x_ignore_errors_for_next_request (dpyinfo);
          XChangeProperty (dpy, parent,
                           dpyinfo->Xatom_net_wm_window_opacity,
                           XA_CARDINAL, 32, PropModeReplace,
                           (unsigned char *) &opac, 1);
-         x_stop_ignoring_errors (dpyinfo);
        }
     }
 
-  x_ignore_errors_for_next_request (dpyinfo);
   XChangeProperty (dpy, win, dpyinfo->Xatom_net_wm_window_opacity,
                   XA_CARDINAL, 32, PropModeReplace,
                   (unsigned char *) &opac, 1);
@@ -7611,6 +7607,11 @@ static void
 x_display_set_last_user_time (struct x_display_info *dpyinfo, Time time,
                              bool send_event)
 {
+#if defined HAVE_XSYNC && !defined USE_GTK && defined HAVE_CLOCK_GETTIME
+  uint_fast64_t monotonic_time;
+  uint_fast64_t monotonic_ms;
+  int_fast64_t diff_ms;
+#endif
 #ifndef USE_GTK
   struct frame *focus_frame;
   Time old_time;
@@ -7631,9 +7632,8 @@ x_display_set_last_user_time (struct x_display_info 
*dpyinfo, Time time,
     {
       /* See if the current CLOCK_MONOTONIC time is reasonably close
         to the X server time.  */
-      uint_fast64_t monotonic_time = x_sync_current_monotonic_time ();
-      uint_fast64_t monotonic_ms = monotonic_time / 1000;
-      int_fast64_t diff_ms;
+      monotonic_time = x_sync_current_monotonic_time ();
+      monotonic_ms = monotonic_time / 1000;
 
       dpyinfo->server_time_monotonic_p
        = (monotonic_time != 0
@@ -7651,6 +7651,27 @@ x_display_set_last_user_time (struct x_display_info 
*dpyinfo, Time time,
                                     monotonic_time,
                                     &dpyinfo->server_time_offset))
            dpyinfo->server_time_offset = 0;
+
+         /* If the server time is reasonably close to the monotonic
+            time after the latter is truncated to CARD32, simply make
+            the offset that between the server time in ms and the
+            actual time in ms.  */
+
+         monotonic_ms = monotonic_ms & 0xffffffff;
+         if (!INT_SUBTRACT_WRAPV (time, monotonic_ms, &diff_ms)
+             && -500 < diff_ms && diff_ms < 500)
+           {
+             /* The server timestamp overflowed.  Make the time
+                offset exactly how much it overflowed by.  */
+
+             if (INT_SUBTRACT_WRAPV (monotonic_time / 1000, monotonic_ms,
+                                     &dpyinfo->server_time_offset)
+                 || INT_MULTIPLY_WRAPV (dpyinfo->server_time_offset,
+                                        1000, &dpyinfo->server_time_offset)
+                 || INT_SUBTRACT_WRAPV (0, dpyinfo->server_time_offset,
+                                        &dpyinfo->server_time_offset))
+               dpyinfo->server_time_offset = 0;
+           }
        }
     }
 #endif
@@ -11914,7 +11935,8 @@ x_dnd_process_quit (struct frame *f, Time timestamp)
     {
       if (x_dnd_last_seen_window != None
          && x_dnd_last_protocol_version != -1)
-       x_dnd_send_leave (f, x_dnd_last_seen_window);
+       x_dnd_send_leave (f, x_dnd_last_seen_window,
+                         x_dnd_last_seen_toplevel);
       else if (x_dnd_last_seen_window != None
               && !XM_DRAG_STYLE_IS_DROP_ONLY (x_dnd_last_motif_style)
               && x_dnd_last_motif_style != XM_DRAG_STYLE_NONE
@@ -12641,9 +12663,11 @@ xi_handle_focus_change (struct x_display_info *dpyinfo)
          else
            dpyinfo->client_pointer_device = device->device_id;
        }
-
-      if (device->focus_implicit_frame
-         && device->focus_implicit_time > time)
+      /* Even if the implicit focus was set after the explicit focus
+        on this specific device, the explicit focus is what really
+        matters.  So use it instead.  */
+      else if (device->focus_implicit_frame
+              && device->focus_implicit_time > time)
        {
          new = device->focus_implicit_frame;
          time = device->focus_implicit_time;
@@ -12740,6 +12764,25 @@ xi_focus_handle_for_device (struct x_display_info 
*dpyinfo,
 
     case XI_FocusOut:
       device->focus_frame = NULL;
+
+      /* So, unfortunately, the X Input Extension is implemented such
+        that means XI_Leave events will not have their focus field
+        set if the core focus is transferred to another window after
+        an entry event that pretends to (or really does) set the
+        implicit focus.  In addition, if the core focus is set, but
+        the extension focus on the client pointer is not, all
+        XI_Enter events will have their focus fields set, despite not
+        actually changing the effective focus window.  Combined with
+        almost all window managers not setting the focus on input
+        extension devices, this means that Emacs will continue to
+        think the implicit focus is set on one of its frames if the
+        actual (core) focus is transferred to another window while
+        the pointer remains inside a frame.  The only workaround in
+        this case is to clear the implicit focus along with
+        XI_FocusOut events, which is not correct at all, but better
+        than leaving frames in an incorrectly-focused state.
+        (bug#57468) */
+      device->focus_implicit_frame = NULL;
       break;
 
     case XI_Enter:
@@ -13155,7 +13198,12 @@ x_detect_focus_change (struct x_display_info *dpyinfo, 
struct frame *frame,
 void
 x_mouse_leave (struct x_display_info *dpyinfo)
 {
-  Mouse_HLInfo *hlinfo = &dpyinfo->mouse_highlight;
+#if defined HAVE_XINPUT2 && !defined USE_X_TOOLKIT
+  struct xi_device_t *device;
+#endif
+  Mouse_HLInfo *hlinfo;
+
+  hlinfo = &dpyinfo->mouse_highlight;
 
   if (hlinfo->mouse_face_mouse_frame)
     {
@@ -13163,7 +13211,35 @@ x_mouse_leave (struct x_display_info *dpyinfo)
       hlinfo->mouse_face_mouse_frame = NULL;
     }
 
-  x_new_focus_frame (dpyinfo, dpyinfo->x_focus_event_frame);
+#if defined HAVE_XINPUT2 && !defined USE_X_TOOLKIT
+  if (!dpyinfo->supports_xi2)
+    /* The call below is supposed to reset the implicit focus and
+       revert the focus back to the last explicitly focused frame.  It
+       doesn't work on input extension builds because focus tracking
+       does not set x_focus_event_frame, and proceeds on a per-device
+       basis.  On such builds, clear the implicit focus of the client
+       pointer instead.  */
+#endif
+    x_new_focus_frame (dpyinfo, dpyinfo->x_focus_event_frame);
+#if defined HAVE_XINPUT2 && !defined USE_X_TOOLKIT
+  else
+    {
+      if (dpyinfo->client_pointer_device == -1)
+       /* If there's no client pointer device, then no implicit focus
+          is currently set.  */
+       return;
+
+      device = xi_device_from_id (dpyinfo, dpyinfo->client_pointer_device);
+
+      if (device && device->focus_implicit_frame)
+       {
+         device->focus_implicit_frame = NULL;
+
+         /* The focus might have changed; compute the new focus.  */
+         xi_handle_focus_change (dpyinfo);
+       }
+    }
+#endif
 }
 #endif
 
@@ -13463,6 +13539,98 @@ get_keysym_name (int keysym)
   return value;
 }
 
+/* Given the root and event coordinates of an X event destined for F's
+   edit window, compute the offset between that window and F's root
+   window.  This information is then used as an optimization to avoid
+   synchronizing when converting coordinates from some other event to
+   F's edit window.  */
+
+static void
+x_compute_root_window_offset (struct frame *f, int root_x, int root_y,
+                             int event_x, int event_y)
+{
+  FRAME_X_OUTPUT (f)->window_offset_certain_p = true;
+  FRAME_X_OUTPUT (f)->root_x = root_x - event_x;
+  FRAME_X_OUTPUT (f)->root_y = root_y - event_y;
+}
+
+/* Translate the given coordinates from the root window to the edit
+   window of FRAME, taking into account any cached root window
+   offsets.  This allows Emacs to avoid excessive calls to _XReply in
+   many cases while handling events, which would otherwise result in
+   slowdowns over slow network connections.  */
+
+static void
+x_translate_coordinates (struct frame *f, int root_x, int root_y,
+                        int *x_out, int *y_out)
+{
+  struct x_output *output;
+  Window dummy;
+
+  output = FRAME_X_OUTPUT (f);
+
+  if (output->window_offset_certain_p)
+    {
+      /* Use the cached root window offset.  */
+      *x_out = root_x - output->root_x;
+      *y_out = root_y - output->root_y;
+
+      return;
+    }
+
+  /* Otherwise, do the transformation manually.  Then, cache the root
+     window position.  */
+  if (!XTranslateCoordinates (FRAME_X_DISPLAY (f),
+                             FRAME_DISPLAY_INFO (f)->root_window,
+                             FRAME_X_WINDOW (f), root_x, root_y,
+                             x_out, y_out, &dummy))
+    /* Use some dummy values.  This is not supposed to be called with
+       coordinates out of the screen.  */
+    *x_out = 0, *y_out = 0;
+  else
+    {
+      /* Cache the root window offset of the edit window.  */
+      output->window_offset_certain_p = true;
+      output->root_x = root_x - *x_out;
+      output->root_y = root_y - *y_out;
+    }
+}
+
+/* The same, but for an XIDeviceEvent.  */
+
+#ifdef HAVE_XINPUT2
+
+static void
+xi_compute_root_window_offset (struct frame *f, XIDeviceEvent *xev)
+{
+  /* Truncate coordinates instead of rounding them, because that is
+     how the X server handles window hierarchy.  */
+  x_compute_root_window_offset (f, xev->root_x, xev->root_y,
+                               xev->event_x, xev->event_y);
+}
+
+static void
+xi_compute_root_window_offset_enter (struct frame *f, XIEnterEvent *enter)
+{
+  x_compute_root_window_offset (f, enter->root_x, enter->root_y,
+                               enter->event_x, enter->event_y);
+}
+
+#ifdef HAVE_XINPUT2_4
+
+static void
+xi_compute_root_window_offset_pinch (struct frame *f, XIGesturePinchEvent *pev)
+{
+  /* Truncate coordinates instead of rounding them, because that is
+     how the X server handles window hierarchy.  */
+  x_compute_root_window_offset (f, pev->root_x, pev->root_y,
+                               pev->event_x, pev->event_y);
+}
+
+#endif
+
+#endif
+
 static Bool
 x_query_pointer_1 (struct x_display_info *dpyinfo,
                   int client_pointer_device, Window w,
@@ -13591,10 +13759,10 @@ x_query_pointer (Display *dpy, Window w, Window 
*root_return,
    X server, and instead be artificially constructed from input
    extension events.  In these special events, the only fields that
    are initialized are `time', `button', `state', `type', `window' and
-   `x' and `y'.  This function should not access any other fields in
-   EVENT without also initializing the corresponding fields in `bv'
-   under the XI_ButtonPress and XI_ButtonRelease labels inside
-   `handle_one_xevent'.  */
+   `x', `y', `x_root' and `y_root'.  This function should not access
+   any other fields in EVENT without also initializing the
+   corresponding fields in `bv' under the XI_ButtonPress and
+   XI_ButtonRelease labels inside `handle_one_xevent'.  */
 
 static Lisp_Object
 x_construct_mouse_click (struct input_event *result,
@@ -13603,7 +13771,6 @@ x_construct_mouse_click (struct input_event *result,
 {
   int x = event->x;
   int y = event->y;
-  Window dummy;
 
   /* Make the event type NO_EVENT; we'll change that when we decide
      otherwise.  */
@@ -13620,9 +13787,8 @@ x_construct_mouse_click (struct input_event *result,
      happen with GTK+ scroll bars, for example), translate the
      coordinates so they appear at the correct position.  */
   if (event->window != FRAME_X_WINDOW (f))
-    XTranslateCoordinates (FRAME_X_DISPLAY (f),
-                          event->window, FRAME_X_WINDOW (f),
-                          x, y, &x, &y, &dummy);
+    x_translate_coordinates (f, event->x_root, event->y_root,
+                            &x, &y);
 
   XSETINT (result->x, x);
   XSETINT (result->y, y);
@@ -14325,17 +14491,23 @@ x_protect_window_for_callback (struct x_display_info 
*dpyinfo,
   return true;
 }
 
-static void
+static Lisp_Object
 x_unprotect_window_for_callback (struct x_display_info *dpyinfo)
 {
+  Lisp_Object window;
+
   if (!dpyinfo->n_protected_windows)
-    emacs_abort ();
+    return Qnil;
+
+  window = dpyinfo->protected_windows[0];
 
   dpyinfo->n_protected_windows--;
 
   if (dpyinfo->n_protected_windows)
     memmove (dpyinfo->protected_windows, &dpyinfo->protected_windows[1],
             sizeof (Lisp_Object) * dpyinfo->n_protected_windows);
+
+  return window;
 }
 
 /* Send a client message with message type Xatom_Scrollbar for a
@@ -14402,30 +14574,34 @@ x_send_scroll_bar_event (Lisp_Object window, enum 
scroll_bar_part part,
    in *IEVENT.  */
 
 static void
-x_scroll_bar_to_input_event (const XEvent *event,
+x_scroll_bar_to_input_event (struct x_display_info *dpyinfo,
+                            const XEvent *event,
                             struct input_event *ievent)
 {
-  const XClientMessageEvent *ev = &event->xclient;
   Lisp_Object window;
-  struct window *w;
 
-  /* See the comment in the function above.  */
-  intptr_t iw0 = ev->data.l[0];
-  intptr_t iw1 = ev->data.l[1];
-  intptr_t iw = (iw0 << 31 << 1) + (iw1 & 0xffffffffu);
-  w = (struct window *) iw;
+  /* Every time a scroll bar ClientMessage event is sent, the window
+     is pushed onto a queue that is traced for garbage collection.
+     Every time we need a window for a read scroll bar event, we
+     simply read from the other side of the queue.  */
+  window = x_unprotect_window_for_callback (dpyinfo);
 
-  XSETWINDOW (window, w);
+  if (NILP (window))
+    {
+      /* This means we are getting extra scroll bar events for some
+        reason, and shouldn't be possible in practice.  */
+      EVENT_INIT (*ievent);
+      return;
+    }
 
   ievent->kind = SCROLL_BAR_CLICK_EVENT;
   ievent->frame_or_window = window;
   ievent->arg = Qnil;
-  ievent->timestamp
-    = x_get_last_toolkit_time (FRAME_DISPLAY_INFO (XFRAME (w->frame)));
+  ievent->timestamp = x_get_last_toolkit_time (dpyinfo);
   ievent->code = 0;
-  ievent->part = ev->data.l[2];
-  ievent->x = make_fixnum (ev->data.l[3]);
-  ievent->y = make_fixnum (ev->data.l[4]);
+  ievent->part = event->xclient.data.l[2];
+  ievent->x = make_fixnum (event->xclient.data.l[3]);
+  ievent->y = make_fixnum (event->xclient.data.l[4]);
   ievent->modifiers = 0;
 }
 
@@ -14433,30 +14609,34 @@ x_scroll_bar_to_input_event (const XEvent *event,
    input event in *IEVENT.  */
 
 static void
-x_horizontal_scroll_bar_to_input_event (const XEvent *event,
+x_horizontal_scroll_bar_to_input_event (struct x_display_info *dpyinfo,
+                                       const XEvent *event,
                                        struct input_event *ievent)
 {
-  const XClientMessageEvent *ev = &event->xclient;
   Lisp_Object window;
-  struct window *w;
 
-  /* See the comment in the function above.  */
-  intptr_t iw0 = ev->data.l[0];
-  intptr_t iw1 = ev->data.l[1];
-  intptr_t iw = (iw0 << 31 << 1) + (iw1 & 0xffffffffu);
-  w = (struct window *) iw;
+  /* Every time a scroll bar ClientMessage event is sent, the window
+     is pushed onto a queue that is traced for garbage collection.
+     Every time we need a window for a read scroll bar event, we
+     simply read from the other side of the queue.  */
+  window = x_unprotect_window_for_callback (dpyinfo);
 
-  XSETWINDOW (window, w);
+  if (NILP (window))
+    {
+      /* This means we are getting extra scroll bar events for some
+        reason, and shouldn't be possible in practice.  */
+      EVENT_INIT (*ievent);
+      return;
+    }
 
   ievent->kind = HORIZONTAL_SCROLL_BAR_CLICK_EVENT;
   ievent->frame_or_window = window;
   ievent->arg = Qnil;
-  ievent->timestamp
-    = x_get_last_toolkit_time (FRAME_DISPLAY_INFO (XFRAME (w->frame)));
+  ievent->timestamp = x_get_last_toolkit_time (dpyinfo);
   ievent->code = 0;
-  ievent->part = ev->data.l[2];
-  ievent->x = make_fixnum (ev->data.l[3]);
-  ievent->y = make_fixnum (ev->data.l[4]);
+  ievent->part = event->xclient.data.l[2];
+  ievent->x = make_fixnum (event->xclient.data.l[3]);
+  ievent->y = make_fixnum (event->xclient.data.l[4]);
   ievent->modifiers = 0;
 }
 
@@ -17062,7 +17242,8 @@ x_dnd_update_state (struct x_display_info *dpyinfo, 
Time timestamp)
              if (x_dnd_last_seen_window != None
                  && x_dnd_last_protocol_version != -1
                  && x_dnd_last_seen_window != FRAME_OUTER_WINDOW (x_dnd_frame))
-               x_dnd_send_leave (x_dnd_frame, x_dnd_last_seen_window);
+               x_dnd_send_leave (x_dnd_frame, x_dnd_last_seen_window,
+                                 x_dnd_last_seen_toplevel);
              else if (x_dnd_last_seen_window != None
                       && XM_DRAG_STYLE_IS_DYNAMIC (x_dnd_last_motif_style)
                       && !x_dnd_disable_motif_drag
@@ -17093,8 +17274,6 @@ x_dnd_update_state (struct x_display_info *dpyinfo, 
Time timestamp)
              x_dnd_waiting_for_finish = false;
              target = None;
            }
-
-         x_dnd_last_seen_toplevel = toplevel;
        }
 
       if (target != x_dnd_last_seen_window)
@@ -17102,7 +17281,8 @@ x_dnd_update_state (struct x_display_info *dpyinfo, 
Time timestamp)
          if (x_dnd_last_seen_window != None
              && x_dnd_last_protocol_version != -1
              && x_dnd_last_seen_window != FRAME_OUTER_WINDOW (x_dnd_frame))
-           x_dnd_send_leave (x_dnd_frame, x_dnd_last_seen_window);
+           x_dnd_send_leave (x_dnd_frame, x_dnd_last_seen_window,
+                             x_dnd_last_seen_toplevel);
          else if (x_dnd_last_seen_window != None
                   && XM_DRAG_STYLE_IS_DYNAMIC (x_dnd_last_motif_style)
                   && !x_dnd_disable_motif_drag
@@ -17124,13 +17304,14 @@ x_dnd_update_state (struct x_display_info *dpyinfo, 
Time timestamp)
            }
 
          x_dnd_action = None;
+         x_dnd_last_seen_toplevel = toplevel;
          x_dnd_last_seen_window = target;
          x_dnd_last_protocol_version = target_proto;
          x_dnd_last_motif_style = motif_style;
          x_dnd_last_window_is_frame = was_frame;
 
          if (target != None && x_dnd_last_protocol_version != -1)
-           x_dnd_send_enter (x_dnd_frame, target,
+           x_dnd_send_enter (x_dnd_frame, target, x_dnd_last_seen_toplevel,
                              x_dnd_last_protocol_version);
          else if (target != None && XM_DRAG_STYLE_IS_DYNAMIC 
(x_dnd_last_motif_style)
                   && !x_dnd_disable_motif_drag)
@@ -17151,11 +17332,14 @@ x_dnd_update_state (struct x_display_info *dpyinfo, 
Time timestamp)
                                                 target, &emsg);
            }
        }
+      else
+       x_dnd_last_seen_toplevel = toplevel;
 
       if (x_dnd_last_window_is_frame && target != None)
        x_dnd_note_self_position (dpyinfo, target, root_x, root_y);
       else if (x_dnd_last_protocol_version != -1 && target != None)
        x_dnd_send_position (x_dnd_frame, target,
+                            x_dnd_last_seen_toplevel,
                             x_dnd_last_protocol_version,
                             root_x, root_y,
                             x_dnd_selection_timestamp,
@@ -17199,7 +17383,8 @@ x_dnd_update_state (struct x_display_info *dpyinfo, 
Time timestamp)
       if (x_dnd_last_seen_window != None
          && x_dnd_last_protocol_version != -1)
        x_dnd_send_leave (x_dnd_frame,
-                         x_dnd_last_seen_window);
+                         x_dnd_last_seen_window,
+                         x_dnd_last_seen_toplevel);
       else if (x_dnd_last_seen_window != None
               && !XM_DRAG_STYLE_IS_DROP_ONLY (x_dnd_last_motif_style)
               && x_dnd_last_motif_style != XM_DRAG_STYLE_NONE
@@ -17516,6 +17701,53 @@ x_coords_from_dnd_message (struct x_display_info 
*dpyinfo,
   return false;
 }
 
+static void
+x_handle_wm_state (struct frame *f, struct input_event *ie)
+{
+  Atom type;
+  int format;
+  unsigned long nitems, bytes_after;
+  unsigned char *data;
+  unsigned long *state;
+
+  data = NULL;
+
+  if (XGetWindowProperty (FRAME_X_DISPLAY (f), FRAME_OUTER_WINDOW (f),
+                         FRAME_DISPLAY_INFO (f)->Xatom_wm_state, 0, 2,
+                         False, AnyPropertyType, &type, &format, &nitems,
+                         &bytes_after, &data) != Success)
+    return;
+
+  if (!data || nitems != 2 || format != 32)
+    {
+      if (data)
+       XFree (data);
+
+      return;
+    }
+
+  state = (unsigned long *) data;
+
+  if (state[0] == NormalState && FRAME_ICONIFIED_P (f))
+    {
+      /* The frame has been deiconified.  It has not been withdrawn
+        and is now visible.  */
+      SET_FRAME_VISIBLE (f, 1);
+      SET_FRAME_ICONIFIED (f, false);
+      f->output_data.x->has_been_visible = true;
+
+      ie->kind = DEICONIFY_EVENT;
+      XSETFRAME (ie->frame_or_window, f);
+    }
+
+  /* state[0] can also be WithdrawnState, meaning that the window has
+     been withdrawn and is no longer iconified.  However, Emacs sets
+     the correct flags upon withdrawing the window, so there is no
+     need to do anything here.  */
+
+  XFree (data);
+}
+
 /* Handles the XEvent EVENT on display DPYINFO.
 
    *FINISH is X_EVENT_GOTO_OUT if caller should stop reading events.
@@ -17539,7 +17771,7 @@ handle_one_xevent (struct x_display_info *dpyinfo,
   int do_help = 0;
 #ifdef HAVE_XINPUT2
   struct xi_device_t *gen_help_device;
-  Time gen_help_time;
+  Time gen_help_time UNINIT;
 #endif
   ptrdiff_t nbytes = 0;
   struct frame *any, *f = NULL;
@@ -17565,6 +17797,12 @@ handle_one_xevent (struct x_display_info *dpyinfo,
   int dx, dy;
   USE_SAFE_ALLOCA;
 
+  /* This function is not reentrant, so input should be blocked before
+     it is called.  */
+
+  if (!input_blocked_p ())
+    emacs_abort ();
+
   *finish = X_EVENT_NORMAL;
 
   EVENT_INIT (inev.ie);
@@ -17614,7 +17852,7 @@ handle_one_xevent (struct x_display_info *dpyinfo,
 
            if (x_dnd_last_protocol_version != -1
                && x_dnd_in_progress
-               && target == x_dnd_last_seen_window
+               && target == x_dnd_last_seen_toplevel
                /* The XDND documentation is not very clearly worded.
                   But this should be the correct behavior, since
                   "kDNDStatusSendHereFlag" in the reference
@@ -17711,7 +17949,9 @@ handle_one_xevent (struct x_display_info *dpyinfo,
 
                    x_dnd_waiting_for_finish
                      = x_dnd_send_drop (x_dnd_finish_frame,
-                                        target, x_dnd_selection_timestamp,
+                                        target,
+                                        x_dnd_last_seen_toplevel,
+                                        x_dnd_selection_timestamp,
                                         x_dnd_send_drop_proto);
                  }
              }
@@ -18031,13 +18271,22 @@ handle_one_xevent (struct x_display_info *dpyinfo,
            we construct an input_event.  */
         if (event->xclient.message_type == dpyinfo->Xatom_Scrollbar)
           {
-            x_scroll_bar_to_input_event (event, &inev.ie);
+           /* Convert the scroll bar event to an input event using
+              the first window entered into the scroll bar event
+              queue. */
+            x_scroll_bar_to_input_event (dpyinfo, event, &inev.ie);
+
            *finish = X_EVENT_GOTO_OUT;
             goto done;
           }
         else if (event->xclient.message_type == 
dpyinfo->Xatom_Horizontal_Scrollbar)
           {
-            x_horizontal_scroll_bar_to_input_event (event, &inev.ie);
+           /* Convert the horizontal scroll bar event to an input
+              event using the first window entered into the scroll
+              bar event queue. */
+            x_horizontal_scroll_bar_to_input_event (dpyinfo, event,
+                                                   &inev.ie);
+
            *finish = X_EVENT_GOTO_OUT;
             goto done;
           }
@@ -18287,6 +18536,19 @@ handle_one_xevent (struct x_display_info *dpyinfo,
            }
        }
 
+      if (f && event->xproperty.atom == dpyinfo->Xatom_wm_state
+         && !FRAME_X_EMBEDDED_P (f) && !FRAME_PARENT_FRAME (f))
+       /* Handle WM_STATE.  We use this to clear the iconified flag
+          on a frame if it is set.
+
+          GTK builds ignore deiconifying frames on FocusIn or Expose
+          by default, and cannot wait for the window manager to
+          remove _NET_WM_STATE_HIDDEN, as it is ambiguous when not
+          set.  Handling UnmapNotify also checks for
+          _NET_WM_STATE_HIDDEN, and thus suffers from the same
+          problem.  */
+       x_handle_wm_state (f, &inev.ie);
+
       if (f && FRAME_X_OUTPUT (f)->alpha_identical_p
          && (event->xproperty.atom
              == dpyinfo->Xatom_net_wm_window_opacity))
@@ -18416,6 +18678,24 @@ handle_one_xevent (struct x_display_info *dpyinfo,
       f = x_top_window_to_frame (dpyinfo, event->xreparent.window);
       if (f)
         {
+#ifndef USE_GTK
+         if (FRAME_OUTPUT_DATA (f)->parent_desc
+             && FRAME_X_EMBEDDED_P (f))
+           {
+             /* The frame's embedder was destroyed; mark the frame as
+                no longer embedded, and map the frame.  An
+                UnmapNotify event must have previously been received
+                during the start of save-set processing.  */
+
+             FRAME_X_OUTPUT (f)->explicit_parent = false;
+
+             /* Remove the leftover XEMBED_INFO property.  */
+             XDeleteProperty (dpyinfo->display, FRAME_OUTER_WINDOW (f),
+                              dpyinfo->Xatom_XEMBED_INFO);
+             x_make_frame_visible (f);
+           }
+#endif
+
          /* Maybe we shouldn't set this for child frames ??  */
          f->output_data.x->parent_desc = event->xreparent.parent;
 
@@ -18443,11 +18723,9 @@ handle_one_xevent (struct x_display_info *dpyinfo,
              Window root;
              unsigned int dummy_uint;
 
-             block_input ();
              XGetGeometry (FRAME_X_DISPLAY (f), FRAME_OUTER_WINDOW (f),
                            &root, &f->left_pos, &f->top_pos,
                            &dummy_uint, &dummy_uint, &dummy_uint, &dummy_uint);
-             unblock_input ();
            }
 
           x_set_frame_alpha (f);
@@ -18472,7 +18750,6 @@ handle_one_xevent (struct x_display_info *dpyinfo,
         {
           if (!FRAME_VISIBLE_P (f))
             {
-              block_input ();
              /* By default, do not set the frame's visibility here, see
                 
https://lists.gnu.org/archive/html/emacs-devel/2017-02/msg00133.html.
                 The default behavior can be overridden by setting
@@ -18491,7 +18768,6 @@ handle_one_xevent (struct x_display_info *dpyinfo,
 #endif
               f->output_data.x->has_been_visible = true;
               SET_FRAME_GARBAGED (f);
-              unblock_input ();
             }
           else if (FRAME_GARBAGED_P (f))
            {
@@ -18858,6 +19134,13 @@ handle_one_xevent (struct x_display_info *dpyinfo,
             `event' itself.  */
          XKeyEvent xkey = event->xkey;
 
+         if (event->xkey.window == FRAME_X_WINDOW (f))
+           /* See the comment above x_compute_root_window_offset for
+              why this optimization is performed.  */
+           x_compute_root_window_offset (f, event->xkey.x_root,
+                                         event->xkey.y_root,
+                                         event->xkey.x, event->xkey.y);
+
 #ifdef HAVE_XINPUT2
          Time pending_keystroke_time;
          struct xi_device_t *source;
@@ -18916,7 +19199,7 @@ handle_one_xevent (struct x_display_info *dpyinfo,
                                         &xkey, (char *) copy_bufptr,
                                         copy_bufsiz, &keysym,
                                         &status_return);
-             coding = Qnil;
+             coding = FRAME_X_XIM_CODING (f);
               if (status_return == XBufferOverflow)
                 {
                   copy_bufsiz = nbytes + 1;
@@ -19262,6 +19545,18 @@ handle_one_xevent (struct x_display_info *dpyinfo,
       x_display_set_last_user_time (dpyinfo, event->xcrossing.time,
                                    event->xcrossing.send_event);
 
+#ifdef HAVE_XINPUT2
+      /* For whatever reason, the X server continues to deliver
+        EnterNotify and LeaveNotify events despite us selecting for
+        related XI_Enter and XI_Leave events.  It's not just our
+        problem, since windows created by "xinput test-xi2" suffer
+        from the same defect.  Simply ignore all such events while
+        the input extension is enabled.  (bug#57468) */
+
+      if (dpyinfo->supports_xi2)
+       goto OTHER;
+#endif
+
       if (x_top_window_to_frame (dpyinfo, event->xcrossing.window))
        x_detect_focus_change (dpyinfo, any, event, &inev.ie);
 
@@ -19292,6 +19587,16 @@ handle_one_xevent (struct x_display_info *dpyinfo,
 
       f = any;
 
+      if (f && event->xcrossing.window == FRAME_X_WINDOW (f))
+       x_compute_root_window_offset (f, event->xcrossing.x_root,
+                                     event->xcrossing.y_root,
+                                     event->xcrossing.x,
+                                     event->xcrossing.y);
+
+      /* The code below relies on the first several fields of
+        XCrossingEvent being laid out the same way as
+        XMotionEvent.  */
+
       if (f && x_mouse_click_focus_ignore_position)
        {
          ignore_next_mouse_click_timeout = (event->xmotion.time
@@ -19363,6 +19668,18 @@ handle_one_xevent (struct x_display_info *dpyinfo,
       x_display_set_last_user_time (dpyinfo, event->xcrossing.time,
                                    event->xcrossing.send_event);
 
+#ifdef HAVE_XINPUT2
+      /* For whatever reason, the X server continues to deliver
+        EnterNotify and LeaveNotify events despite us selecting for
+        related XI_Enter and XI_Leave events.  It's not just our
+        problem, since windows created by "xinput test-xi2" suffer
+        from the same defect.  Simply ignore all such events while
+        the input extension is enabled.  (bug#57468) */
+
+      if (dpyinfo->supports_xi2)
+       goto OTHER;
+#endif
+
 #ifdef HAVE_XWIDGETS
       {
        struct xwidget_view *xvw = xwidget_view_from_window 
(event->xcrossing.window);
@@ -19388,14 +19705,7 @@ handle_one_xevent (struct x_display_info *dpyinfo,
 #else
       f = x_top_window_to_frame (dpyinfo, event->xcrossing.window);
 #endif
-#if defined USE_X_TOOLKIT && defined HAVE_XINPUT2 && !defined USE_MOTIF
-      /* The XI2 event mask is set on the frame widget, so this event
-        likely originates from the shell widget, which we aren't
-        interested in.  (But don't ignore this on Motif, since we
-        want to clear the mouse face when a popup is active.)  */
-      if (dpyinfo->supports_xi2)
-       f = NULL;
-#endif
+
       if (f)
         {
          /* Now clear dpyinfo->last_mouse_motion_frame, or
@@ -19431,6 +19741,13 @@ handle_one_xevent (struct x_display_info *dpyinfo,
                    || EQ (track_mouse, Qdropping))
                   && gui_mouse_grabbed (dpyinfo)))
            do_help = -1;
+
+         if (event->xcrossing.window == FRAME_X_WINDOW (f))
+           x_compute_root_window_offset (f, event->xcrossing.x_root,
+                                         event->xcrossing.y_root,
+                                         event->xcrossing.x,
+                                         event->xcrossing.y);
+
         }
 #ifdef USE_GTK
       /* See comment in EnterNotify above */
@@ -19474,6 +19791,14 @@ handle_one_xevent (struct x_display_info *dpyinfo,
 
        f = mouse_or_wdesc_frame (dpyinfo, event->xmotion.window);
 
+       if (f && event->xmotion.window == FRAME_X_WINDOW (f))
+         /* See the comment above x_compute_root_window_offset for
+            why this optimization is performed.  */
+         x_compute_root_window_offset (f, event->xmotion.x_root,
+                                       event->xmotion.y_root,
+                                       event->xmotion.x,
+                                       event->xmotion.y);
+
        if (x_dnd_in_progress
            /* Handle these events normally if the recursion
               level is higher than when the drag-and-drop
@@ -19548,7 +19873,8 @@ handle_one_xevent (struct x_display_info *dpyinfo,
                    if (x_dnd_last_seen_window != None
                        && x_dnd_last_protocol_version != -1
                        && x_dnd_last_seen_window != FRAME_OUTER_WINDOW 
(x_dnd_frame))
-                     x_dnd_send_leave (x_dnd_frame, x_dnd_last_seen_window);
+                     x_dnd_send_leave (x_dnd_frame, x_dnd_last_seen_window,
+                                       x_dnd_last_seen_toplevel);
                    else if (x_dnd_last_seen_window != None
                             && XM_DRAG_STYLE_IS_DYNAMIC 
(x_dnd_last_motif_style)
                             && !x_dnd_disable_motif_drag
@@ -19579,8 +19905,6 @@ handle_one_xevent (struct x_display_info *dpyinfo,
                    x_dnd_waiting_for_finish = false;
                    target = None;
                  }
-
-               x_dnd_last_seen_toplevel = toplevel;
              }
 
            if (target != x_dnd_last_seen_window)
@@ -19588,7 +19912,8 @@ handle_one_xevent (struct x_display_info *dpyinfo,
                if (x_dnd_last_seen_window != None
                    && x_dnd_last_protocol_version != -1
                    && x_dnd_last_seen_window != FRAME_OUTER_WINDOW 
(x_dnd_frame))
-                 x_dnd_send_leave (x_dnd_frame, x_dnd_last_seen_window);
+                 x_dnd_send_leave (x_dnd_frame, x_dnd_last_seen_window,
+                                   x_dnd_last_seen_toplevel);
                else if (x_dnd_last_seen_window != None
                         && XM_DRAG_STYLE_IS_DYNAMIC (x_dnd_last_motif_style)
                         && x_dnd_disable_motif_drag
@@ -19631,6 +19956,7 @@ handle_one_xevent (struct x_display_info *dpyinfo,
                  }
 
                x_dnd_action = None;
+               x_dnd_last_seen_toplevel = toplevel;
                x_dnd_last_seen_window = target;
                x_dnd_last_protocol_version = target_proto;
                x_dnd_last_motif_style = motif_style;
@@ -19638,6 +19964,7 @@ handle_one_xevent (struct x_display_info *dpyinfo,
 
                if (target != None && x_dnd_last_protocol_version != -1)
                  x_dnd_send_enter (x_dnd_frame, target,
+                                   x_dnd_last_seen_toplevel,
                                    x_dnd_last_protocol_version);
                else if (target != None && XM_DRAG_STYLE_IS_DYNAMIC 
(x_dnd_last_motif_style)
                         && !x_dnd_disable_motif_drag)
@@ -19658,6 +19985,8 @@ handle_one_xevent (struct x_display_info *dpyinfo,
                                                       target, &emsg);
                  }
              }
+           else
+             x_dnd_last_seen_toplevel = toplevel;
 
            if (x_dnd_last_window_is_frame && target != None)
              x_dnd_note_self_position (dpyinfo, target,
@@ -19665,6 +19994,7 @@ handle_one_xevent (struct x_display_info *dpyinfo,
                                        event->xbutton.y_root);
            else if (x_dnd_last_protocol_version != -1 && target != None)
              x_dnd_send_position (x_dnd_frame, target,
+                                  x_dnd_last_seen_toplevel,
                                   x_dnd_last_protocol_version,
                                   event->xmotion.x_root,
                                   event->xmotion.y_root,
@@ -19733,10 +20063,8 @@ handle_one_xevent (struct x_display_info *dpyinfo,
 
                if (xmotion.window != FRAME_X_WINDOW (f))
                  {
-                   XTranslateCoordinates (FRAME_X_DISPLAY (f),
-                                          xmotion.window, FRAME_X_WINDOW (f),
-                                          xmotion.x, xmotion.y, &xmotion.x,
-                                          &xmotion.y, &xmotion.subwindow);
+                   x_translate_coordinates (f, xmotion.x_root, xmotion.y_root,
+                                            &xmotion.x, &xmotion.y);
                    xmotion.window = FRAME_X_WINDOW (f);
                  }
 
@@ -19959,17 +20287,21 @@ handle_one_xevent (struct x_display_info *dpyinfo,
 #endif
 
       f = x_top_window_to_frame (dpyinfo, configureEvent.xconfigure.window);
+
+      /* This means we can no longer be certain of the root window
+        coordinates of any's edit window.  */
+      if (any)
+       FRAME_X_OUTPUT (any)->window_offset_certain_p = false;
+
       /* Unfortunately, we need to call x_drop_xrender_surfaces for
          _all_ ConfigureNotify events, otherwise we miss some and
          flicker.  Don't try to optimize these calls by looking only
          for size changes: that's not sufficient.  We miss some
          surface invalidations and flicker.  */
-      block_input ();
 #ifdef HAVE_XDBE
       if (f && FRAME_X_DOUBLE_BUFFERED_P (f))
         x_drop_xrender_surfaces (f);
 #endif
-      unblock_input ();
 #if defined USE_CAIRO && !defined USE_GTK
       if (f)
        x_cr_update_surface_desired_size (f, configureEvent.xconfigure.width,
@@ -19999,10 +20331,8 @@ handle_one_xevent (struct x_display_info *dpyinfo,
               f->new_width, f->new_height);
 
 #ifdef HAVE_XDBE
-         block_input ();
           if (FRAME_X_DOUBLE_BUFFERED_P (f))
             x_drop_xrender_surfaces (f);
-          unblock_input ();
 #endif
           xg_frame_resized (f, configureEvent.xconfigure.width,
                             configureEvent.xconfigure.height);
@@ -20092,11 +20422,9 @@ handle_one_xevent (struct x_display_info *dpyinfo,
                  Window root;
                  unsigned int dummy_uint;
 
-                 block_input ();
                  XGetGeometry (FRAME_X_DISPLAY (f), FRAME_OUTER_WINDOW (f),
                                &root, &f->left_pos, &f->top_pos,
                                &dummy_uint, &dummy_uint, &dummy_uint, 
&dummy_uint);
-                 unblock_input ();
                }
 
              if (!FRAME_TOOLTIP_P (f)
@@ -20194,6 +20522,14 @@ handle_one_xevent (struct x_display_info *dpyinfo,
          {
            f = mouse_or_wdesc_frame (dpyinfo, event->xbutton.window);
 
+           if (f && event->xbutton.window == FRAME_X_WINDOW (f))
+             /* See the comment above x_compute_root_window_offset
+                for why this optimization is performed.  */
+             x_compute_root_window_offset (f, event->xbutton.x_root,
+                                           event->xbutton.y_root,
+                                           event->xbutton.x,
+                                           event->xbutton.y);
+
            if (event->type == ButtonPress)
              {
                x_display_set_last_user_time (dpyinfo, event->xbutton.time,
@@ -20226,6 +20562,7 @@ handle_one_xevent (struct x_display_info *dpyinfo,
                else if (x_dnd_last_protocol_version != -1)
                  x_dnd_send_position (x_dnd_frame,
                                       x_dnd_last_seen_window,
+                                      x_dnd_last_seen_toplevel,
                                       x_dnd_last_protocol_version,
                                       event->xbutton.x_root,
                                       event->xbutton.y_root,
@@ -20273,11 +20610,12 @@ handle_one_xevent (struct x_display_info *dpyinfo,
                    else if (x_dnd_last_seen_window != None
                        && x_dnd_last_protocol_version != -1)
                      {
-                       x_dnd_pending_finish_target = x_dnd_last_seen_window;
+                       x_dnd_pending_finish_target = x_dnd_last_seen_toplevel;
                        x_dnd_waiting_for_finish_proto = 
x_dnd_last_protocol_version;
 
                        x_dnd_waiting_for_finish
                          = x_dnd_do_drop (x_dnd_last_seen_window,
+                                          x_dnd_last_seen_toplevel,
                                           x_dnd_last_protocol_version);
                        x_dnd_finish_display = dpyinfo->display;
                      }
@@ -20365,6 +20703,15 @@ handle_one_xevent (struct x_display_info *dpyinfo,
        dpyinfo->last_mouse_glyph_frame = NULL;
 
        f = mouse_or_wdesc_frame (dpyinfo, event->xbutton.window);
+
+       if (f && event->xbutton.window == FRAME_X_WINDOW (f))
+         /* See the comment above x_compute_root_window_offset
+            for why this optimization is performed.  */
+         x_compute_root_window_offset (f, event->xbutton.x_root,
+                                       event->xbutton.y_root,
+                                       event->xbutton.x,
+                                       event->xbutton.y);
+
        if (f && event->xbutton.type == ButtonPress
            && !popup_activated ()
            && !x_window_to_scroll_bar (event->xbutton.display,
@@ -20379,12 +20726,10 @@ handle_one_xevent (struct x_display_info *dpyinfo,
 
            if (FRAME_PARENT_FRAME (f) || (hf && frame_ancestor_p (f, hf)))
              {
-               block_input ();
                XSetInputFocus (FRAME_X_DISPLAY (f), FRAME_OUTER_WINDOW (f),
                                RevertToParent, event->xbutton.time);
                if (FRAME_PARENT_FRAME (f))
                  XRaiseWindow (FRAME_X_DISPLAY (f), FRAME_OUTER_WINDOW (f));
-               unblock_input ();
              }
          }
 
@@ -20615,6 +20960,15 @@ handle_one_xevent (struct x_display_info *dpyinfo,
          == dpyinfo->net_supported_window)
        dpyinfo->net_supported_window = None;
 
+      if (event->xdestroywindow.window
+         == dpyinfo->motif_drag_window)
+       /* We get DestroyNotify events for the drag window because
+          x_special_window_exists_p selects for structure
+          notification.  The drag window is not supposed to go away
+          but not all clients obey that requirement when setting the
+          drag window property.  */
+       dpyinfo->motif_drag_window = None;
+
       xft_settings_event (dpyinfo, event);
       break;
 
@@ -20771,8 +21125,20 @@ handle_one_xevent (struct x_display_info *dpyinfo,
                any = x_any_window_to_frame (dpyinfo, enter->event);
 
 #ifdef HAVE_XINPUT2_1
-             xi_reset_scroll_valuators_for_device_id (dpyinfo, enter->deviceid,
-                                                      true);
+             /* xfwm4 selects for button events on the frame window,
+                resulting in passive grabs being generated along with
+                the delivery of emulated button events; this then
+                interferes with scrolling, since device valuators
+                will constantly be reset as the crossing events
+                related to those grabs arrive.  The only way to
+                remedy this is to never reset scroll valuators on a
+                grab-related crossing event.  (bug#57476) */
+             if (enter->mode != XINotifyUngrab
+                 && enter->mode != XINotifyGrab
+                 && enter->mode != XINotifyPassiveGrab
+                 && enter->mode != XINotifyPassiveUngrab)
+               xi_reset_scroll_valuators_for_device_id (dpyinfo, 
enter->deviceid,
+                                                        true);
 #endif
 
              {
@@ -20792,6 +21158,9 @@ handle_one_xevent (struct x_display_info *dpyinfo,
 
              f = any;
 
+             if (f && enter->event == FRAME_X_WINDOW (f))
+               xi_compute_root_window_offset_enter (f, enter);
+
              if (f && x_mouse_click_focus_ignore_position)
                {
                  ignore_next_mouse_click_timeout = (enter->time
@@ -20888,7 +21257,20 @@ handle_one_xevent (struct x_display_info *dpyinfo,
                 moves out of a frame (and not into one of its
                 children, which we know about).  */
 #ifdef HAVE_XINPUT2_1
-             if (leave->detail != XINotifyInferior && any)
+             if (leave->detail != XINotifyInferior && any
+                 /* xfwm4 selects for button events on the frame
+                    window, resulting in passive grabs being
+                    generated along with the delivery of emulated
+                    button events; this then interferes with
+                    scrolling, since device valuators will constantly
+                    be reset as the crossing events related to those
+                    grabs arrive.  The only way to remedy this is to
+                    never reset scroll valuators on a grab-related
+                    crossing event.  (bug#57476) */
+                 && leave->mode != XINotifyUngrab
+                 && leave->mode != XINotifyGrab
+                 && leave->mode != XINotifyPassiveUngrab
+                 && leave->mode != XINotifyPassiveGrab)
                xi_reset_scroll_valuators_for_device_id (dpyinfo,
                                                         leave->deviceid, 
false);
 #endif
@@ -20926,7 +21308,11 @@ handle_one_xevent (struct x_display_info *dpyinfo,
                 just looks up a top window on Xt builds.  */
 
 #ifdef HAVE_XINPUT2_1
-             if (leave->detail != XINotifyInferior && f)
+             if (leave->detail != XINotifyInferior && f
+                 && leave->mode != XINotifyUngrab
+                 && leave->mode != XINotifyGrab
+                 && leave->mode != XINotifyPassiveUngrab
+                 && leave->mode != XINotifyPassiveGrab)
                xi_reset_scroll_valuators_for_device_id (dpyinfo,
                                                         leave->deviceid, 
false);
 #endif
@@ -20971,6 +21357,9 @@ handle_one_xevent (struct x_display_info *dpyinfo,
                            || EQ (track_mouse, Qdropping))
                           && gui_mouse_grabbed (dpyinfo)))
                    do_help = -1;
+
+                 if (f && leave->event == FRAME_X_WINDOW (f))
+                   xi_compute_root_window_offset_enter (f, leave);
                }
 #ifdef USE_GTK
              /* See comment in EnterNotify above */
@@ -21014,8 +21403,6 @@ handle_one_xevent (struct x_display_info *dpyinfo,
                goto XI_OTHER;
 #endif
 
-             Window dummy;
-
 #ifdef HAVE_XINPUT2_1
 #ifdef HAVE_XWIDGETS
              struct xwidget_view *xv = xwidget_view_from_window (xev->event);
@@ -21093,11 +21480,10 @@ handle_one_xevent (struct x_display_info *dpyinfo,
                                  real_y = lrint (xev->event_y + bar->top);
                                }
                              else
-                               XTranslateCoordinates (dpyinfo->display,
-                                                      xev->event, 
FRAME_X_WINDOW (f),
-                                                      lrint (xev->event_x),
-                                                      lrint (xev->event_y),
-                                                      &real_x, &real_y, 
&dummy);
+                               x_translate_coordinates (f,
+                                                        lrint (xev->root_x),
+                                                        lrint (xev->root_y),
+                                                        &real_x, &real_y);
                            }
                          else
                            {
@@ -21299,6 +21685,11 @@ handle_one_xevent (struct x_display_info *dpyinfo,
 
              f = mouse_or_wdesc_frame (dpyinfo, xev->event);
 
+             if (f && xev->event == FRAME_X_WINDOW (f))
+               /* See the comment above x_compute_root_window_offset
+                  for why this optimization is performed.  */
+               xi_compute_root_window_offset (f, xev);
+
              if (x_dnd_in_progress
                  /* Handle these events normally if the recursion
                     level is higher than when the drag-and-drop
@@ -21333,26 +21724,26 @@ handle_one_xevent (struct x_display_info *dpyinfo,
                      /* Also remember the mouse glyph and set
                         mouse_moved.  */
                      if (f != dpyinfo->last_mouse_glyph_frame
-                         || xev->event_x < r->x
-                         || xev->event_x >= r->x + r->width
-                         || xev->event_y < r->y
-                         || xev->event_y >= r->y + r->height)
+                         || lrint (xev->event_x) < r->x
+                         || lrint (xev->event_x) >= r->x + r->width
+                         || lrint (xev->event_y) < r->y
+                         || lrint (xev->event_y) >= r->y + r->height)
                        {
                          f->mouse_moved = true;
                          f->last_mouse_device = (source ? source->name
                                                  : Qnil);
                          dpyinfo->last_mouse_scroll_bar = NULL;
 
-                         remember_mouse_glyph (f, xev->event_x,
-                                               xev->event_y, r);
+                         remember_mouse_glyph (f, lrint (xev->event_x),
+                                               lrint (xev->event_y), r);
                          dpyinfo->last_mouse_glyph_frame = f;
                        }
                    }
 
                  if (xev->root == dpyinfo->root_window)
                    target = x_dnd_get_target_window (dpyinfo,
-                                                     xev->root_x,
-                                                     xev->root_y,
+                                                     lrint (xev->root_x),
+                                                     lrint (xev->root_y),
                                                      &target_proto,
                                                      &motif_style,
                                                      &toplevel,
@@ -21375,7 +21766,8 @@ handle_one_xevent (struct x_display_info *dpyinfo,
                          if (x_dnd_last_seen_window != None
                              && x_dnd_last_protocol_version != -1
                              && x_dnd_last_seen_window != FRAME_OUTER_WINDOW 
(x_dnd_frame))
-                           x_dnd_send_leave (x_dnd_frame, 
x_dnd_last_seen_window);
+                           x_dnd_send_leave (x_dnd_frame, 
x_dnd_last_seen_window,
+                                             x_dnd_last_seen_toplevel);
                          else if (x_dnd_last_seen_window != None
                                   && XM_DRAG_STYLE_IS_DYNAMIC 
(x_dnd_last_motif_style)
                                   && !x_dnd_disable_motif_drag
@@ -21406,8 +21798,6 @@ handle_one_xevent (struct x_display_info *dpyinfo,
                          x_dnd_waiting_for_finish = false;
                          target = None;
                        }
-
-                     x_dnd_last_seen_toplevel = toplevel;
                    }
 
                  if (target != x_dnd_last_seen_window)
@@ -21415,7 +21805,8 @@ handle_one_xevent (struct x_display_info *dpyinfo,
                      if (x_dnd_last_seen_window != None
                          && x_dnd_last_protocol_version != -1
                          && x_dnd_last_seen_window != FRAME_OUTER_WINDOW 
(x_dnd_frame))
-                       x_dnd_send_leave (x_dnd_frame, x_dnd_last_seen_window);
+                       x_dnd_send_leave (x_dnd_frame, x_dnd_last_seen_window,
+                                         x_dnd_last_seen_toplevel);
                      else if (x_dnd_last_seen_window != None
                               && XM_DRAG_STYLE_IS_DYNAMIC 
(x_dnd_last_motif_style)
                               && !x_dnd_disable_motif_drag
@@ -21460,6 +21851,7 @@ handle_one_xevent (struct x_display_info *dpyinfo,
                        }
 
                      x_dnd_action = None;
+                     x_dnd_last_seen_toplevel = toplevel;
                      x_dnd_last_seen_window = target;
                      x_dnd_last_protocol_version = target_proto;
                      x_dnd_last_motif_style = motif_style;
@@ -21467,6 +21859,7 @@ handle_one_xevent (struct x_display_info *dpyinfo,
 
                      if (target != None && x_dnd_last_protocol_version != -1)
                        x_dnd_send_enter (x_dnd_frame, target,
+                                         x_dnd_last_seen_toplevel,
                                          x_dnd_last_protocol_version);
                      else if (target != None && XM_DRAG_STYLE_IS_DYNAMIC 
(x_dnd_last_motif_style)
                               && !x_dnd_disable_motif_drag)
@@ -21487,17 +21880,22 @@ handle_one_xevent (struct x_display_info *dpyinfo,
                                                             target, &emsg);
                        }
                    }
+                 else
+                   x_dnd_last_seen_toplevel = toplevel;
 
                  if (x_dnd_last_window_is_frame && target != None)
                    x_dnd_note_self_position (dpyinfo, target,
-                                             xev->root_x, xev->root_y);
+                                             lrint (xev->root_x),
+                                             lrint (xev->root_y));
                  else if (x_dnd_last_protocol_version != -1 && target != None)
                    {
                      dnd_state = xi_convert_event_state (xev);
 
                      x_dnd_send_position (x_dnd_frame, target,
+                                          x_dnd_last_seen_toplevel,
                                           x_dnd_last_protocol_version,
-                                          xev->root_x, xev->root_y,
+                                          lrint (xev->root_x),
+                                          lrint (xev->root_y),
                                           x_dnd_selection_timestamp,
                                           x_dnd_wanted_action, 0,
                                           dnd_state);
@@ -21540,9 +21938,9 @@ handle_one_xevent (struct x_display_info *dpyinfo,
                {
                  if (xev->event != FRAME_X_WINDOW (f))
                    {
-                     XTranslateCoordinates (FRAME_X_DISPLAY (f),
-                                            xev->event, FRAME_X_WINDOW (f),
-                                            ev.x, ev.y, &ev.x, &ev.y, &dummy);
+                     x_translate_coordinates (f, lrint (xev->root_x),
+                                              lrint (xev->root_y),
+                                              &ev.x, &ev.y);
                      ev.window = FRAME_X_WINDOW (f);
                    }
 
@@ -21648,6 +22046,12 @@ handle_one_xevent (struct x_display_info *dpyinfo,
                  f = mouse_or_wdesc_frame (dpyinfo, xev->event);
                  device = xi_device_from_id (dpyinfo, xev->deviceid);
 
+                 if (f && xev->event == FRAME_X_WINDOW (f))
+                   /* See the comment above
+                      x_compute_root_window_offset for why this
+                      optimization is performed.  */
+                   xi_compute_root_window_offset (f, xev);
+
                  /* Don't track grab status for emulated pointer
                     events, because they are ignored by the regular
                     mouse click processing code.  */
@@ -21705,15 +22109,18 @@ handle_one_xevent (struct x_display_info *dpyinfo,
 #endif
                          x_dnd_note_self_wheel (dpyinfo,
                                                 x_dnd_last_seen_window,
-                                                xev->root_x, xev->root_y,
+                                                lrint (xev->root_x),
+                                                lrint (xev->root_y),
                                                 xev->detail, dnd_state,
                                                 xev->time);
                        }
                      else
                        x_dnd_send_position (x_dnd_frame,
                                             x_dnd_last_seen_window,
+                                            x_dnd_last_seen_toplevel,
                                             x_dnd_last_protocol_version,
-                                            xev->root_x, xev->root_y,
+                                            lrint (xev->root_x),
+                                            lrint (xev->root_y),
                                             xev->time, x_dnd_wanted_action,
                                             xev->detail, dnd_state);
 
@@ -21756,16 +22163,18 @@ handle_one_xevent (struct x_display_info *dpyinfo,
                            {
                              x_dnd_waiting_for_finish = false;
                              x_dnd_note_self_drop (dpyinfo, 
x_dnd_last_seen_window,
-                                                   xev->root_x, xev->root_y, 
xev->time);
+                                                   lrint (xev->root_x),
+                                                   lrint (xev->root_y), 
xev->time);
                            }
                          else if (x_dnd_last_seen_window != None
                                   && x_dnd_last_protocol_version != -1)
                            {
-                             x_dnd_pending_finish_target = 
x_dnd_last_seen_window;
+                             x_dnd_pending_finish_target = 
x_dnd_last_seen_toplevel;
                              x_dnd_waiting_for_finish_proto = 
x_dnd_last_protocol_version;
 
                              x_dnd_waiting_for_finish
                                = x_dnd_do_drop (x_dnd_last_seen_window,
+                                                x_dnd_last_seen_toplevel,
                                                 x_dnd_last_protocol_version);
                              x_dnd_finish_display = dpyinfo->display;
                            }
@@ -21831,12 +22240,14 @@ handle_one_xevent (struct x_display_info *dpyinfo,
                                x_dnd_send_unsupported_drop (dpyinfo, 
(x_dnd_last_seen_toplevel != None
                                                                       ? 
x_dnd_last_seen_toplevel
                                                                       : 
x_dnd_last_seen_window),
-                                                            xev->root_x, 
xev->root_y, xev->time);
+                                                            lrint 
(xev->root_x),
+                                                            lrint 
(xev->root_y), xev->time);
                            }
                          else if (x_dnd_last_seen_toplevel != None)
                            x_dnd_send_unsupported_drop (dpyinfo,
                                                         
x_dnd_last_seen_toplevel,
-                                                        xev->root_x, 
xev->root_y,
+                                                        lrint (xev->root_x),
+                                                        lrint (xev->root_y),
                                                         xev->time);
 
                          x_dnd_last_protocol_version = -1;
@@ -21972,6 +22383,8 @@ handle_one_xevent (struct x_display_info *dpyinfo,
              bv.type = xev->evtype == XI_ButtonPress ? ButtonPress : 
ButtonRelease;
              bv.x = lrint (xev->event_x);
              bv.y = lrint (xev->event_y);
+             bv.x_root = lrint (xev->root_x);
+             bv.y_root = lrint (xev->root_y);
              bv.window = xev->event;
              bv.state = xi_convert_event_state (xev);
              bv.time = xev->time;
@@ -21980,6 +22393,11 @@ handle_one_xevent (struct x_display_info *dpyinfo,
 
              f = mouse_or_wdesc_frame (dpyinfo, xev->event);
 
+             if (f && xev->event == FRAME_X_WINDOW (f))
+               /* See the comment above x_compute_root_window_offset
+                  for why this optimization is performed.  */
+               xi_compute_root_window_offset (f, xev);
+
              if (f && xev->evtype == XI_ButtonPress
                  && !popup_activated ()
                  && !x_window_to_scroll_bar (dpyinfo->display, xev->event, 2)
@@ -21993,7 +22411,6 @@ handle_one_xevent (struct x_display_info *dpyinfo,
 
                  if (FRAME_PARENT_FRAME (f) || (hf && frame_ancestor_p (f, 
hf)))
                    {
-                     block_input ();
 #if defined HAVE_GTK3 || (!defined USE_GTK && !defined USE_X_TOOLKIT)
                      if (device)
                        {
@@ -22016,7 +22433,6 @@ handle_one_xevent (struct x_display_info *dpyinfo,
 #endif
                      if (FRAME_PARENT_FRAME (f))
                        XRaiseWindow (FRAME_X_DISPLAY (f), FRAME_OUTER_WINDOW 
(f));
-                     unblock_input ();
                    }
                }
 
@@ -22033,9 +22449,8 @@ handle_one_xevent (struct x_display_info *dpyinfo,
 #ifdef USE_GTK
              if (!f)
                {
-                 int real_x = lrint (xev->event_x);
-                 int real_y = lrint (xev->event_y);
-                 Window child;
+                 int real_x = lrint (xev->root_x);
+                 int real_y = lrint (xev->root_y);
 
                  f = x_any_window_to_frame (dpyinfo, xev->event);
 
@@ -22044,9 +22459,8 @@ handle_one_xevent (struct x_display_info *dpyinfo,
                      if (xev->evtype == XI_ButtonRelease)
                        {
                          if (FRAME_X_WINDOW (f) != xev->event)
-                           XTranslateCoordinates (dpyinfo->display, xev->event,
-                                                  FRAME_X_WINDOW (f), real_x,
-                                                  real_y, &real_x, &real_y, 
&child);
+                           x_translate_coordinates (f, real_x, real_y,
+                                                    &real_x, &real_y);
 
                          if (xev->detail <= 5)
                            inev.ie.kind = WHEEL_EVENT;
@@ -22311,6 +22725,11 @@ handle_one_xevent (struct x_display_info *dpyinfo,
 
              f = x_any_window_to_frame (dpyinfo, xev->event);
 
+             if (f && xev->event == FRAME_X_WINDOW (f))
+               /* See the comment above x_compute_root_window_offset
+                  for why this optimization is performed.  */
+               xi_compute_root_window_offset (f, xev);
+
              /* GTK handles TAB events in an undesirable manner, so
                 keyboard events are always dropped.  But as a side
                 effect, the user time will no longer be set by GDK,
@@ -22469,7 +22888,7 @@ handle_one_xevent (struct x_display_info *dpyinfo,
                                                &xkey, (char *) copy_bufptr,
                                                copy_bufsiz, &keysym,
                                                &status_return);
-                     coding = Qnil;
+                     coding = FRAME_X_XIM_CODING (f);
 
                      if (status_return == XBufferOverflow)
                        {
@@ -22835,24 +23254,36 @@ handle_one_xevent (struct x_display_info *dpyinfo,
                          any_changed = true;
                        }
 
-                     x_catch_errors (dpyinfo->display);
-                     info = XIQueryDevice (dpyinfo->display, 
hev->info[i].deviceid,
-                                           &ndevices);
-                     x_uncatch_errors ();
+                     /* Under unknown circumstances, multiple
+                        XIDeviceEnabled events are sent at once,
+                        causing the device to be duplicated.  Check
+                        that the device doesn't exist before adding
+                        it.  */
 
-                     if (info && info->enabled)
+                     if (!xi_device_from_id (dpyinfo,
+                                             hev->info[i].deviceid))
                        {
-                         dpyinfo->devices
-                           = xrealloc (dpyinfo->devices, (sizeof 
*dpyinfo->devices
-                                                          * 
++dpyinfo->num_devices));
-                         memset (dpyinfo->devices + dpyinfo->num_devices - 1,
-                                 0, sizeof *dpyinfo->devices);
-                         device = &dpyinfo->devices[dpyinfo->num_devices - 1];
-                         xi_populate_device_from_info (device, info);
-                       }
+                         x_catch_errors (dpyinfo->display);
+                         info = XIQueryDevice (dpyinfo->display,
+                                               hev->info[i].deviceid,
+                                               &ndevices);
+                         x_uncatch_errors ();
 
-                     if (info)
-                       XIFreeDeviceInfo (info);
+                         if (info && info->enabled)
+                           {
+                             dpyinfo->devices
+                               = xrealloc (dpyinfo->devices,
+                                           (sizeof *dpyinfo->devices
+                                            * ++dpyinfo->num_devices));
+                             memset (dpyinfo->devices + dpyinfo->num_devices - 
1,
+                                     0, sizeof *dpyinfo->devices);
+                             device = &dpyinfo->devices[dpyinfo->num_devices - 
1];
+                             xi_populate_device_from_info (device, info);
+                           }
+
+                         if (info)
+                           XIFreeDeviceInfo (info);
+                       }
                    }
                  else if (hev->info[i].flags & XIDeviceDisabled)
                    disabled[n_disabled++] = hev->info[i].deviceid;
@@ -22938,6 +23369,11 @@ handle_one_xevent (struct x_display_info *dpyinfo,
 
              f = x_window_to_frame (dpyinfo, xev->event);
 
+             if (f)
+               /* See the comment above x_compute_root_window_offset
+                  for why this optimization is performed.  */
+               xi_compute_root_window_offset (f, xev);
+
 #ifdef HAVE_GTK3
              menu_bar_p = (f && FRAME_X_OUTPUT (f)->menubar_widget
                            && xg_event_is_for_menubar (f, event));
@@ -23126,8 +23562,12 @@ handle_one_xevent (struct x_display_info *dpyinfo,
 #endif
 
              any = x_window_to_frame (dpyinfo, pev->event);
+
              if (any)
                {
+                 if (pev->event == FRAME_X_WINDOW (any))
+                   xi_compute_root_window_offset_pinch (any, pev);
+
                  inev.ie.kind = PINCH_EVENT;
                  inev.ie.modifiers = x_x_to_emacs_modifiers 
(FRAME_DISPLAY_INFO (any),
                                                              
pev->mods.effective);
@@ -23521,7 +23961,6 @@ handle_one_xevent (struct x_display_info *dpyinfo,
 #endif
     OTHER:
 #ifdef USE_X_TOOLKIT
-      block_input ();
       if (*finish != X_EVENT_DROP)
        {
          /* Ignore some obviously bogus ConfigureNotify events that
@@ -23538,7 +23977,6 @@ handle_one_xevent (struct x_display_info *dpyinfo,
 #endif
            }
        }
-      unblock_input ();
 #endif /* USE_X_TOOLKIT */
 #if defined USE_GTK && !defined HAVE_GTK3 && defined HAVE_XINPUT2
       if (*finish != X_EVENT_DROP && copy)
@@ -23560,12 +23998,6 @@ handle_one_xevent (struct x_display_info *dpyinfo,
       count++;
     }
 
-#ifdef USE_TOOLKIT_SCROLL_BARS
-  if (event->xany.type == ClientMessage
-      && inev.ie.kind == SCROLL_BAR_CLICK_EVENT)
-    x_unprotect_window_for_callback (dpyinfo);
-#endif
-
   if (do_help
       && !(hold_quit && hold_quit->kind != NO_EVENT))
     {
@@ -24705,7 +25137,8 @@ x_connection_closed (Display *dpy, const char 
*error_message, bool ioerror)
              if (x_dnd_last_seen_window != None
                  && x_dnd_last_protocol_version != -1)
                x_dnd_send_leave (x_dnd_frame,
-                                 x_dnd_last_seen_window);
+                                 x_dnd_last_seen_window,
+                                 x_dnd_last_seen_toplevel);
              else if (x_dnd_last_seen_window != None
                       && !XM_DRAG_STYLE_IS_DROP_ONLY (x_dnd_last_motif_style)
                       && x_dnd_last_motif_style != XM_DRAG_STYLE_NONE
@@ -25115,9 +25548,10 @@ xim_destroy_callback (XIM xim, XPointer client_data, 
XPointer call_data)
 static void
 xim_open_dpy (struct x_display_info *dpyinfo, char *resource_name)
 {
+#ifdef HAVE_XIM
   XIM xim;
+  const char *locale;
 
-#ifdef HAVE_XIM
   if (use_xim)
     {
       if (dpyinfo->xim)
@@ -25140,6 +25574,14 @@ xim_open_dpy (struct x_display_info *dpyinfo, char 
*resource_name)
          destroy.client_data = (XPointer)dpyinfo;
          XSetIMValues (xim, XNDestroyCallback, &destroy, NULL);
 #endif
+
+         locale = XLocaleOfIM (xim);
+
+         /* Now try to determine the coding system that should be
+            used.  locale is in Host Portable Character Encoding, and
+            as such can be passed to build_string as is.  */
+         dpyinfo->xim_coding = safe_call1 (Vx_input_coding_function,
+                                           build_string (locale));
        }
     }
 
@@ -26469,8 +26911,7 @@ x_ewmh_activate_frame (struct frame *f)
 
   dpyinfo = FRAME_DISPLAY_INFO (f);
 
-  if (FRAME_VISIBLE_P (f)
-      && x_wm_supports (f, dpyinfo->Xatom_net_active_window))
+  if (FRAME_VISIBLE_P (f))
     {
       /* See the documentation at
         https://specifications.freedesktop.org/wm-spec/wm-spec-latest.html
@@ -26530,14 +26971,23 @@ x_focus_frame (struct frame *f, bool noactivate)
     xembed_request_focus (f);
   else
     {
+      if (!noactivate
+         && x_wm_supports (f, dpyinfo->Xatom_net_active_window))
+       {
+         /* When window manager activation is possible, use it
+            instead.  The window manager is expected to perform any
+            necessary actions such as raising the frame, moving it to
+            the current workspace, and mapping it, etc, before moving
+            input focus to the frame.  */
+         x_ewmh_activate_frame (f);
+         return;
+       }
+
       /* Ignore any BadMatch error this request might result in.  */
       x_ignore_errors_for_next_request (dpyinfo);
       XSetInputFocus (FRAME_X_DISPLAY (f), FRAME_OUTER_WINDOW (f),
                      RevertToParent, CurrentTime);
       x_stop_ignoring_errors (dpyinfo);
-
-      if (!noactivate)
-       x_ewmh_activate_frame (f);
     }
 }
 
@@ -27169,6 +27619,13 @@ x_free_frame_resources (struct frame *f)
 #if defined HAVE_XSYNCTRIGGERFENCE && !defined USE_GTK && defined 
HAVE_CLOCK_GETTIME
       x_sync_free_fences (f);
 #endif
+
+#ifdef USE_TOOLKIT_SCROLL_BARS
+      /* Since the frame was destroyed, we can no longer guarantee
+        that scroll bar events will be received.  Clear
+        protected_windows.  */
+      dpyinfo->n_protected_windows = 0;
+#endif
     }
 
 #ifdef HAVE_GTK3
@@ -27194,6 +27651,16 @@ x_free_frame_resources (struct frame *f)
   if (f == hlinfo->mouse_face_mouse_frame)
     reset_mouse_highlight (hlinfo);
 
+#ifdef HAVE_XINPUT2
+  /* Consider a frame being unfocused with no following FocusIn event
+     while an older focus from another seat exists.  The client
+     pointer should then revert to the other seat, so handle potential
+     focus changes.  */
+
+  if (dpyinfo->supports_xi2)
+    xi_handle_focus_change (dpyinfo);
+#endif
+
   unblock_input ();
 }
 
@@ -27420,6 +27887,31 @@ x_get_atom_name (struct x_display_info *dpyinfo, Atom 
atom,
   return value;
 }
 
+#ifndef USE_GTK
+
+/* Set up XEmbed for F, and change its save set to handle the parent
+   being destroyed.  */
+
+bool
+x_embed_frame (struct x_display_info *dpyinfo, struct frame *f)
+{
+  bool rc;
+
+  x_catch_errors (dpyinfo->display);
+  /* Catch errors; the target window might no longer exist.  */
+  XReparentWindow (dpyinfo->display, FRAME_OUTER_WINDOW (f),
+                  FRAME_OUTPUT_DATA (f)->parent_desc, 0, 0);
+  rc = x_had_errors_p (dpyinfo->display);
+  x_uncatch_errors_after_check ();
+
+  if (rc)
+    return false;
+
+  return true;
+}
+
+#endif
+
 
 /* Setting window manager hints.  */
 
@@ -27452,8 +27944,11 @@ x_wm_set_size_hint (struct frame *f, long flags, bool 
user_position)
       eassert (XtIsWMShell (f->output_data.x->widget));
       shell = (WMShellWidget) f->output_data.x->widget;
 
-      shell->wm.size_hints.flags &= ~(PPosition | USPosition);
-      shell->wm.size_hints.flags |= flags & (PPosition | USPosition);
+      if (flags)
+       {
+         shell->wm.size_hints.flags &= ~(PPosition | USPosition);
+         shell->wm.size_hints.flags |= flags & (PPosition | USPosition);
+       }
 
       if (user_position)
        {
@@ -27885,7 +28380,7 @@ xi_select_hierarchy_events (struct x_display_info 
*dpyinfo)
    extension.
 
    Value is 0 if GTK was not built with the input extension, or if it
-   was explictly disabled, 1 if GTK enabled the input extension and
+   was explicitly disabled, 1 if GTK enabled the input extension and
    the version was successfully determined, and 2 if that information
    could not be determined.  */
 
@@ -28599,10 +29094,13 @@ x_term_init (Lisp_Object display_name, char 
*xrm_option, char *resource_name)
                          (RRScreenChangeNotifyMask
                           | RRCrtcChangeNotifyMask
                           | RROutputChangeNotifyMask
-                          /* Emacs doesn't actually need this, but GTK
-                             selects for it when the display is
+#ifdef USE_GTK
+                          /* Emacs doesn't actually need this, but
+                             GTK selects for it when the display is
                              initialized.  */
-                          | RROutputPropertyNotifyMask));
+                          | RROutputPropertyNotifyMask
+#endif
+                          ));
 
          dpyinfo->last_monitor_attributes_list
            = Fx_display_monitor_attributes_list (term);
@@ -29414,7 +29912,7 @@ mark_xterm (void)
     }
 
 #if defined HAVE_XINPUT2 || defined USE_TOOLKIT_SCROLL_BARS \
-  || defined HAVE_XRANDR || defined USE_GTK
+  || defined HAVE_XRANDR || defined USE_GTK || defined HAVE_X_I18N
   for (dpyinfo = x_display_list; dpyinfo; dpyinfo = dpyinfo->next)
     {
 #ifdef HAVE_XINPUT2
@@ -29427,6 +29925,9 @@ mark_xterm (void)
 #endif
 #if defined HAVE_XRANDR || defined USE_GTK
       mark_object (dpyinfo->last_monitor_attributes_list);
+#endif
+#if defined HAVE_X_I18N
+      mark_object (dpyinfo->xim_coding);
 #endif
     }
 #endif
@@ -29637,6 +30138,9 @@ syms_of_xterm (void)
   x_dnd_unsupported_drop_data = Qnil;
   staticpro (&x_dnd_unsupported_drop_data);
 
+  /* Used by x_cr_export_frames.  */
+  DEFSYM (Qconcat, "concat");
+
   DEFSYM (Qvendor_specific_keysyms, "vendor-specific-keysyms");
   DEFSYM (Qlatin_1, "latin-1");
   DEFSYM (Qnow, "now");
@@ -29953,4 +30457,18 @@ on the same display.
 In addition, when this variable is a list, only preserve the
 selections whose names are contained within.  */);
   Vx_auto_preserve_selections = list2 (QCLIPBOARD, QPRIMARY);
+
+  DEFVAR_LISP ("x-input-coding-system", Vx_input_coding_system,
+    doc: /* Coding system used for input from X input methods.
+If a symbol and non-nil, this is the coding system that will be used
+to decode input from X input methods.  It does not affect input from
+GTK native input methods enabled through `x-gtk-use-native-input'.  */);
+  Vx_input_coding_system = Qnil;
+
+  DEFVAR_LISP ("x-input-coding-function", Vx_input_coding_function,
+    doc: /* Function used to determine the coding system used by input methods.
+It should accept a single argument, a string describing the locale of
+the input method, and return a coding system that can decode keyboard
+input generated by said input method.  */);
+  Vx_input_coding_function = Qnil;
 }
diff --git a/src/xterm.h b/src/xterm.h
index a0ae3a330a..b68a234faa 100644
--- a/src/xterm.h
+++ b/src/xterm.h
@@ -580,6 +580,9 @@ struct x_display_info
   XIMStyles *xim_styles;
   struct xim_inst_t *xim_callback_data;
   XIMStyle preferred_xim_style;
+
+  /* The named coding system to use for this input method.  */
+  Lisp_Object xim_coding;
 #endif
 
   /* A cache mapping color names to RGB values.  */
@@ -656,7 +659,8 @@ struct x_display_info
     Xatom_net_wm_sync_request, Xatom_net_wm_sync_request_counter,
     Xatom_net_wm_sync_fences, Xatom_net_wm_frame_drawn, 
Xatom_net_wm_frame_timings,
     Xatom_net_wm_user_time, Xatom_net_wm_user_time_window,
-    Xatom_net_client_list_stacking, Xatom_net_wm_pid;
+    Xatom_net_client_list_stacking, Xatom_net_wm_pid,
+    Xatom_net_wm_bypass_compositor;
 
   /* XSettings atoms and windows.  */
   Atom Xatom_xsettings_sel, Xatom_xsettings_prop, Xatom_xsettings_mgr;
@@ -1195,6 +1199,15 @@ struct x_output
   XIEventMask *xi_masks;
   int num_xi_masks;
 #endif
+
+  /* Whether or not we are certain we know the offset from the root
+     window to this frame.  */
+  bool window_offset_certain_p;
+
+  /* The offset of the edit window from the root window.  This is
+     strictly an optimization to avoid extraneous synchronizing in
+     some cases.  */
+  int root_x, root_y;
 };
 
 enum
@@ -1209,7 +1222,6 @@ enum
   FOCUS_EXPLICIT = 2
 };
 
-
 /* Return the X output data for frame F.  */
 #define FRAME_X_OUTPUT(f) ((f)->output_data.x)
 #define FRAME_OUTPUT_DATA(f) FRAME_X_OUTPUT (f)
@@ -1339,6 +1351,12 @@ extern void x_mark_frame_dirty (struct frame *f);
 #define FRAME_X_XIM_STYLES(f) (FRAME_DISPLAY_INFO (f)->xim_styles)
 #define FRAME_XIC_STYLE(f) ((f)->output_data.x->xic_style)
 #define FRAME_XIC_FONTSET(f) ((f)->output_data.x->xic_xfs)
+#define FRAME_X_XIM_CODING(f)                          \
+  (SYMBOLP (Vx_input_coding_system)                    \
+   ? Vx_input_coding_system                            \
+   : (!NILP (FRAME_DISPLAY_INFO (f)->xim_coding)       \
+      ? FRAME_DISPLAY_INFO(f)->xim_coding              \
+      : Vlocale_coding_system))
 
 /* X-specific scroll bar stuff.  */
 
@@ -1588,6 +1606,7 @@ extern void x_wm_set_size_hint (struct frame *, long, 
bool);
   && defined HAVE_CLOCK_GETTIME
 extern void x_sync_init_fences (struct frame *);
 #endif
+extern bool x_embed_frame (struct x_display_info *, struct frame *);
 
 extern void x_delete_terminal (struct terminal *);
 extern Cursor x_create_font_cursor (struct x_display_info *, int);
@@ -1827,7 +1846,7 @@ extern void mark_xterm (void);
 
 /* Is the frame embedded into another application? */
 
-#define FRAME_X_EMBEDDED_P(f) (FRAME_X_OUTPUT(f)->explicit_parent != 0)
+#define FRAME_X_EMBEDDED_P(f) (FRAME_X_OUTPUT (f)->explicit_parent != 0)
 
 #define STORE_NATIVE_RECT(nr,rx,ry,rwidth,rheight)     \
   ((nr).x = (rx),                                      \
diff --git a/test/lisp/ansi-color-tests.el b/test/lisp/ansi-color-tests.el
index 1b04e8e9de..f672f33491 100644
--- a/test/lisp/ansi-color-tests.el
+++ b/test/lisp/ansi-color-tests.el
@@ -135,7 +135,7 @@ strings with `eq', this function compares them with 
`equal'."
     (with-temp-buffer
       (should (equal-including-properties
                filtered-str
-               (mapconcat ansi-filt strs ""))))
+               (mapconcat ansi-filt strs))))
 
     ;; Tests for `ansi-color-filter-region'
     (with-temp-buffer
@@ -156,7 +156,7 @@ strings with `eq', this function compares them with 
`equal'."
     (with-temp-buffer
       (should (ansi-color-tests-equal-props
                propertized-str
-               (mapconcat ansi-app strs ""))))
+               (mapconcat ansi-app strs))))
 
     ;; Tests for `ansi-color-apply-on-region'
     (with-temp-buffer
diff --git a/test/lisp/ansi-osc-tests.el b/test/lisp/ansi-osc-tests.el
new file mode 100644
index 0000000000..b3d66fb036
--- /dev/null
+++ b/test/lisp/ansi-osc-tests.el
@@ -0,0 +1,57 @@
+;;; osc-tests.el --- Tests for osc.el  -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2022 Free Software Foundation, Inc.
+
+;; Author: Matthias Meulien <orontee@gmail.com>
+;; Keywords:
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;;
+
+;;; Code:
+
+(require 'ansi-osc)
+(require 'ert)
+
+(defvar ansi-osc-tests--strings
+  `(
+    ("Hello World" "Hello World")
+
+    ;; window title
+    ("Buffer \e]2;A window title\e\\content" "Buffer content")
+
+    ;; window title
+    ("Unfinished \e]2;window title" "Unfinished \e]2;window title")
+
+    ;; current directory
+    ("\e]7;file://127.0.0.1/tmp\e\\user@host$ " "user@host$ ")
+
+    ;; hyperlink
+    ("\e]8;;http://example.com\e\\This is a link\e]8;;\e\\" "This is a link")
+    ))
+;; Don't output those strings to stdout since they may have
+;; side-effects on the environment
+
+(ert-deftest ansi-osc-tests-apply-region-no-handlers ()
+  (let ((ansi-osc-handlers nil))
+    (pcase-dolist (`(,input ,text) ansi-osc-tests--strings)
+      (with-temp-buffer
+        (insert input)
+        (ansi-osc-apply-on-region (point-min) (point-max))
+        (should (equal (buffer-string) text))))))
diff --git a/test/lisp/char-fold-tests.el b/test/lisp/char-fold-tests.el
index 749ec0a8d3..e7f5ff6fd2 100644
--- a/test/lisp/char-fold-tests.el
+++ b/test/lisp/char-fold-tests.el
@@ -26,7 +26,7 @@
 
 (defun char-fold--random-word (n)
   (mapconcat (lambda (_) (string (+ 9 (random 117))))
-             (make-list n nil) ""))
+             (make-list n nil)))
 
 (defun char-fold--ascii-upcase (string)
   "Like `upcase' but acts on ASCII characters only."
diff --git a/test/lisp/dired-tests.el b/test/lisp/dired-tests.el
index 9cf0151905..09becc7fe7 100644
--- a/test/lisp/dired-tests.el
+++ b/test/lisp/dired-tests.el
@@ -456,8 +456,8 @@
             (dolist (file '(a b c d))
               (make-empty-file (expand-file-name (symbol-name file) testdir)))
             (should (= 6 (length (directory-files testdir))))
-            (should (equal "abcd" (mapconcat 'identity (directory-files
-                                                        testdir nil nod) "")))
+            (should (equal "abcd" (mapconcat #'identity (directory-files
+                                                         testdir nil nod))))
             (should (= 2 (length (directory-files testdir nil "[bc]"))))
             (should (= 3 (length (directory-files testdir nil nod nil 3))))
             (dolist (file '(5 4 3 2 1))
diff --git a/test/lisp/dnd-tests.el b/test/lisp/dnd-tests.el
index 88f6e69457..67b660fc12 100644
--- a/test/lisp/dnd-tests.el
+++ b/test/lisp/dnd-tests.el
@@ -52,13 +52,13 @@
       ;; Verify that the action is valid and pretend the drag succeeded
       ;; (by returning the action).
       (cl-ecase action
-        ('XdndActionCopy action)
-        ('XdndActionMove action)
-        ('XdndActionLink action)
+        (XdndActionCopy action)
+        (XdndActionMove action)
+        (XdndActionLink action)
         ;; These two are not technically valid, but x-begin-drag accepts
         ;; them anyway.
-        ('XdndActionPrivate action)
-        ('XdndActionAsk 'XdndActionPrivate))))
+        (XdndActionPrivate action)
+        (XdndActionAsk 'XdndActionPrivate))))
 
   ;; This doesn't work during tests.
   (defalias 'gui-set-selection
diff --git a/test/lisp/electric-tests.el b/test/lisp/electric-tests.el
index 5d7e905cfa..d34737e609 100644
--- a/test/lisp/electric-tests.el
+++ b/test/lisp/electric-tests.el
@@ -901,7 +901,7 @@ baz\"\""
     (should (equal (buffer-string) "int main () {\n  \n}"))))
 
 (ert-deftest electric-layout-control-reindentation ()
-  "Same as `emacs-lisp-int-main-kernel-style', but checking
+  "Same as `electric-layout-int-main-kernel-style', but checking
 Bug#35254."
   (ert-with-test-buffer ()
     (plainer-c-mode)
diff --git 
a/test/lisp/emacs-lisp/bytecomp-resources/warn-variable-set-nonvariable.el 
b/test/lisp/emacs-lisp/bytecomp-resources/warn-variable-set-nonvariable.el
deleted file mode 100644
index 0c76c4d388..0000000000
--- a/test/lisp/emacs-lisp/bytecomp-resources/warn-variable-set-nonvariable.el
+++ /dev/null
@@ -1,3 +0,0 @@
-;;; -*- lexical-binding: t -*-
-(defun foo ()
-  (set '(a) nil))
diff --git 
a/test/lisp/emacs-lisp/bytecomp-resources/warn-wide-docstring-ignore-function-signature.el
 
b/test/lisp/emacs-lisp/bytecomp-resources/warn-wide-docstring-ignore-function-signature.el
new file mode 100644
index 0000000000..e83f516e58
--- /dev/null
+++ 
b/test/lisp/emacs-lisp/bytecomp-resources/warn-wide-docstring-ignore-function-signature.el
@@ -0,0 +1,4 @@
+;;; -*- lexical-binding: t -*-
+(defun foo-bar ()
+  "This should not warn:
+(fn COMMAND &rest ARGS &key (MARGIN (rx bol (+ \" \"))) (ARGUMENT (rx \"-\" (+ 
(any \"-\" alnum)) (32 \"=\"))) (METAVAR (rx (32 \" \") (or (+ (any alnum 
\"_-\")) (seq \"[\" (+? nonl) \"]\") (seq \"<\" (+? nonl) \">\") (seq \"{\" (+? 
nonl) \"}\")))) (SEPARATOR (rx \", \" symbol-start)) (DESCRIPTION (rx (* nonl) 
(* \"\\=\\n\" (>= 9 \" \") (* nonl)))) NARROW-START NARROW-END)")
diff --git a/test/lisp/emacs-lisp/bytecomp-tests.el 
b/test/lisp/emacs-lisp/bytecomp-tests.el
index a246c25e24..e7c308213e 100644
--- a/test/lisp/emacs-lisp/bytecomp-tests.el
+++ b/test/lisp/emacs-lisp/bytecomp-tests.el
@@ -59,6 +59,8 @@ inner loops respectively."
        (setq i (1- i)))
      res))
 
+(defvar bytecomp-tests--xx nil)
+
 (defconst bytecomp-tests--test-cases
   '(
     ;; some functional tests
@@ -692,6 +694,16 @@ inner loops respectively."
            (f (lambda ()
                 (let ((y x)) (list y 3 y)))))
       (funcall f))
+
+    ;; Test rewriting of `set' to `setq' (only done on dynamic variables).
+    (let ((xx 1)) (set 'xx 2) xx)
+    (let ((bytecomp-tests--xx 1))
+      (set 'bytecomp-tests--xx 2)
+      bytecomp-tests--xx)
+    (let ((aaa 1)) (set (make-local-variable 'aaa) 2) aaa)
+    (let ((bytecomp-tests--xx 1))
+      (set (make-local-variable 'bytecomp-tests--xx) 2)
+      bytecomp-tests--xx)
     )
   "List of expressions for cross-testing interpreted and compiled code.")
 
@@ -912,7 +924,7 @@ byte-compiled.  Run with dynamic binding."
                             "next-line.*interactive use only.*forward-line")
 
 (bytecomp--define-warning-file-test "warn-lambda-malformed-interactive-spec.el"
-                            "malformed interactive spec")
+                            "malformed .interactive. specification")
 
 (bytecomp--define-warning-file-test "warn-obsolete-defun.el"
   "foo-obsolete. is an obsolete function (as of 99.99)")
@@ -953,9 +965,6 @@ byte-compiled.  Run with dynamic binding."
 (bytecomp--define-warning-file-test "warn-variable-set-constant.el"
                             "attempt to set constant")
 
-(bytecomp--define-warning-file-test "warn-variable-set-nonvariable.el"
-                            "variable reference to nonvariable")
-
 (bytecomp--define-warning-file-test "warn-variable-setq-nonvariable.el"
                             "attempt to set non-variable")
 
@@ -1006,6 +1015,10 @@ byte-compiled.  Run with dynamic binding."
  "warn-wide-docstring-ignore-fill-column.el"
  "defvar .foo-bar. docstring wider than .* characters" 'reverse)
 
+(bytecomp--define-warning-file-test
+ "warn-wide-docstring-ignore-function-signature.el"
+ "defvar .foo-bar. docstring wider than .* characters" 'reverse)
+
 (bytecomp--define-warning-file-test
  "warn-wide-docstring-ignore-override.el"
  "defvar .foo-bar. docstring wider than .* characters" 'reverse)
diff --git a/test/lisp/emacs-lisp/cconv-tests.el 
b/test/lisp/emacs-lisp/cconv-tests.el
index 9904c6a969..37470f863f 100644
--- a/test/lisp/emacs-lisp/cconv-tests.el
+++ b/test/lisp/emacs-lisp/cconv-tests.el
@@ -347,5 +347,15 @@
                       (list x (funcall g closed-x) (funcall h closed-x))))))))
   )
 
+(ert-deftest cconv-tests-interactive-closure-bug51695 ()
+  (let ((f (let ((d 51695))
+             (lambda (data)
+               (interactive (progn (setq d (1+ d)) (list d)))
+               (list (called-interactively-p 'any) data)))))
+    (should (equal (list (call-interactively f)
+                         (funcall f 51695)
+                         (call-interactively f))
+                   '((t 51696) (nil 51695) (t 51697))))))
+
 (provide 'cconv-tests)
 ;;; cconv-tests.el ends here
diff --git a/test/lisp/emacs-lisp/cl-extra-tests.el 
b/test/lisp/emacs-lisp/cl-extra-tests.el
index 801885c0d4..297e413d85 100644
--- a/test/lisp/emacs-lisp/cl-extra-tests.el
+++ b/test/lisp/emacs-lisp/cl-extra-tests.el
@@ -77,7 +77,7 @@
         (fn3 (lambda (x _y _z) (string-to-char (format "%S" x)))))
     (should (equal lst (cl-map 'list fn1 lst)))
     (should (equal (vconcat lst2) (cl-map 'vector fn2 lst lst2)))
-    (should (equal (mapconcat (lambda (x) (format "%S" x)) lst "")
+    (should (equal (mapconcat (lambda (x) (format "%S" x)) lst)
                    (cl-map 'string fn3 lst lst2 lst3)))))
 
 (ert-deftest cl-extra-test-maplist ()
diff --git a/test/lisp/emacs-lisp/cl-macs-tests.el 
b/test/lisp/emacs-lisp/cl-macs-tests.el
index 19ede627a1..f742637ee3 100644
--- a/test/lisp/emacs-lisp/cl-macs-tests.el
+++ b/test/lisp/emacs-lisp/cl-macs-tests.el
@@ -25,6 +25,8 @@
 (require 'cl-macs)
 (require 'edebug)
 (require 'ert)
+(require 'ert-x)
+(require 'pcase)
 
 
 ;;;; cl-loop tests -- many adapted from Steele's CLtL2
@@ -539,7 +541,27 @@ collection clause."
                          ((p (gv-synthetic-place cl (lambda (v) `(setcar l 
,v)))))
                        (cl-incf p)))
                    l)
-                 '(1))))
+                 '(1)))
+  ;; Make sure `gv-synthetic-place' isn't macro-expanded before
+  ;; `cl-letf' gets to see its `gv-expander'.
+  (should (equal
+           (condition-case err
+               (let ((x 1))
+                 (list x
+                       (cl-letf (((gv-synthetic-place (+ 1 2)
+                                                      (lambda (v) `(setq x 
,v)))
+                                  7))
+                         x)
+                       x))
+             (error err))
+           '(1 7 3)))
+  (should (equal
+           (let ((x (list 42)))
+             (cl-symbol-macrolet ((m (car x)))
+               (list m
+                     (cl-letf ((m 5)) m)
+                     m)))
+           '(42 5 42))))
 
 (ert-deftest cl-macs-loop-conditional-step-clauses ()
   "These tests failed under the initial fixes in #bug#29799."
@@ -727,4 +749,58 @@ collection clause."
       ;; Just make sure the forms can be instrumented.
       (eval-buffer))))
 
+(ert-deftest cl-case-error ()
+  "Test that `cl-case' and `cl-ecase' signal an error if a t or
+`otherwise' key is misplaced."
+  (let ((text-quoting-style 'grave))
+    (dolist (form '((cl-case val (t 1) (123 2))
+                    (cl-ecase val (t 1) (123 2))
+                    (cl-ecase val (123 2) (t 1))))
+      (ert-info ((prin1-to-string form) :prefix "Form: ")
+                (let ((error (should-error (macroexpand form))))
+                  (should (equal (cdr error)
+                                 '("Misplaced t or `otherwise' clause"))))))))
+
+(ert-deftest cl-case-warning ()
+  "Test that `cl-case' and `cl-ecase' warn about suspicious
+constructs."
+  (let ((text-quoting-style 'grave))
+    (pcase-dolist (`(,case . ,message)
+                   `((nil . "Case nil will never match")
+                     ('nil . ,(concat "Case 'nil will match `quote'.  "
+                                      "If that's intended, write "
+                                      "(nil quote) instead.  "
+                                      "Otherwise, don't quote `nil'."))
+                     ('t . ,(concat "Case 't will match `quote'.  "
+                                    "If that's intended, write "
+                                    "(t quote) instead.  "
+                                    "Otherwise, don't quote `t'."))
+                     ('foo . ,(concat "Case 'foo will match `quote'.  "
+                                      "If that's intended, write "
+                                      "(foo quote) instead.  "
+                                      "Otherwise, don't quote `foo'."))
+                     (#'foo . ,(concat "Case #'foo will match "
+                                       "`function'.  If that's "
+                                       "intended, write (foo function) "
+                                       "instead.  Otherwise, don't "
+                                       "quote `foo'."))))
+      (dolist (macro '(cl-case cl-ecase))
+        (let ((form `(,macro val (,case 1))))
+          (ert-info ((prin1-to-string form) :prefix "Form: ")
+                    (ert-with-message-capture messages
+                                              (macroexpand form)
+                                              (should (equal messages
+                                                             (concat "Warning: 
" message "\n"))))))))))
+
+(ert-deftest cl-case-no-warning ()
+  "Test that `cl-case' and `cl-ecase' don't warn in some valid cases.
+See Bug#57915."
+  (dolist (case '(quote (quote) function (function)))
+    (dolist (macro '(cl-case cl-ecase))
+      (let ((form `(,macro val (,case 1))))
+        (ert-info ((prin1-to-string form) :prefix "Form: ")
+          (ert-with-message-capture messages
+            (macroexpand form)
+            (should (string-empty-p messages))))))))
+
 ;;; cl-macs-tests.el ends here
diff --git a/test/lisp/emacs-lisp/edebug-tests.el 
b/test/lisp/emacs-lisp/edebug-tests.el
index 008e1e467b..dea6e9ed61 100644
--- a/test/lisp/emacs-lisp/edebug-tests.el
+++ b/test/lisp/emacs-lisp/edebug-tests.el
@@ -428,7 +428,8 @@ test and possibly others should be updated."
     (verify-keybinding "-" 'negative-argument)
     (verify-keybinding "=" 'edebug-temp-display-freq-count)
     (should (eq (lookup-key backtrace-mode-map "n") 'backtrace-forward-frame))
-    (should (eq (lookup-key backtrace-mode-map "s") 'backtrace-goto-source))))
+    (should (eq (lookup-key edebug-backtrace-mode-map "s")
+                'backtrace-goto-source))))
 
 (ert-deftest edebug-tests-stop-point-at-start-of-first-instrumented-function ()
   "Edebug stops at the beginning of an instrumented function."
diff --git a/test/lisp/emacs-lisp/ert-x-tests.el 
b/test/lisp/emacs-lisp/ert-x-tests.el
index 3869804110..63e7cd7608 100644
--- a/test/lisp/emacs-lisp/ert-x-tests.el
+++ b/test/lisp/emacs-lisp/ert-x-tests.el
@@ -82,6 +82,21 @@
         (should-not (buffer-live-p buffer-1))
         (should (buffer-live-p buffer-2))))))
 
+(ert-deftest ert-test-with-test-buffer-selected/selected ()
+  (ert-with-test-buffer-selected ()
+    (should (eq (window-buffer) (current-buffer)))))
+
+(ert-deftest ert-test-with-test-buffer-selected/modification-hooks ()
+  (ert-with-test-buffer-selected ()
+    (should (null inhibit-modification-hooks))))
+
+(ert-deftest ert-test-with-test-buffer-selected/return-value ()
+  (should (equal (ert-with-test-buffer-selected () "foo") "foo")))
+
+(ert-deftest ert-test-with-test-buffer-selected/buffer-name ()
+  (should (equal (ert-with-test-buffer (:name "foo") (buffer-name))
+                 (ert-with-test-buffer-selected (:name "foo")
+                   (buffer-name)))))
 
 (ert-deftest ert-filter-string ()
   (should (equal (ert-filter-string "foo bar baz" "quux")
diff --git a/test/lisp/emacs-lisp/seq-tests.el 
b/test/lisp/emacs-lisp/seq-tests.el
index 1a27467d29..e22f86f044 100644
--- a/test/lisp/emacs-lisp/seq-tests.el
+++ b/test/lisp/emacs-lisp/seq-tests.el
@@ -137,6 +137,14 @@ Evaluate BODY for each created sequence.
   (with-test-sequences (seq '())
     (should (equal (seq-remove #'test-sequences-evenp seq) '()))))
 
+(ert-deftest test-seq-remove-at-position ()
+  (with-test-sequences (seq '(1 2 3 4))
+    (should (same-contents-p (seq-remove-at-position seq 2) '(1 2 4)))
+    (should (same-contents-p (seq-remove-at-position seq 0) '(2 3 4)))
+    (should (same-contents-p (seq-remove-at-position seq 3) '(1 2 3)))
+    (should (eq (type-of (seq-remove-at-position seq 2))
+                (type-of seq)))))
+
 (ert-deftest test-seq-count ()
   (with-test-sequences (seq '(6 7 8 9 10))
     (should (equal (seq-count #'test-sequences-evenp seq) 3))
@@ -482,6 +490,13 @@ Evaluate BODY for each created sequence.
     (should (= (seq-position seq 'a #'eq) 0))
     (should (null (seq-position seq (make-symbol "a") #'eq)))))
 
+(ert-deftest test-seq-positions ()
+  (with-test-sequences (seq '(1 2 3 1 4))
+    (should (equal '(0 3) (seq-positions seq 1)))
+    (should (seq-empty-p (seq-positions seq 9))))
+  (with-test-sequences (seq '(11 5 7 12 9 15))
+    (should (equal '(0 3 5) (seq-positions seq 10 #'>=)))))
+
 (ert-deftest test-seq-sort-by ()
   (let ((seq ["x" "xx" "xxx"]))
     (should (equal (seq-sort-by #'seq-length #'> seq)
@@ -577,5 +592,11 @@ Evaluate BODY for each created sequence.
     (should (= (length list) 10000))
     (should (= (length (seq-uniq (append list list))) 10000))))
 
+(ert-deftest test-seq-keep ()
+  (should (equal (seq-keep #'cl-digit-char-p '(?6 ?a ?7))
+                 '(6 7)))
+  (should (equal (seq-keep #'cl-digit-char-p [?6 ?a ?7])
+                 '(6 7))))
+
 (provide 'seq-tests)
 ;;; seq-tests.el ends here
diff --git a/test/lisp/erc/erc-match-tests.el b/test/lisp/erc/erc-match-tests.el
new file mode 100644
index 0000000000..cd7598703b
--- /dev/null
+++ b/test/lisp/erc/erc-match-tests.el
@@ -0,0 +1,193 @@
+;;; erc-match-tests.el --- Tests for erc-match.  -*- lexical-binding:t -*-
+
+;; Copyright (C) 2022 Free Software Foundation, Inc.
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.
+
+;;; Commentary:
+;;; Code:
+
+(require 'ert-x)
+(require 'erc-match)
+
+
+(ert-deftest erc-add-entry-to-list ()
+  (let ((erc-pals '("z"))
+        (erc-match-quote-when-adding 'ask))
+
+    (ert-info ("Default (ask)")
+      (ert-simulate-keys "\t\ry\r"
+        (erc-add-entry-to-list 'erc-pals "?" '((".")) nil)
+        (should (equal (pop erc-pals) "\\.")))
+
+      (ert-info ("Inverted")
+        (ert-simulate-keys "\t\ry\r"
+          (erc-add-entry-to-list 'erc-pals "?" '((".")) nil)
+          (should (equal (pop erc-pals) "\\."))))
+
+      (ert-info ("Skipped")
+        (ert-simulate-keys "\t\r"
+          (erc-add-entry-to-list 'erc-pals "?" '(("x")) nil)
+          (should (equal (pop erc-pals) "x")))))
+
+    (ert-info ("Verbatim")
+      (setq erc-match-quote-when-adding nil)
+      (ert-simulate-keys "\t\r"
+        (erc-add-entry-to-list 'erc-pals "?" '((".")) nil)
+        (should (equal (pop erc-pals) ".")))
+
+      (ert-info ("Inverted")
+        (ert-simulate-keys "\t\r"
+          (erc-add-entry-to-list 'erc-pals "?" '((".")) t)
+          (should (equal (pop erc-pals) "\\.")))))
+
+    (ert-info ("Quoted")
+      (setq erc-match-quote-when-adding t)
+      (ert-simulate-keys "\t\r"
+        (erc-add-entry-to-list 'erc-pals "?" '((".")) nil)
+        (should (equal (pop erc-pals) "\\.")))
+
+      (ert-info ("Inverted")
+        (ert-simulate-keys "\t\r"
+          (erc-add-entry-to-list 'erc-pals "?" '((".")) t)
+          (should (equal (pop erc-pals) ".")))))
+
+    (should (equal erc-pals '("z")))))
+
+(ert-deftest erc-pals ()
+  (with-temp-buffer
+    (setq erc-server-process (start-process "true" (current-buffer) "true")
+          erc-server-users (make-hash-table :test #'equal))
+    (set-process-query-on-exit-flag erc-server-process nil)
+    (erc-add-server-user "FOO[m]" (make-erc-server-user :nickname "foo[m]"))
+    (erc-add-server-user "tester" (make-erc-server-user :nickname "tester"))
+
+    (let ((erc-match-quote-when-adding t)
+          erc-pals calls rvs)
+      (cl-letf (((symbol-function 'completing-read)
+                 (lambda (&rest r) (push r calls) (pop rvs))))
+
+        (ert-info ("`erc-add-pal'")
+          (push "foo[m]" rvs)
+          (ert-simulate-command '(erc-add-pal))
+          (should (equal (cadr (pop calls)) '(("tester") ("foo[m]"))))
+          (should (equal erc-pals '("foo\\[m]"))))
+
+        (ert-info ("`erc-match-pal-p'")
+          (should (erc-match-pal-p "FOO[m]!~u@example.net" nil)))
+
+        (ert-info ("`erc-delete-pal'")
+          (push "foo\\[m]" rvs)
+          (ert-simulate-command '(erc-delete-pal))
+          (should (equal (cadr (pop calls)) '(("foo\\[m]"))))
+          (should-not erc-pals))
+
+        (ert-info ("`erc-add-pal' verbatim")
+          (push "foo[m]" rvs)
+          (ert-simulate-command '(erc-add-pal (4)))
+          (should (equal (cadr (pop calls)) '(("tester") ("foo[m]"))))
+          (should (equal erc-pals '("foo[m]"))))))))
+
+(ert-deftest erc-fools ()
+  (with-temp-buffer
+    (setq erc-server-process (start-process "true" (current-buffer) "true")
+          erc-server-users (make-hash-table :test #'equal))
+    (set-process-query-on-exit-flag erc-server-process nil)
+    (erc-add-server-user "FOO[m]" (make-erc-server-user :nickname "foo[m]"))
+    (erc-add-server-user "tester" (make-erc-server-user :nickname "tester"))
+
+    (let ((erc-match-quote-when-adding t)
+          erc-fools calls rvs)
+      (cl-letf (((symbol-function 'completing-read)
+                 (lambda (&rest r) (push r calls) (pop rvs))))
+
+        (ert-info ("`erc-add-fool'")
+          (push "foo[m]" rvs)
+          (ert-simulate-command '(erc-add-fool))
+          (should (equal (cadr (pop calls)) '(("tester") ("foo[m]"))))
+          (should (equal erc-fools '("foo\\[m]"))))
+
+        (ert-info ("`erc-match-fool-p'")
+          (should (erc-match-fool-p "FOO[m]!~u@example.net" ""))
+          (should (erc-match-fool-p "tester!~u@example.net" "FOO[m]: die")))
+
+        (ert-info ("`erc-delete-fool'")
+          (push "foo\\[m]" rvs)
+          (ert-simulate-command '(erc-delete-fool))
+          (should (equal (cadr (pop calls)) '(("foo\\[m]"))))
+          (should-not erc-fools))
+
+        (ert-info ("`erc-add-fool' verbatim")
+          (push "foo[m]" rvs)
+          (ert-simulate-command '(erc-add-fool (4)))
+          (should (equal (cadr (pop calls)) '(("tester") ("foo[m]"))))
+          (should (equal erc-fools '("foo[m]"))))))))
+
+(ert-deftest erc-keywords ()
+  (let ((erc-match-quote-when-adding t)
+        erc-keywords calls rvs)
+    (cl-letf (((symbol-function 'completing-read)
+               (lambda (&rest r) (push r calls) (pop rvs))))
+
+      (ert-info ("`erc-add-keyword'")
+        (push "[cit. needed]" rvs)
+        (ert-simulate-command '(erc-add-keyword))
+        (should (equal (cadr (pop calls)) nil))
+        (should (equal erc-keywords '("\\[cit\\. needed]"))))
+
+      (ert-info ("`erc-match-keyword-p'")
+        (should (erc-match-keyword-p nil "is pretty [cit. needed]")))
+
+      (ert-info ("`erc-delete-keyword'")
+        (push "\\[cit\\. needed]" rvs)
+        (ert-simulate-command '(erc-delete-keyword))
+        (should (equal (cadr (pop calls)) '(("\\[cit\\. needed]"))))
+        (should-not erc-keywords))
+
+      (ert-info ("`erc-add-keyword' verbatim")
+        (push "[...]" rvs)
+        (ert-simulate-command '(erc-add-keyword (4)))
+        (should (equal (cadr (pop calls)) nil))
+        (should (equal erc-keywords '("[...]")))))))
+
+(ert-deftest erc-dangerous-hosts ()
+  (let ((erc-match-quote-when-adding t)
+        erc-dangerous-hosts calls rvs)
+    (cl-letf (((symbol-function 'completing-read)
+               (lambda (&rest r) (push r calls) (pop rvs))))
+
+      (ert-info ("`erc-add-dangerous-host'")
+        (push "example.net" rvs)
+        (ert-simulate-command '(erc-add-dangerous-host))
+        (should (equal (cadr (pop calls)) nil))
+        (should (equal erc-dangerous-hosts '("example\\.net"))))
+
+      (ert-info ("`erc-match-dangerous-host-p'")
+        (should (erc-match-dangerous-host-p "FOO[m]!~u@example.net" nil)))
+
+      (ert-info ("`erc-delete-dangerous-host'")
+        (push "example\\.net" rvs)
+        (ert-simulate-command '(erc-delete-dangerous-host))
+        (should (equal (cadr (pop calls)) '(("example\\.net"))))
+        (should-not erc-dangerous-hosts))
+
+      (ert-info ("`erc-add-dangerous-host' verbatim")
+        (push "example.net" rvs)
+        (ert-simulate-command '(erc-add-dangerous-host (4)))
+        (should (equal (cadr (pop calls)) nil))
+        (should (equal erc-dangerous-hosts '("example.net")))))))
+
+;;; erc-match-tests.el ends here
diff --git a/test/lisp/erc/erc-scenarios-base-reconnect.el 
b/test/lisp/erc/erc-scenarios-base-reconnect.el
index 30d692058d..49298dc594 100644
--- a/test/lisp/erc/erc-scenarios-base-reconnect.el
+++ b/test/lisp/erc/erc-scenarios-base-reconnect.el
@@ -99,10 +99,11 @@
 
     (funcall test)
 
+    ;; A manual /JOIN command tells ERC we're done auto-reconnecting
     (with-current-buffer "FooNet" (erc-cmd-JOIN "#spam"))
 
-    (erc-d-t-wait-for 5 "Channel #spam shown when autojoined"
-      (eq (window-buffer) (get-buffer "#spam")))
+    (erc-d-t-ensure-for 1 "Newly joined chan ignores `erc-reconnect-display'"
+      (not (eq (window-buffer) (get-buffer "#spam"))))
 
     (ert-info ("Wait for auto reconnect")
       (with-current-buffer erc-server-buffer
@@ -114,43 +115,43 @@
       (with-current-buffer (erc-d-t-wait-for 10 (get-buffer "#spam"))
         (funcall expect 10 "her elves come here anon")))))
 
-(ert-deftest erc-scenarios-base-reconnect-options--default ()
+(ert-deftest erc-scenarios-base-reconnect-options--buffer ()
   :tags '(:expensive-test)
-  (should (eq erc-join-buffer 'buffer))
+  (should (eq erc-join-buffer 'bury))
   (should-not erc-reconnect-display)
 
   ;; FooNet (the server buffer) is not switched to because it's
   ;; already current (but not shown) when `erc-open' is called.  See
   ;; related conditional guard towards the end of that function.
 
-  (erc-scenarios-common--base-reconnect-options
-   (lambda ()
-     (pop-to-buffer-same-window "*Messages*")
+  (let ((erc-reconnect-display 'buffer))
+    (erc-scenarios-common--base-reconnect-options
+     (lambda ()
+       (pop-to-buffer-same-window "*Messages*")
 
-     (erc-d-t-ensure-for 1 "Server buffer not shown"
-       (not (eq (window-buffer) (get-buffer "FooNet"))))
+       (erc-d-t-ensure-for 1 "Server buffer not shown"
+         (not (eq (window-buffer) (get-buffer "FooNet"))))
 
-     (erc-d-t-wait-for 5 "Channel #chan shown when autojoined"
-       (eq (window-buffer) (get-buffer "#chan"))))))
+       (erc-d-t-wait-for 5 "Channel #chan shown when autojoined"
+         (eq (window-buffer) (get-buffer "#chan")))))))
 
-(ert-deftest erc-scenarios-base-reconnect-options--bury ()
+(ert-deftest erc-scenarios-base-reconnect-options--default ()
   :tags '(:expensive-test)
-  (should (eq erc-join-buffer 'buffer))
+  (should (eq erc-join-buffer 'bury))
   (should-not erc-reconnect-display)
 
-  (let ((erc-reconnect-display 'bury))
-    (erc-scenarios-common--base-reconnect-options
+  (erc-scenarios-common--base-reconnect-options
 
-     (lambda ()
-       (pop-to-buffer-same-window "*Messages*")
+   (lambda ()
+     (pop-to-buffer-same-window "*Messages*")
 
-       (erc-d-t-ensure-for 1 "Server buffer not shown"
-         (not (eq (window-buffer) (get-buffer "FooNet"))))
+     (erc-d-t-ensure-for 1 "Server buffer not shown"
+       (not (eq (window-buffer) (get-buffer "FooNet"))))
 
-       (erc-d-t-ensure-for 3 "Channel #chan not shown"
-         (not (eq (window-buffer) (get-buffer "#chan"))))
+     (erc-d-t-ensure-for 3 "Channel #chan not shown"
+       (not (eq (window-buffer) (get-buffer "#chan"))))
 
-       (eq (window-buffer) (messages-buffer))))))
+     (eq (window-buffer) (messages-buffer)))))
 
 ;; Upon reconnecting, playback for channel and target buffers is
 ;; routed correctly.  Autojoin is irrelevant here, but for the
diff --git a/test/lisp/erc/erc-scenarios-base-reuse-buffers.el 
b/test/lisp/erc/erc-scenarios-base-reuse-buffers.el
index f134f3ffb6..8e7e939d04 100644
--- a/test/lisp/erc/erc-scenarios-base-reuse-buffers.el
+++ b/test/lisp/erc/erc-scenarios-base-reuse-buffers.el
@@ -131,43 +131,38 @@ Adapted from scenario clash-of-chans/uniquify described 
in Bug#48598:
           (get-buffer (format "127.0.0.1:%d/127.0.0.1" port)))
          (server-buffer-bar
           (get-buffer (format "127.0.0.1:%d/127.0.0.1<2>" port)))
-         (chan-buffer-foo (get-buffer "#chan/127.0.0.1"))
-         (chan-buffer-bar (get-buffer "#chan/127.0.0.1<2>"))
-         (server-process-foo (with-current-buffer server-buffer-foo
-                               erc-server-process))
-         (server-process-bar (with-current-buffer server-buffer-bar
-                               erc-server-process)))
+         (server-process-foo
+          (buffer-local-value 'erc-server-process server-buffer-foo))
+         (server-process-bar
+          (buffer-local-value 'erc-server-process server-buffer-bar)))
 
     (ert-info ("Unique #chan buffers exist")
-      (let ((chan-bufs (erc-scenarios-common-buflist "#chan"))
-            (known (list chan-buffer-bar chan-buffer-foo)))
-        (should (memq (pop chan-bufs) known))
-        (should (memq (pop chan-bufs) known))
-        (should-not chan-bufs)))
+      (erc-d-t-wait-for 3 (get-buffer "#chan/127.0.0.1<2>"))
+      (erc-d-t-wait-for 3 (get-buffer "#chan/127.0.0.1")))
 
     (ert-info ("#chan@foonet is exclusive and not contaminated")
-      (with-current-buffer chan-buffer-foo
+      (with-current-buffer "#chan/127.0.0.1"
         (funcall expect 1 "<bob>")
         (erc-d-t-absent-for 0.1 "<joe>")
         (funcall expect 1 "strength to climb")
         (should (eq erc-server-process server-process-foo))))
 
     (ert-info ("#chan@barnet is exclusive and not contaminated")
-      (with-current-buffer chan-buffer-bar
+      (with-current-buffer "#chan/127.0.0.1<2>"
         (funcall expect 1 "<joe>")
         (erc-d-t-absent-for 0.1 "<bob>")
         (funcall expect 1 "the loudest noise")
         (should (eq erc-server-process server-process-bar))))
 
     (ert-info ("Part #chan@foonet")
-      (with-current-buffer chan-buffer-foo
+      (with-current-buffer "#chan/127.0.0.1"
         (erc-d-t-search-for 1 "shake my sword")
         (erc-cmd-PART "#chan")
         (funcall expect 3 "You have left channel #chan")
         (erc-cmd-JOIN "#chan")))
 
     (ert-info ("Part #chan@barnet")
-      (with-current-buffer chan-buffer-bar
+      (with-current-buffer "#chan/127.0.0.1<2>"
         (funcall expect 10 "Arm it in rags")
         (should (erc-get-channel-user (erc-current-nick)))
         (erc-cmd-PART "#chan")
@@ -179,7 +174,7 @@ Adapted from scenario clash-of-chans/uniquify described in 
Bug#48598:
       (get-buffer "#chan/127.0.0.1<3>"))
 
     (ert-info ("Activity continues in new, <n>-suffixed #chan@foonet buffer")
-      (with-current-buffer chan-buffer-foo
+      (with-current-buffer "#chan/127.0.0.1"
         (should-not (erc-get-channel-user (erc-current-nick))))
       (with-current-buffer "#chan/127.0.0.1<3>"
         (should (erc-get-channel-user (erc-current-nick)))
@@ -194,7 +189,7 @@ Adapted from scenario clash-of-chans/uniquify described in 
Bug#48598:
       (get-buffer "#chan/127.0.0.1<4>"))
 
     (ert-info ("Activity continues in new, <n>-suffixed #chan@barnet buffer")
-      (with-current-buffer chan-buffer-bar
+      (with-current-buffer "#chan/127.0.0.1<2>"
         (should-not (erc-get-channel-user (erc-current-nick))))
       (with-current-buffer "#chan/127.0.0.1<4>"
         (funcall expect 2 "You have joined channel #chan")
@@ -221,12 +216,12 @@ Adapted from scenario clash-of-chans/uniquify described 
in Bug#48598:
     (ert-info ("Buffers are exempt from shortening")
       (kill-buffer "#chan/127.0.0.1<4>")
       (kill-buffer "#chan/127.0.0.1<3>")
-      (kill-buffer chan-buffer-bar)
+      (kill-buffer "#chan/127.0.0.1<2>")
       (should-not (get-buffer "#chan"))
-      (should chan-buffer-foo))))
+      (should (get-buffer "#chan/127.0.0.1")))))
 
 (ert-deftest erc-scenarios-base-reuse-buffers-channel-buffers--disabled ()
-  :tags '(:expensive-test :unstable)
+  :tags '(:expensive-test)
   (with-suppressed-warnings ((obsolete erc-reuse-buffers))
     (should erc-reuse-buffers)
     (let ((erc-scenarios-common-dialog "base/reuse-buffers/channel")
diff --git a/test/lisp/erc/erc-tests.el b/test/lisp/erc/erc-tests.el
index 55efe2fd2d..b2ed29e80e 100644
--- a/test/lisp/erc/erc-tests.el
+++ b/test/lisp/erc/erc-tests.el
@@ -522,7 +522,7 @@
         (erc-send-current-line)
         (should (ring-p erc-input-ring))
         (should (zerop (ring-member erc-input-ring "/one"))) ; equal
-        (should (save-excursion (forward-line -1) (goto-char (pos-bol))
+        (should (save-excursion (forward-line -1)
                                 (looking-at-p "[*]+ echo: one")))
         (should-not erc-input-ring-index)
         (erc-bol)
diff --git a/test/lisp/erc/resources/base/assoc/samenet/chester.eld 
b/test/lisp/erc/resources/base/assoc/samenet/chester.eld
index f1aed2836c..0132de677c 100644
--- a/test/lisp/erc/resources/base/assoc/samenet/chester.eld
+++ b/test/lisp/erc/resources/base/assoc/samenet/chester.eld
@@ -1,5 +1,5 @@
 ;; -*- mode: lisp-data; -*-
-((pass 1 "PASS :changeme"))
+((pass 10 "PASS :changeme"))
 ((nick 1 "NICK chester"))
 ((user 1 "USER user 0 * :chester")
  (0 ":irc.foonet.org 001 chester :Welcome to the foonet IRC Network chester")
diff --git a/test/lisp/erc/resources/base/assoc/samenet/tester.eld 
b/test/lisp/erc/resources/base/assoc/samenet/tester.eld
index cd9cacbe5d..995fab00f7 100644
--- a/test/lisp/erc/resources/base/assoc/samenet/tester.eld
+++ b/test/lisp/erc/resources/base/assoc/samenet/tester.eld
@@ -1,5 +1,5 @@
 ;; -*- mode: lisp-data; -*-
-((pass 1 "PASS :changeme"))
+((pass 10 "PASS :changeme"))
 ((nick 1 "NICK tester"))
 ((user 1 "USER user 0 * :tester")
  (0 ":irc.foonet.org 001 tester :Welcome to the foonet IRC Network tester")
diff --git a/test/lisp/erc/resources/base/assoc/samenet/tester2.eld 
b/test/lisp/erc/resources/base/assoc/samenet/tester2.eld
index 67c3a94a26..33a05fe261 100644
--- a/test/lisp/erc/resources/base/assoc/samenet/tester2.eld
+++ b/test/lisp/erc/resources/base/assoc/samenet/tester2.eld
@@ -1,5 +1,5 @@
 ;; -*- mode: lisp-data; -*-
-((pass 1 "PASS :changeme"))
+((pass 10 "PASS :changeme"))
 ((nick 1 "NICK tester"))
 ((user 1 "USER user 0 * :tester")
  (0 ":irc.foonet.org 001 tester :Welcome to the foonet IRC Network tester")
diff --git a/test/lisp/erc/resources/base/netid/samenet/chester.eld 
b/test/lisp/erc/resources/base/netid/samenet/chester.eld
index 8c2448733c..7b4bfee9c9 100644
--- a/test/lisp/erc/resources/base/netid/samenet/chester.eld
+++ b/test/lisp/erc/resources/base/netid/samenet/chester.eld
@@ -1,5 +1,5 @@
 ;; -*- mode: lisp-data; -*-
-((pass 1 "PASS :changeme"))
+((pass 10 "PASS :changeme"))
 ((nick 1 "NICK chester"))
 ((user 1 "USER user 0 * :chester")
  (0 ":irc.foonet.org 001 chester :Welcome to the foonet IRC Network chester")
diff --git a/test/lisp/erc/resources/base/netid/samenet/tester.eld 
b/test/lisp/erc/resources/base/netid/samenet/tester.eld
index 76312a7a14..f41b041db4 100644
--- a/test/lisp/erc/resources/base/netid/samenet/tester.eld
+++ b/test/lisp/erc/resources/base/netid/samenet/tester.eld
@@ -1,5 +1,5 @@
 ;; -*- mode: lisp-data; -*-
-((pass 1 "PASS :changeme"))
+((pass 10 "PASS :changeme"))
 ((nick 1 "NICK tester"))
 ((user 1 "USER user 0 * :tester")
  (0 ":irc.foonet.org 001 tester :Welcome to the foonet IRC Network tester")
diff --git a/test/lisp/erc/resources/erc-d/erc-d-tests.el 
b/test/lisp/erc/resources/erc-d/erc-d-tests.el
index 357bc48b08..a4befd96b5 100644
--- a/test/lisp/erc/resources/erc-d/erc-d-tests.el
+++ b/test/lisp/erc/resources/erc-d/erc-d-tests.el
@@ -673,7 +673,7 @@ nonzero for this to work."
                             (cadr (pop errors))))))
 
 (ert-deftest erc-d-run-linger ()
-  :tags '(:expensive-test)
+  :tags '(:unstable :expensive-test)
   (erc-d-tests-with-server (dumb-s _) linger
     (with-current-buffer (erc-d-t-wait-for 6 (get-buffer "#chan"))
       (erc-d-t-search-for 2 "hey"))
@@ -683,7 +683,7 @@ nonzero for this to work."
       (erc-d-t-search-for 3 "Lingered for 1.00 seconds"))))
 
 (ert-deftest erc-d-run-linger-fail ()
-  :tags '(:expensive-test)
+  :tags '(:unstable :expensive-test)
   (let ((erc-server-flood-penalty 0.1)
         errors)
     (erc-d-tests-with-failure-spy
@@ -696,7 +696,7 @@ nonzero for this to work."
     (should (string-match-p "Match failed.*hi" (cadr (pop errors))))))
 
 (ert-deftest erc-d-run-linger-direct ()
-  :tags '(:expensive-test)
+  :tags '(:unstable :expensive-test)
   (let* ((dumb-server (erc-d-run "localhost" t
                                  'linger-multi-a 'linger-multi-b))
          (port (process-contact dumb-server :service))
diff --git a/test/lisp/eshell/esh-cmd-tests.el 
b/test/lisp/eshell/esh-cmd-tests.el
index 3a582965d6..92d785d7fd 100644
--- a/test/lisp/eshell/esh-cmd-tests.el
+++ b/test/lisp/eshell/esh-cmd-tests.el
@@ -74,6 +74,25 @@ e.g. \"{(+ 1 2)} 3\" => 3"
   (eshell-command-result-equal "{(+ 1 2)} 3" 3))
 
 
+;; Lisp forms
+
+(ert-deftest esh-cmd-test/quoted-lisp-form ()
+  "Test parsing of a quoted Lisp form."
+  (eshell-command-result-equal "echo #'(1 2)" '(1 2)))
+
+(ert-deftest esh-cmd-test/backquoted-lisp-form ()
+  "Test parsing of a backquoted Lisp form."
+  (let ((eshell-test-value 42))
+    (eshell-command-result-equal "echo `(answer ,eshell-test-value)"
+                                 '(answer 42))))
+
+(ert-deftest esh-cmd-test/backquoted-lisp-form/splice ()
+  "Test parsing of a backquoted Lisp form using splicing."
+  (let ((eshell-test-value '(2 3)))
+    (eshell-command-result-equal "echo `(1 ,@eshell-test-value)"
+                                 '(1 2 3))))
+
+
 ;; Logical operators
 
 (ert-deftest esh-cmd-test/and-operator ()
diff --git a/test/lisp/eshell/esh-io-tests.el b/test/lisp/eshell/esh-io-tests.el
new file mode 100644
index 0000000000..37b234eaf0
--- /dev/null
+++ b/test/lisp/eshell/esh-io-tests.el
@@ -0,0 +1,292 @@
+;;; esh-io-tests.el --- esh-io test suite  -*- lexical-binding:t -*-
+
+;; Copyright (C) 2022 Free Software Foundation, Inc.
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.
+
+;;; Code:
+
+(require 'ert)
+(require 'ert-x)
+(require 'esh-mode)
+(require 'eshell)
+
+(require 'eshell-tests-helpers
+         (expand-file-name "eshell-tests-helpers"
+                           (file-name-directory (or load-file-name
+                                                    default-directory))))
+
+(defvar eshell-test-value nil)
+
+(defun eshell-test-file-string (file)
+  "Return the contents of FILE as a string."
+  (with-temp-buffer
+    (insert-file-contents file)
+    (buffer-string)))
+
+(defun eshell/test-output ()
+  "Write some test output separately to stdout and stderr."
+  (eshell-printn "stdout")
+  (eshell-errorn "stderr"))
+
+;;; Tests:
+
+
+;; Basic redirection
+
+(ert-deftest esh-io-test/redirect-file/overwrite ()
+  "Check that redirecting to a file in overwrite mode works."
+  (ert-with-temp-file temp-file
+    :text "old"
+    (with-temp-eshell
+     (eshell-insert-command (format "echo new > %s" temp-file)))
+    (should (equal (eshell-test-file-string temp-file) "new"))))
+
+(ert-deftest esh-io-test/redirect-file/append ()
+  "Check that redirecting to a file in append mode works."
+  (ert-with-temp-file temp-file
+    :text "old"
+    (with-temp-eshell
+     (eshell-insert-command (format "echo new >> %s" temp-file)))
+    (should (equal (eshell-test-file-string temp-file) "oldnew"))))
+
+(ert-deftest esh-io-test/redirect-file/insert ()
+  "Check that redirecting to a file in insert works."
+  (ert-with-temp-file temp-file
+    :text "old"
+    (with-temp-eshell
+     (eshell-insert-command (format "echo new >>> %s" temp-file)))
+    (should (equal (eshell-test-file-string temp-file) "newold"))))
+
+(ert-deftest esh-io-test/redirect-buffer/overwrite ()
+  "Check that redirecting to a buffer in overwrite mode works."
+  (eshell-with-temp-buffer bufname "old"
+    (with-temp-eshell
+     (eshell-insert-command (format "echo new > #<%s>" bufname)))
+    (should (equal (buffer-string) "new"))))
+
+(ert-deftest esh-io-test/redirect-buffer/append ()
+  "Check that redirecting to a buffer in append mode works."
+  (eshell-with-temp-buffer bufname "old"
+    (with-temp-eshell
+     (eshell-insert-command (format "echo new >> #<%s>" bufname)))
+    (should (equal (buffer-string) "oldnew"))))
+
+(ert-deftest esh-io-test/redirect-buffer/insert ()
+  "Check that redirecting to a buffer in insert mode works."
+  (eshell-with-temp-buffer bufname "old"
+    (goto-char (point-min))
+    (with-temp-eshell
+     (eshell-insert-command (format "echo new >>> #<%s>" bufname)))
+    (should (equal (buffer-string) "newold"))))
+
+(ert-deftest esh-io-test/redirect-buffer/escaped ()
+  "Check that redirecting to a buffer with escaped characters works."
+  (with-temp-buffer
+    (rename-buffer "eshell\\temp\\buffer" t)
+    (let ((bufname (buffer-name)))
+      (with-temp-eshell
+       (eshell-insert-command (format "echo hi > #<%s>"
+                                      (string-replace "\\" "\\\\" bufname))))
+      (should (equal (buffer-string) "hi")))))
+
+(ert-deftest esh-io-test/redirect-symbol/overwrite ()
+  "Check that redirecting to a symbol in overwrite mode works."
+  (let ((eshell-test-value "old"))
+    (with-temp-eshell
+     (eshell-insert-command "echo new > #'eshell-test-value"))
+    (should (equal eshell-test-value "new"))))
+
+(ert-deftest esh-io-test/redirect-symbol/append ()
+  "Check that redirecting to a symbol in append mode works."
+  (let ((eshell-test-value "old"))
+    (with-temp-eshell
+     (eshell-insert-command "echo new >> #'eshell-test-value"))
+    (should (equal eshell-test-value "oldnew"))))
+
+(ert-deftest esh-io-test/redirect-marker ()
+  "Check that redirecting to a marker works."
+  (with-temp-buffer
+    (let ((eshell-test-value (point-marker)))
+      (with-temp-eshell
+       (eshell-insert-command "echo hi > $eshell-test-value"))
+      (should (equal (buffer-string) "hi")))))
+
+(ert-deftest esh-io-test/redirect-multiple ()
+  "Check that redirecting to multiple targets works."
+  (let ((eshell-test-value "old"))
+    (eshell-with-temp-buffer bufname "old"
+     (with-temp-eshell
+      (eshell-insert-command (format "echo new > #<%s> > #'eshell-test-value"
+                                     bufname)))
+     (should (equal (buffer-string) "new"))
+     (should (equal eshell-test-value "new")))))
+
+(ert-deftest esh-io-test/redirect-multiple/repeat ()
+  "Check that redirecting to multiple targets works when repeating a target."
+  (let ((eshell-test-value "old"))
+    (eshell-with-temp-buffer bufname "old"
+     (with-temp-eshell
+      (eshell-insert-command
+       (format "echo new > #<%s> > #'eshell-test-value > #<%s>"
+               bufname bufname)))
+     (should (equal (buffer-string) "new"))
+     (should (equal eshell-test-value "new")))))
+
+
+;; Redirecting specific handles
+
+(ert-deftest esh-io-test/redirect-stdout ()
+  "Check that redirecting to stdout doesn't redirect stderr."
+  (eshell-with-temp-buffer bufname "old"
+    (with-temp-eshell
+     (eshell-match-command-output (format "test-output > #<%s>" bufname)
+                                  "stderr\n"))
+    (should (equal (buffer-string) "stdout\n")))
+  ;; Also check explicitly specifying the stdout fd.
+  (eshell-with-temp-buffer bufname "old"
+    (with-temp-eshell
+     (eshell-match-command-output (format "test-output 1> #<%s>" bufname)
+                                  "stderr\n"))
+    (should (equal (buffer-string) "stdout\n"))))
+
+(ert-deftest esh-io-test/redirect-stderr/overwrite ()
+  "Check that redirecting to stderr doesn't redirect stdout."
+  (eshell-with-temp-buffer bufname "old"
+    (with-temp-eshell
+     (eshell-match-command-output (format "test-output 2> #<%s>" bufname)
+                                  "stdout\n"))
+    (should (equal (buffer-string) "stderr\n"))))
+
+(ert-deftest esh-io-test/redirect-stderr/append ()
+  "Check that redirecting to stderr doesn't redirect stdout."
+  (eshell-with-temp-buffer bufname "old"
+    (with-temp-eshell
+     (eshell-match-command-output (format "test-output 2>> #<%s>" bufname)
+                                  "stdout\n"))
+    (should (equal (buffer-string) "oldstderr\n"))))
+
+(ert-deftest esh-io-test/redirect-stderr/insert ()
+  "Check that redirecting to stderr doesn't redirect stdout."
+  (eshell-with-temp-buffer bufname "old"
+    (goto-char (point-min))
+    (with-temp-eshell
+     (eshell-match-command-output (format "test-output 2>>> #<%s>" bufname)
+                                  "stdout\n"))
+    (should (equal (buffer-string) "stderr\nold"))))
+
+(ert-deftest esh-io-test/redirect-stdout-and-stderr ()
+  "Check that redirecting to both stdout and stderr works."
+  (eshell-with-temp-buffer bufname-1 "old"
+    (eshell-with-temp-buffer bufname-2 "old"
+      (with-temp-eshell
+       (eshell-match-command-output (format "test-output > #<%s> 2> #<%s>"
+                                            bufname-1 bufname-2)
+                                    "\\`\\'"))
+      (should (equal (buffer-string) "stderr\n")))
+    (should (equal (buffer-string) "stdout\n"))))
+
+(ert-deftest esh-io-test/redirect-all/overwrite ()
+  "Check that redirecting to stdout and stderr via shorthand works."
+  (eshell-with-temp-buffer bufname "old"
+    (with-temp-eshell
+     (eshell-match-command-output (format "test-output &> #<%s>" bufname)
+                                  "\\`\\'"))
+    (should (equal (buffer-string) "stdout\nstderr\n")))
+  ;; Also check the alternate (and less-preferred in Bash) `>&' syntax.
+  (eshell-with-temp-buffer bufname "old"
+    (with-temp-eshell
+     (eshell-match-command-output (format "test-output >& #<%s>" bufname)
+                                  "\\`\\'"))
+    (should (equal (buffer-string) "stdout\nstderr\n"))))
+
+(ert-deftest esh-io-test/redirect-all/append ()
+  "Check that redirecting to stdout and stderr via shorthand works."
+  (eshell-with-temp-buffer bufname "old"
+    (with-temp-eshell
+     (eshell-match-command-output (format "test-output &>> #<%s>" bufname)
+                                  "\\`\\'"))
+    (should (equal (buffer-string) "oldstdout\nstderr\n")))
+  ;; Also check the alternate (and less-preferred in Bash) `>>&' syntax.
+  (eshell-with-temp-buffer bufname "old"
+    (with-temp-eshell
+     (eshell-match-command-output (format "test-output >>& #<%s>" bufname)
+                                  "\\`\\'"))
+    (should (equal (buffer-string) "oldstdout\nstderr\n"))))
+
+(ert-deftest esh-io-test/redirect-all/insert ()
+  "Check that redirecting to stdout and stderr via shorthand works."
+  (eshell-with-temp-buffer bufname "old"
+    (goto-char (point-min))
+    (with-temp-eshell
+     (eshell-match-command-output (format "test-output &>>> #<%s>" bufname)
+                                  "\\`\\'"))
+    (should (equal (buffer-string) "stdout\nstderr\nold")))
+  ;; Also check the alternate `>>>&' syntax.
+  (eshell-with-temp-buffer bufname "old"
+    (goto-char (point-min))
+    (with-temp-eshell
+     (eshell-match-command-output (format "test-output >>>& #<%s>" bufname)
+                                  "\\`\\'"))
+    (should (equal (buffer-string) "stdout\nstderr\nold"))))
+
+(ert-deftest esh-io-test/redirect-copy ()
+  "Check that redirecting stdout and then copying stdout to stderr works.
+This should redirect both stdout and stderr to the same place."
+  (eshell-with-temp-buffer bufname "old"
+    (with-temp-eshell
+     (eshell-match-command-output (format "test-output > #<%s> 2>&1" bufname)
+                                  "\\`\\'"))
+    (should (equal (buffer-string) "stdout\nstderr\n"))))
+
+(ert-deftest esh-io-test/redirect-copy-first ()
+  "Check that copying stdout to stderr and then redirecting stdout works.
+This should redirect stdout to a buffer, and stderr to where
+stdout originally pointed (the terminal)."
+  (eshell-with-temp-buffer bufname "old"
+    (with-temp-eshell
+     (eshell-match-command-output (format "test-output 2>&1 > #<%s>" bufname)
+                                  "stderr\n"))
+    (should (equal (buffer-string) "stdout\n"))))
+
+(ert-deftest esh-io-test/redirect-pipe ()
+  "Check that \"redirecting\" to a pipe works."
+  ;; `|' should only redirect stdout.
+  (eshell-command-result-equal "test-output | rev"
+                               "stderr\ntuodts\n")
+  ;; `|&' should redirect stdout and stderr.
+  (eshell-command-result-equal "test-output |& rev"
+                               "tuodts\nrredts\n"))
+
+
+;; Virtual targets
+
+(ert-deftest esh-io-test/virtual-dev-eshell ()
+  "Check that redirecting to /dev/eshell works."
+  (with-temp-eshell
+   (eshell-match-command-output "echo hi > /dev/eshell" "hi")))
+
+(ert-deftest esh-io-test/virtual-dev-kill ()
+  "Check that redirecting to /dev/kill works."
+  (with-temp-eshell
+   (eshell-insert-command "echo one > /dev/kill")
+   (should (equal (car kill-ring) "one"))
+   (eshell-insert-command "echo two > /dev/kill")
+   (should (equal (car kill-ring) "two"))
+   (eshell-insert-command "echo three >> /dev/kill")
+   (should (equal (car kill-ring) "twothree"))))
+
+;;; esh-io-tests.el ends here
diff --git a/test/lisp/eshell/esh-proc-tests.el 
b/test/lisp/eshell/esh-proc-tests.el
index 62e784e8f6..abe363bee0 100644
--- a/test/lisp/eshell/esh-proc-tests.el
+++ b/test/lisp/eshell/esh-proc-tests.el
@@ -28,15 +28,97 @@
                            (file-name-directory (or load-file-name
                                                     default-directory))))
 
+(defvar esh-proc-test--output-cmd
+  (concat "sh -c '"
+          "echo stdout; "
+          "echo stderr >&2"
+          "'")
+  "A shell command that prints to both stdout and stderr.")
+
 (defvar esh-proc-test--detect-pty-cmd
   (concat "sh -c '"
           "if [ -t 0 ]; then echo stdin; fi; "
           "if [ -t 1 ]; then echo stdout; fi; "
           "if [ -t 2 ]; then echo stderr; fi"
-          "'"))
+          "'")
+  "A shell command that prints the standard streams connected as TTYs.")
 
 ;;; Tests:
 
+
+;; Output and redirection
+
+(ert-deftest esh-proc-test/output/to-screen ()
+  "Check that outputting stdout and stderr to the screen works."
+  (skip-unless (executable-find "sh"))
+  (with-temp-eshell
+   (eshell-match-command-output esh-proc-test--output-cmd
+                                "stdout\nstderr\n")))
+
+(ert-deftest esh-proc-test/output/stdout-to-buffer ()
+  "Check that redirecting only stdout works."
+  (skip-unless (executable-find "sh"))
+  (eshell-with-temp-buffer bufname "old"
+    (with-temp-eshell
+     (eshell-match-command-output
+      (format "%s > #<%s>" esh-proc-test--output-cmd bufname)
+      "stderr\n"))
+    (should (equal (buffer-string) "stdout\n"))))
+
+(ert-deftest esh-proc-test/output/stderr-to-buffer ()
+  "Check that redirecting only stderr works."
+  (skip-unless (executable-find "sh"))
+  (eshell-with-temp-buffer bufname "old"
+    (with-temp-eshell
+     (eshell-match-command-output
+      (format "%s 2> #<%s>" esh-proc-test--output-cmd bufname)
+      "stdout\n"))
+    (should (equal (buffer-string) "stderr\n"))))
+
+(ert-deftest esh-proc-test/output/stdout-and-stderr-to-buffer ()
+  "Check that redirecting stdout and stderr works."
+  (skip-unless (executable-find "sh"))
+  (eshell-with-temp-buffer bufname "old"
+    (with-temp-eshell
+     (eshell-match-command-output
+      (format "%s &> #<%s>" esh-proc-test--output-cmd bufname)
+      "\\`\\'"))
+    (should (equal (buffer-string) "stdout\nstderr\n"))))
+
+
+;; Exit status
+
+(ert-deftest esh-proc-test/exit-status/success ()
+  "Check that successful execution is properly recorded."
+  (skip-unless (executable-find "sh"))
+  (with-temp-eshell
+   (eshell-insert-command "sh -c 'exit 0'")
+   (eshell-wait-for-subprocess)
+   (should (= eshell-last-command-status 0))
+   (should (eq eshell-last-command-result t))))
+
+(ert-deftest esh-proc-test/exit-status/failure ()
+  "Check that failed execution is properly recorded."
+  (skip-unless (executable-find "sh"))
+  (with-temp-eshell
+   (eshell-insert-command "sh -c 'exit 1'")
+   (eshell-wait-for-subprocess)
+   (should (= eshell-last-command-status 1))
+   (should (eq eshell-last-command-result nil))))
+
+(ert-deftest esh-proc-test/exit-status/with-stderr-pipe ()
+  "Check that failed execution is properly recorded even with a pipe process."
+  (skip-unless (executable-find "sh"))
+  (eshell-with-temp-buffer bufname "old"
+    (with-temp-eshell
+     (eshell-insert-command (format "sh -c 'exit 1' > #<%s>" bufname))
+     (eshell-wait-for-subprocess)
+     (should (= eshell-last-command-status 1))
+     (should (eq eshell-last-command-result nil)))))
+
+
+;; Pipelines
+
 (ert-deftest esh-proc-test/sigpipe-exits-process ()
   "Test that a SIGPIPE is properly sent to a process if a pipe closes"
   (skip-unless (and (executable-find "sh")
@@ -46,8 +128,10 @@
    (eshell-match-command-output
     ;; The first command is like `yes' but slower.  This is to prevent
     ;; it from taxing Emacs's process filter too much and causing a
-    ;; hang.
-    (concat "sh -c 'while true; do echo y; sleep 1; done' | "
+    ;; hang.  Note that we use "|&" to connect the processes so that
+    ;; Emacs doesn't create an extra pipe process for the first "sh"
+    ;; invocation.
+    (concat "sh -c 'while true; do echo y; sleep 1; done' |& "
             "sh -c 'read NAME; echo ${NAME}'")
     "y\n")
    (eshell-wait-for-subprocess t)
@@ -74,23 +158,54 @@
 (ert-deftest esh-proc-test/pipeline-connection-type/middle ()
   "Test that all streams are pipes when a command is in the middle of a
 pipeline."
-  ;; Repeated unreproducible errors.
-  :tags '(:unstable)
   (skip-unless (and (executable-find "sh")
                     (executable-find "cat")))
-  (eshell-command-result-equal
-   (concat "echo | " esh-proc-test--detect-pty-cmd " | cat")
-   nil))
+  ;; An `eshell-pipe-broken' signal might occur internally; let Eshell
+  ;; handle it!
+  (let ((debug-on-error nil))
+    (eshell-command-result-equal
+     (concat "echo hi | " esh-proc-test--detect-pty-cmd " | cat")
+     nil)))
 
 (ert-deftest esh-proc-test/pipeline-connection-type/last ()
   "Test that only output streams are PTYs when a command ends a pipeline."
-  ;; Repeated unreproducible errors.
-  :tags '(:unstable)
   (skip-unless (executable-find "sh"))
-  (eshell-command-result-equal
-   (concat "echo | " esh-proc-test--detect-pty-cmd)
-   (unless (eq system-type 'windows-nt)
-     "stdout\nstderr\n")))
+  ;; An `eshell-pipe-broken' signal might occur internally; let Eshell
+  ;; handle it!
+  (let ((debug-on-error nil))
+    (eshell-command-result-equal
+     (concat "echo hi | " esh-proc-test--detect-pty-cmd)
+     (unless (eq system-type 'windows-nt)
+       "stdout\nstderr\n"))))
+
+
+;; Killing processes
+
+(ert-deftest esh-proc-test/kill-process/foreground-only ()
+  "Test that `eshell-kill-process' only kills foreground processes."
+  (with-temp-eshell
+   (eshell-insert-command "sleep 100 &")
+   (eshell-insert-command "sleep 100")
+   (should (equal (length eshell-process-list) 2))
+   ;; This should kill only the foreground process.
+   (eshell-kill-process)
+   (eshell-wait-for-subprocess)
+   (should (equal (length eshell-process-list) 1))
+   ;; Now kill everything, including the background process.
+   (eshell-process-interact 'kill-process t)
+   (eshell-wait-for-subprocess t)
+   (should (equal (length eshell-process-list) 0))))
+
+(ert-deftest esh-proc-test/kill-process/background-prompt ()
+  "Test that killing a background process doesn't emit a new
+prompt.  See bug#54136."
+  (skip-unless (and (executable-find "sh")
+                    (executable-find "sleep")))
+  (with-temp-eshell
+   (eshell-insert-command "sh -c 'while true; do sleep 1; done' &")
+   (kill-process (caar eshell-process-list))
+   (eshell-wait-for-subprocess)
+   (should (eshell-match-output "\\[sh\\(\\.exe\\)?\\] [[:digit:]]+\n"))))
 
 (ert-deftest esh-proc-test/kill-pipeline ()
   "Test that killing a pipeline of processes only emits a single
@@ -131,14 +246,4 @@ write the exit status to the pipe.  See bug#54136."
                      output-start (eshell-end-of-output))
                     "")))))
 
-(ert-deftest esh-proc-test/kill-background-process ()
-  "Test that killing a background process doesn't emit a new
-prompt.  See bug#54136."
-  (skip-unless (and (executable-find "sh")
-                    (executable-find "sleep")))
-  (with-temp-eshell
-   (eshell-insert-command "sh -c 'while true; do sleep 1; done' &")
-   (kill-process (caar eshell-process-list))
-   ;; Give `eshell-sentinel' a chance to run.
-   (sit-for 0.1)
-   (should (eshell-match-output "\\[sh\\(\\.exe\\)?\\] [[:digit:]]+\n"))))
+;;; esh-proc-tests.el ends here
diff --git a/test/lisp/eshell/esh-var-tests.el 
b/test/lisp/eshell/esh-var-tests.el
index bebc57d359..cb5b1766bb 100644
--- a/test/lisp/eshell/esh-var-tests.el
+++ b/test/lisp/eshell/esh-var-tests.el
@@ -105,9 +105,11 @@
 
 (ert-deftest esh-var-test/interp-var-assoc ()
   "Interpolate alist variable with index"
-  (let ((eshell-test-value '(("foo" . 1))))
+  (let ((eshell-test-value '(("foo" . 1) (bar . 2))))
     (eshell-command-result-equal "echo $eshell-test-value[foo]"
-                                 1)))
+                                 1)
+    (eshell-command-result-equal "echo $eshell-test-value[#'bar]"
+                                 2)))
 
 (ert-deftest esh-var-test/interp-var-length-list ()
   "Interpolate length of list variable"
@@ -257,9 +259,11 @@ inside double-quotes"
 
 (ert-deftest esh-var-test/quoted-interp-var-assoc ()
   "Interpolate alist variable with index inside double-quotes"
-  (let ((eshell-test-value '(("foo" . 1))))
+  (let ((eshell-test-value '(("foo" . 1) (bar . 2))))
     (eshell-command-result-equal "echo \"$eshell-test-value[foo]\""
-                                 "1")))
+                                 "1")
+    (eshell-command-result-equal "echo \"$eshell-test-value[#'bar]\""
+                                 "2")))
 
 (ert-deftest esh-var-test/quoted-interp-var-length-list ()
   "Interpolate length of list variable inside double-quotes"
diff --git a/test/lisp/eshell/eshell-tests-helpers.el 
b/test/lisp/eshell/eshell-tests-helpers.el
index 8f0f993447..73abfcbb55 100644
--- a/test/lisp/eshell/eshell-tests-helpers.el
+++ b/test/lisp/eshell/eshell-tests-helpers.el
@@ -51,6 +51,16 @@ See `eshell-wait-for-subprocess'.")
            (let (kill-buffer-query-functions)
              (kill-buffer eshell-buffer)))))))
 
+(defmacro eshell-with-temp-buffer (bufname text &rest body)
+  "Create a temporary buffer containing TEXT and evaluate BODY there.
+BUFNAME will be set to the name of the temporary buffer."
+  (declare (indent 2))
+  `(with-temp-buffer
+     (insert ,text)
+     (rename-buffer "eshell-temp-buffer" t)
+     (let ((,bufname (buffer-name)))
+       ,@body)))
+
 (defun eshell-wait-for-subprocess (&optional all)
   "Wait until there is no interactive subprocess running in Eshell.
 If ALL is non-nil, wait until there are no Eshell subprocesses at
diff --git a/test/lisp/eshell/eshell-tests.el b/test/lisp/eshell/eshell-tests.el
index 1845dba280..d5112146c2 100644
--- a/test/lisp/eshell/eshell-tests.el
+++ b/test/lisp/eshell/eshell-tests.el
@@ -105,25 +105,6 @@
      (format template "format \"%s\" eshell-in-pipeline-p")
      "nil")))
 
-(ert-deftest eshell-test/redirect-buffer ()
-  "Check that piping to a buffer works"
-  (with-temp-buffer
-    (rename-buffer "eshell-temp-buffer" t)
-    (let ((bufname (buffer-name)))
-      (with-temp-eshell
-       (eshell-insert-command (format "echo hi > #<%s>" bufname)))
-      (should (equal (buffer-string) "hi")))))
-
-(ert-deftest eshell-test/redirect-buffer-escaped ()
-  "Check that piping to a buffer with escaped characters works"
-  (with-temp-buffer
-    (rename-buffer "eshell\\temp\\buffer" t)
-    (let ((bufname (buffer-name)))
-      (with-temp-eshell
-       (eshell-insert-command (format "echo hi > #<%s>"
-                                      (string-replace "\\" "\\\\" bufname))))
-      (should (equal (buffer-string) "hi")))))
-
 (ert-deftest eshell-test/escape-nonspecial ()
   "Test that \"\\c\" and \"c\" are equivalent when \"c\" is not a
 special character."
diff --git a/test/lisp/filenotify-tests.el b/test/lisp/filenotify-tests.el
index 4ed1786a8e..d82e2dae7a 100644
--- a/test/lisp/filenotify-tests.el
+++ b/test/lisp/filenotify-tests.el
@@ -137,6 +137,10 @@ Return nil when any other file notification watch is still 
active."
 
 (defun file-notify--test-cleanup ()
   "Cleanup after a test."
+  ;; (when (getenv "EMACS_EMBA_CI")
+  ;;   (dolist (buf (tramp-list-tramp-buffers))
+  ;;     (message ";; %s\n%s" buf (tramp-get-buffer-string buf))
+  ;;     (kill-buffer buf)))
   (file-notify-rm-all-watches)
 
   (ignore-errors
@@ -173,6 +177,7 @@ Return nil when any other file notification watch is still 
active."
 
 (setq file-notify-debug nil
       password-cache-expiry nil
+      ;; tramp-verbose (if (getenv "EMACS_EMBA_CI") 10 0)
       tramp-verbose 0
       ;; When the remote user id is 0, Tramp refuses unsafe temporary files.
       tramp-allow-unsafe-temporary-files
@@ -639,7 +644,9 @@ delivered."
 
 (ert-deftest file-notify-test03-events ()
   "Check file creation/change/removal notifications."
-  :tags '(:expensive-test)
+  :tags (if (getenv "EMACS_EMBA_CI")
+            '(:expensive-test :unstable)
+          '(:expensive-test))
   (skip-unless (file-notify--test-local-enabled))
 
   (unwind-protect
@@ -1382,7 +1389,9 @@ descriptors that were issued when registering the 
watches.  This
 test caters for the situation in bug#22736 where the callback for
 the directory received events for the file with the descriptor of
 the file watch."
-  :tags '(:expensive-test)
+  :tags (if (getenv "EMACS_EMBA_CI")
+            '(:expensive-test :unstable)
+          '(:expensive-test))
   (skip-unless (file-notify--test-local-enabled))
 
   ;; A directory to be watched.
@@ -1567,6 +1576,136 @@ the file watch."
 (file-notify--deftest-remote file-notify-test10-sufficient-resources
   "Check `file-notify-test10-sufficient-resources' for remote files.")
 
+(ert-deftest file-notify-test11-symlinks ()
+  "Check that file notification do not follow symbolic links."
+  :tags '(:expensive-test)
+  (skip-unless (file-notify--test-local-enabled))
+  ;; This test does not work for kqueue (yet).
+  (skip-unless (not (string-equal (file-notify--test-library) "kqueue")))
+
+  (setq file-notify--test-tmpfile (file-notify--test-make-temp-name)
+        file-notify--test-tmpfile1 (file-notify--test-make-temp-name))
+
+  ;; Symlink a file.
+  (unwind-protect
+      (progn
+       (write-region "any text" nil file-notify--test-tmpfile1 nil 'no-message)
+        ;; Some systems, like MS Windows w/o sufficient privileges, do
+        ;; not allow creation of symbolic links.
+        (condition-case nil
+            (make-symbolic-link
+             file-notify--test-tmpfile1 file-notify--test-tmpfile)
+         (error (ert-skip "`make-symbolic-link' not supported")))
+       (should
+        (setq file-notify--test-desc
+              (file-notify--test-add-watch
+                file-notify--test-tmpfile
+                '(attribute-change change) #'file-notify--test-event-handler)))
+        (should (file-notify-valid-p file-notify--test-desc))
+
+        ;; Writing to either the symlink or the target should not
+        ;; raise any event.
+        (file-notify--test-with-actions nil
+          (write-region
+           "another text" nil file-notify--test-tmpfile nil 'no-message)
+          (write-region
+           "another text" nil file-notify--test-tmpfile1 nil 'no-message))
+        ;; Sanity check.
+        (file-notify--test-wait-for-events
+         (file-notify--test-timeout)
+         (not (input-pending-p)))
+        (should-not file-notify--test-events)
+
+        ;; Changing timestamp of the target should not raise any
+        ;; event.  We don't use `nofollow'.
+        (file-notify--test-with-actions nil
+          (set-file-times file-notify--test-tmpfile1 '(0 0))
+          (set-file-times file-notify--test-tmpfile '(0 0)))
+        ;; Sanity check.
+        (file-notify--test-wait-for-events
+         (file-notify--test-timeout)
+         (not (input-pending-p)))
+        (should-not file-notify--test-events)
+
+        ;; Changing timestamp of the symlink shows the event.
+        (file-notify--test-with-actions
+        (cond
+         ;; w32notify does not distinguish between `changed' and
+         ;; `attribute-changed'.
+         ((string-equal (file-notify--test-library) "w32notify")
+          '(changed))
+         ;; GFam{File,Directory}Monitor, GKqueueFileMonitor and
+         ;; GPollFileMonitor do not report the `attribute-changed'
+         ;; event.
+         ((memq (file-notify--test-monitor)
+                 '(GFamFileMonitor GFamDirectoryMonitor
+                   GKqueueFileMonitor GPollFileMonitor))
+           '())
+          (t '(attribute-changed)))
+         (set-file-times file-notify--test-tmpfile '(0 0) 'nofollow))
+
+        ;; Deleting the target should not raise any event.
+        (file-notify--test-with-actions nil
+          (delete-file file-notify--test-tmpfile1)
+          (delete-file file-notify--test-tmpfile))
+        ;; Sanity check.
+        (file-notify--test-wait-for-events
+         (file-notify--test-timeout)
+         (not (input-pending-p)))
+        (should-not file-notify--test-events)
+
+        ;; The environment shall be cleaned up.
+       (file-notify-rm-watch file-notify--test-desc)
+        (file-notify--test-cleanup-p))
+
+    ;; Cleanup.
+    (file-notify--test-cleanup))
+
+  (setq file-notify--test-tmpfile1 (file-notify--test-make-temp-name)
+        file-notify--test-tmpfile (file-notify--test-make-temp-name))
+
+  ;; Symlink a directory.
+  (unwind-protect
+      (let ((tmpfile (expand-file-name "foo" file-notify--test-tmpfile))
+            (tmpfile1 (expand-file-name "foo" file-notify--test-tmpfile1)))
+       (make-directory file-notify--test-tmpfile1)
+        (make-symbolic-link file-notify--test-tmpfile1 
file-notify--test-tmpfile)
+       (write-region "any text" nil tmpfile1 nil 'no-message)
+       (should
+        (setq file-notify--test-desc
+              (file-notify--test-add-watch
+                file-notify--test-tmpfile
+                '(attribute-change change) #'file-notify--test-event-handler)))
+        (should (file-notify-valid-p file-notify--test-desc))
+
+        ;; None of the actions on a file in the symlinked directory
+        ;; will be reported.
+        (file-notify--test-with-actions nil
+          (write-region "another text" nil tmpfile nil 'no-message)
+          (write-region "another text" nil tmpfile1 nil 'no-message)
+          (set-file-times tmpfile '(0 0))
+          (set-file-times tmpfile '(0 0) 'nofollow)
+          (set-file-times tmpfile1 '(0 0))
+          (set-file-times tmpfile1 '(0 0) 'nofollow)
+          (delete-file tmpfile)
+          (delete-file tmpfile1))
+        ;; Sanity check.
+        (file-notify--test-wait-for-events
+         (file-notify--test-timeout)
+         (not (input-pending-p)))
+        (should-not file-notify--test-events)
+
+        ;; The environment shall be cleaned up.
+        (delete-directory file-notify--test-tmpdir 'recursive)
+       (file-notify-rm-watch file-notify--test-desc)
+        (file-notify--test-cleanup-p))
+
+    ;; Cleanup.
+    (file-notify--test-cleanup)))
+
+(file-notify--deftest-remote file-notify-test11-symlinks
+  "Check `file-notify-test11-symlinks' for remote files.")
+
 (defun file-notify-test-all (&optional interactive)
   "Run all tests for \\[file-notify]."
   (interactive "p")
diff --git a/test/lisp/files-tests.el b/test/lisp/files-tests.el
index 20c712226e..682b5cdb44 100644
--- a/test/lisp/files-tests.el
+++ b/test/lisp/files-tests.el
@@ -223,20 +223,39 @@ form.")
                 ("x:/foo/bar/baz/" "z:/qux/foo/"))
                ("///foo/bar/" "$FOO/baz/;/qux/foo/"
                 ("//foo/bar//baz/" "/qux/foo/")))
-           '(("/foo/bar//baz/:/bar/foo/baz//" nil
-              ("/foo/bar//baz/" "/bar/foo/baz//"))
-             ("/foo/bar/:/bar/qux/:/qux/foo" nil
-              ("/foo/bar/" "/bar/qux/" "/qux/foo/"))
-             ("//foo/bar/:/bar/qux/:/qux/foo/" nil
-              ("/foo/bar/" "/bar/qux/" "/qux/foo/"))
-             ("/foo/bar/:/bar/qux/:/qux/foo/" nil
-              ("/foo/bar/" "/bar/qux/" "/qux/foo/"))
-             ("/foo//bar/:/bar/qux/:/qux/foo/" nil
-              ("/foo//bar/" "/bar/qux/" "/qux/foo/"))
-             ("/foo//bar/:/bar/qux/:/qux/foo" nil
-              ("/foo//bar/" "/bar/qux/" "/qux/foo/"))
-             ("/foo/bar" "$FOO/baz/:/qux/foo/" ("/foo/bar/baz/" "/qux/foo/"))
-             ("//foo/bar/" "$FOO/baz/:/qux/foo/" ("/foo/bar//baz/" 
"/qux/foo/")))))
+           (if (eq system-type 'cygwin)
+               '(("/foo/bar//baz/:/bar/foo/baz//" nil
+                  ("/foo/bar//baz/" "/bar/foo/baz//"))
+                 ("/foo/bar/:/bar/qux/:/qux/foo" nil
+                  ("/foo/bar/" "/bar/qux/" "/qux/foo/"))
+                 ("//foo/bar/:/bar/qux/:/qux/foo/" nil
+                  ("//foo/bar/" "/bar/qux/" "/qux/foo/"))
+                 ("/foo/bar/:/bar/qux/:/qux/foo/" nil
+                  ("/foo/bar/" "/bar/qux/" "/qux/foo/"))
+                 ("/foo//bar/:/bar/qux/:/qux/foo/" nil
+                  ("/foo//bar/" "/bar/qux/" "/qux/foo/"))
+                 ("/foo//bar/:/bar/qux/:/qux/foo" nil
+                  ("/foo//bar/" "/bar/qux/" "/qux/foo/"))
+                 ("/foo/bar" "$FOO/baz/:/qux/foo/"
+                  ("/foo/bar/baz/" "/qux/foo/"))
+                 ("///foo/bar/" "$FOO/baz/:/qux/foo/"
+                  ("//foo/bar//baz/" "/qux/foo/")))
+             '(("/foo/bar//baz/:/bar/foo/baz//" nil
+                ("/foo/bar//baz/" "/bar/foo/baz//"))
+               ("/foo/bar/:/bar/qux/:/qux/foo" nil
+                ("/foo/bar/" "/bar/qux/" "/qux/foo/"))
+               ("//foo/bar/:/bar/qux/:/qux/foo/" nil
+                ("/foo/bar/" "/bar/qux/" "/qux/foo/"))
+               ("/foo/bar/:/bar/qux/:/qux/foo/" nil
+                ("/foo/bar/" "/bar/qux/" "/qux/foo/"))
+               ("/foo//bar/:/bar/qux/:/qux/foo/" nil
+                ("/foo//bar/" "/bar/qux/" "/qux/foo/"))
+               ("/foo//bar/:/bar/qux/:/qux/foo" nil
+                ("/foo//bar/" "/bar/qux/" "/qux/foo/"))
+               ("/foo/bar" "$FOO/baz/:/qux/foo/"
+                ("/foo/bar/baz/" "/qux/foo/"))
+               ("//foo/bar/" "$FOO/baz/:/qux/foo/"
+                ("/foo/bar//baz/" "/qux/foo/"))))))
         (foo-env (getenv "FOO"))
         (bar-env (getenv "BAR")))
     (unwind-protect
diff --git a/test/lisp/format-spec-tests.el b/test/lisp/format-spec-tests.el
index 4a3cc74c33..bd493ae1d7 100644
--- a/test/lisp/format-spec-tests.el
+++ b/test/lisp/format-spec-tests.el
@@ -148,6 +148,17 @@
              (format-spec fmt '((?b . "asd") (?a . "fgh")))
              #("fgh%asdasd" 0 3 (a b) 3 4 (c d) 7 10 (e f))))))
 
+(ert-deftest format-spec/function ()
+  (let* (called
+         (spec `((?a . "foo")
+                 (?f . ,(lambda ()
+                          (setq called t)
+                          "bar")))))
+    (should (equal (format-spec "%a" spec) "foo"))
+    (should-not called)
+    (should (equal (format-spec "%f" spec) "bar"))
+    (should called)))
+
 (ert-deftest format-spec-unknown ()
   (should-error (format-spec "foo %b %z zot" '((?b . "bar"))))
   (should-error (format-spec "foo %b %%%z zot" '((?b . "bar"))))
diff --git a/test/lisp/help-tests.el b/test/lisp/help-tests.el
index 833c32ffb2..0fcaacb644 100644
--- a/test/lisp/help-tests.el
+++ b/test/lisp/help-tests.el
@@ -181,8 +181,12 @@ M-g M-c            switch-to-completions
 
 (ert-deftest help-tests-substitute-command-keys/keymap-change ()
   (with-substitute-command-keys-test
+   ;; Global binding should be found even if specifying a specific map
    (test "\\<minibuffer-local-must-match-map>\\[abort-recursive-edit]" "C-]")
-   (test "\\<emacs-lisp-mode-map>\\[eval-defun]" "C-M-x")))
+   (test "\\<emacs-lisp-mode-map>\\[eval-defun]" "C-M-x")
+   ;; Specific map overrides advertised-binding
+   (test "\\<undo-repeat-map>\\[undo]" "u")
+   (test "\\[undo]" "C-x u")))
 
 (defvar-keymap help-tests-remap-map
   :full t
@@ -200,25 +204,45 @@ M-g M-c           switch-to-completions
             "\nUses keymap [`'‘]foobar-map['’], which is not currently 
defined.\n")))
 
 (ert-deftest help-tests-substitute-command-keys/quotes ()
- (with-substitute-command-keys-test
+  (with-substitute-command-keys-test
+   (let ((text-quoting-style 'curve))
+     (test "quotes ‘like this’" "quotes ‘like this’")
+     (test "`x'" "‘x’")
+     (test "`" "‘")
+     (test "'" "’")
+     (test "\\`" "\\‘"))
+   (let ((text-quoting-style 'straight))
+     (test "quotes `like this'" "quotes 'like this'")
+     (test "`x'" "'x'")
+     (test "`" "'")
+     (test "'" "'")
+     (test "\\`" "\\'"))
+   (let ((text-quoting-style 'grave))
+     (test "quotes `like this'" "quotes `like this'")
+     (test "`x'" "`x'")
+     (test "`" "`")
+     (test "'" "'")
+     (test "\\`" "\\`"))))
+
+(ert-deftest help-tests-substitute-quotes ()
   (let ((text-quoting-style 'curve))
-    (test "quotes ‘like this’" "quotes ‘like this’")
-    (test "`x'" "‘x’")
-    (test "`" "‘")
-    (test "'" "’")
-    (test "\\`" "\\‘"))
+    (should (string= (substitute-quotes "quotes ‘like this’") "quotes ‘like 
this’"))
+    (should (string= (substitute-quotes "`x'") "‘x’"))
+    (should (string= (substitute-quotes "`") "‘"))
+    (should (string= (substitute-quotes "'") "’"))
+    (should (string= (substitute-quotes "\\`") "\\‘")))
   (let ((text-quoting-style 'straight))
-    (test "quotes `like this'" "quotes 'like this'")
-    (test "`x'" "'x'")
-    (test "`" "'")
-    (test "'" "'")
-    (test "\\`" "\\'"))
+    (should (string= (substitute-quotes "quotes `like this'") "quotes 'like 
this'"))
+    (should (string= (substitute-quotes "`x'") "'x'"))
+    (should (string= (substitute-quotes "`") "'"))
+    (should (string= (substitute-quotes "'") "'"))
+    (should (string= (substitute-quotes "\\`") "\\'")))
   (let ((text-quoting-style 'grave))
-    (test "quotes `like this'" "quotes `like this'")
-    (test "`x'" "`x'")
-    (test "`" "`")
-    (test "'" "'")
-    (test "\\`" "\\`"))))
+    (should (string= (substitute-quotes "quotes `like this'") "quotes `like 
this'"))
+    (should (string= (substitute-quotes "`x'") "`x'"))
+    (should (string= (substitute-quotes "`") "`"))
+    (should (string= (substitute-quotes "'") "'"))
+    (should (string= (substitute-quotes "\\`") "\\`"))))
 
 (ert-deftest help-tests-substitute-command-keys/literals ()
   (with-substitute-command-keys-test
diff --git a/test/lisp/image-dired-tests.el 
b/test/lisp/image/image-dired-tests.el
similarity index 100%
rename from test/lisp/image-dired-tests.el
rename to test/lisp/image/image-dired-tests.el
diff --git a/test/lisp/image/image-dired-util-tests.el 
b/test/lisp/image/image-dired-util-tests.el
new file mode 100644
index 0000000000..63d42f56de
--- /dev/null
+++ b/test/lisp/image/image-dired-util-tests.el
@@ -0,0 +1,71 @@
+;;; image-dired-util-tests.el --- Tests for image-dired.el  -*- 
lexical-binding: t -*-
+
+;; Copyright (C) 2022 Free Software Foundation, Inc.
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.
+
+;;; Code:
+
+(require 'ert)
+(require 'ert-x)
+(require 'image-dired)
+(require 'image-dired-util)
+(require 'xdg)
+
+(ert-deftest image-dired-thumb-name/standard ()
+  (let ((image-dired-thumbnail-storage 'standard))
+    (should (file-name-absolute-p (image-dired-thumb-name "foo.jpg")))
+    (should (file-name-absolute-p (image-dired-thumb-name "/tmp/foo.jpg")))
+    (should (equal
+             (file-name-directory (image-dired-thumb-name "foo.jpg"))
+             (file-name-directory (image-dired-thumb-name "/tmp/foo.jpg"))))
+    (should (string-search (xdg-cache-home)
+                           (image-dired-thumb-name "foo.jpg")))
+    (should (string-match (rx (in "0-9a-f") ".png")
+                          (image-dired-thumb-name "foo.jpg")))))
+
+(ert-deftest image-dired-thumb-name/image-dired ()
+  ;; Avoid trying to create `image-dired-dir'.
+  (ert-with-temp-directory dir
+    (let ((image-dired-dir dir)
+          (image-dired-thumbnail-storage 'image-dired))
+      (should (file-name-absolute-p (image-dired-thumb-name "foo.jpg")))
+      (should (file-name-absolute-p (image-dired-thumb-name "/tmp/foo.jpg")))
+      (should (equal
+               (file-name-directory (image-dired-thumb-name "foo.jpg"))
+               (file-name-directory (image-dired-thumb-name "/tmp/foo.jpg"))))
+      (should (equal (file-name-nondirectory
+                      ;; The checksum is based on the file name.
+                      (image-dired-thumb-name "/some/path/foo.jpg"))
+                     "dc4e6f7068157023e7f2e8362d15bdd2e3ca89e4.jpg"))
+      (should (equal (file-name-extension
+                      (image-dired-thumb-name "foo.gif"))
+                     "jpg")))))
+
+(ert-deftest image-dired-thumb-name/per-directory ()
+  (let ((image-dired-thumbnail-storage 'per-directory))
+    (should (file-name-absolute-p (image-dired-thumb-name "foo.jpg")))
+    (should (file-name-absolute-p (image-dired-thumb-name "/tmp/foo.jpg")))
+    (should (equal
+             (file-name-nondirectory (image-dired-thumb-name "foo.jpg"))
+             (file-name-nondirectory (image-dired-thumb-name "/tmp/foo.jpg"))))
+    (should (equal (file-name-split (image-dired-thumb-name "/tmp/foo.jpg"))
+                   '("" "tmp" ".image-dired" "foo.jpg.thumb.jpg")))
+    (should (equal (file-name-nondirectory
+                    (image-dired-thumb-name "foo.jpg"))
+                   "foo.jpg.thumb.jpg"))))
+
+;;; image-dired-util-tests.el ends here
diff --git a/test/lisp/image/wallpaper-tests.el 
b/test/lisp/image/wallpaper-tests.el
new file mode 100644
index 0000000000..52011fe797
--- /dev/null
+++ b/test/lisp/image/wallpaper-tests.el
@@ -0,0 +1,86 @@
+;;; wallpaper-tests.el --- tests for wallpaper.el  -*- lexical-binding: t -*-
+
+;; Copyright (C) 2022 Free Software Foundation, Inc.
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.
+
+;;; Code:
+
+(require 'ert)
+(require 'ert-x)
+(require 'wallpaper)
+
+(ert-deftest wallpaper--find-command/return-string ()
+  (should (or (not (wallpaper--find-command))
+              (stringp (wallpaper--find-command)))))
+
+(ert-deftest wallpaper--find-command-args/return-list ()
+  (should (or (not (wallpaper--find-command-args))
+              (listp (wallpaper--find-command-args)))))
+
+(ert-deftest wallpaper--image-file-regexp/return-string ()
+  (should (stringp (wallpaper--image-file-regexp))))
+
+(ert-deftest wallpaper--get-default-file/empty-gives-nil ()
+  (with-temp-buffer
+    (should-not (wallpaper--get-default-file))))
+
+(ert-deftest wallpaper--get-default-file/visiting-file ()
+  (ert-with-temp-file _
+    :buffer buf
+    :suffix (format ".%s" (car image-file-name-extensions))
+    (with-current-buffer buf
+      (should (wallpaper--get-default-file)))))
+
+(ert-deftest wallpaper--get-default-file/file-at-point ()
+  ;; ffap needs the file to exist
+  (ert-with-temp-file fil
+    :buffer buf
+    :suffix (format ".%s" (car image-file-name-extensions))
+    (with-current-buffer buf
+      (insert fil)
+      (should (stringp (wallpaper--get-default-file))))))
+
+(ert-deftest wallpaper--format-arg/filename ()
+  (should (file-name-absolute-p (wallpaper--format-arg "%f" "foo.jpg"))))
+
+(ert-deftest wallpaper--format-arg/filename-hex ()
+  (should (equal (wallpaper--format-arg "%F" "foo bar åäö.jpg")
+                 "foo%20bar%20%C3%A5%C3%A4%C3%B6.jpg")))
+
+(ert-deftest wallpaper--format-arg/width ()
+  (skip-unless noninteractive)
+  (should (equal (wallpaper--format-arg "%w" "foo.jpg")
+                 (number-to-string wallpaper-default-width))))
+
+(ert-deftest wallpaper--format-arg/height ()
+  (skip-unless noninteractive)
+  (should (equal (wallpaper--format-arg "%h" "foo.jpg")
+                 (number-to-string wallpaper-default-height))))
+
+(ert-deftest wallpaper--format-arg/screen ()
+  (skip-unless noninteractive)
+  (should (equal (wallpaper--format-arg "%S" "foo.jpg") "0")))
+
+(ert-deftest wallpaper--format-arg/monitor ()
+  (skip-unless noninteractive)
+  (should (equal (wallpaper--format-arg "%M" "foo.jpg") "0")))
+
+(ert-deftest wallpaper--format-arg/workspace ()
+  (skip-unless noninteractive)
+  (should (equal (wallpaper--format-arg "%W" "foo.jpg") "0")))
+
+;;; wallpaper-tests.el ends here
diff --git a/test/lisp/international/ucs-normalize-tests.el 
b/test/lisp/international/ucs-normalize-tests.el
index 774a3ea7ec..9e359d5022 100644
--- a/test/lisp/international/ucs-normalize-tests.el
+++ b/test/lisp/international/ucs-normalize-tests.el
@@ -207,8 +207,17 @@ must be true for all conformant implementations:
         16326 16327 16328 16329 16330 16331 16332 16333
         16334 16335 16336 16337 16338 16339 16340 16341
         16342 16343 16344 16345 16346 16347 16348 16349
-        16350 16351 16488 16489 16490 16491 16492 16493
-        16494 16495 16496 16497))
+        16350 16351 16352 16353 16354 16355 16356 16357
+        16358 16359 16360 16361 16362 16363 16364 16365
+        16366 16367 16368 16369 16370 16371 16372 16373
+        16374 16375 16376 16377 16378 16379 16380 16381
+        16382 16383 16384 16385 16386 16387 16388 16389
+        16390 16391 16392 16393 16394 16395 16396 16397
+        16398 16399 16400 16401 16402 16403 16404 16405
+        16406 16407 16408 16409 16410 16411 16412 16413
+        16550 16551 16552 16553 16554 16555 16556 16557
+        16488 16489 16490 16491 16492 16493 16494 16495
+        16496 16497 16558 16559))
 
 ;; Keep a record of failures, for consulting afterwards (the ert
 ;; backtrace only shows a truncated version of these lists).
@@ -293,17 +302,19 @@ must be true for all conformant implementations:
         17692 17693 17694 17707 17708 17713 17714 17715
         17716 17727 17728 17733 17734 17739 17740 17745
         17746 17749 17750 17753 17754 17759 17760 17767
-        17768 17807 17808 17809 17810 17811 17812 17813
-        17814 17816 17843 17844 17845 17846 17851 17852
-        17861 17875 17876 17879 17880 17899 17900 17911
-        17912 17913 17914 17915 17916 17917 17918 17919
-        17920 17921 17922 17927 17928 17929 17930 17931
-        17932 17933 17935 17937 17938 17939 17940 17941
-        17943 17945 17947 17949 17951 17952 17953 17955
-        17957 17959 17961 17962 17967 17968 17987 17988
-        17993 17994 18003 18004 18005 18006 18007 18008
-        18009 18010 18011 18012 18017 18018 18019 18020
-        18021 18022 18023 18024 18041 18042 18053 18054
+        17768 17789 17790 17801 17802 17807 17808 17809
+        17810 17811 17812 17813 17814 17815 17816 17821
+        17822 17829 17830 17843 17844 17845 17846 17851
+        17852 17861 17875 17876 17879 17880 17899 17900
+        17097 17907 17908 17911 17912 17913 17914 17915
+        17916 17917 17918 17919 17920 17921 17922 17927
+        17928 17929 17930 17931 17932 17933 17935 17937
+        17938 17939 17940 17941 17943 17945 17947 17949
+        17951 17952 17953 17955 17957 17959 17961 17962
+        17967 17968 17987 17988 17993 17994 18003 18004
+        18005 18006 18007 18008 18009 18010 18011 18012
+        18017 18018 18019 18020 18021 18022 18023 18024
+        18041 18042 18049 18050 18053 18054 18055 18056
         18069 18070 18079 18080 18163 18164 18165 18166
         18171 18172 18175 18176 18211 18212 18219 18220
         18221 18222 18223 18224 18225 18226 18301 18302
@@ -316,27 +327,37 @@ must be true for all conformant implementations:
         18521 18523 18524 18525 18527 18528 18531 18537
         18538 18539 18541 18543 18545 18547 18549 18550
         18551 18553 18554 18555 18557 18558 18559 18560
-        18561 18563 18564 18565 18566 18567 18569 18571
-        18573 18575 18577 18579 18581 18583 18585 18587
-        18589 18591 18593 18595 18596 18597 18599 18601
-        18602 18603 18605 18606 18607 18609 18611 18612
-        18613 18615 18617 18618 18619 18621 18623 18624
-        18625 18627 18629 18631 18633 18635 18636 18637
-        18639 18641 18643 18645 18647 18649 18651 18653
-        18655 18657 18659 18661 18663 18665 18667 18668
-        18669 18670 18671 18674 18676 18686 18688 18690
-        18692 18694 18695 18696 18697 18698 18699 18700
-        18701 18702 18703 18704 18705 18706 18707 18708
-        18709 18710 18721 18722 18723 18724 18739 18741
-        18743 18745 18747 18749 18751 18753 18755 18757
-        18759 18761 18763 18765 18767 18769 18771 18773
-        18775 18777 18779 18781 18783 18785 18787 18789
-        18791 18793 18795 18797 18799 18801 18803 18805
-        18807 18809 18811 18813 18815 18817 18819 18821
-        18823 18825 18827 18829 18831 18833 18835 18837
-        18839 18840 18841 18842 18843 18844 18845 18846
-        18847 18848 18849 18850 18851 18852 18853 18855
-        18857 18859 18861 18863 18865 18866))
+        18561 18562 18563 18564 18565 18566 18567 18569
+        18571 18573 18575 18577 18579 18581 18583 18585
+        18587 18589 18591 18593 18595 18596 18597 18599
+        18601 18602 18603 18605 18606 18607 18609 18611
+        18612 18613 18615 18617 18618 18619 18621 18622
+        18623 18624 18625 18626 18627 18628 18629 18631
+        18632 18633 18634 18635 18636 18637 18639 18641
+        18643 18645 18647 18649 18651 18653 18655 18657
+        18659 18661 18663 18664 18665 18667 18668 18669
+        18670 18671 18673 18674 18675 18676 18677 18679
+        18680 18681 18683 18685 18686 18687 18688 18689
+        18690 18691 18692 18693 18694 18695 18696 18697
+        18698 18699 18700 18701 18702 18703 18704 18705
+        18706 18707 18708 18709 18710 18711 18712 18713
+        18714 18715 18717 18719 18721 18722 18723 18724
+        18725 18727 18729 18731 18733 18735 18737 18739
+        18740 18741 18742 18743 18745 18747 18749 18751
+        18753 18755 18757 18759 18761 18763 18765 18767
+        18769 18771 18773 18775 18777 18779 18781 18783
+        18785 18787 18789 18791 18793 18795 18797 18799
+        18801 18803 18805 18807 18809 18811 18813 18815
+        18817 18819 18821 18823 18825 18827 18829 18831
+        18833 18835 18837 18839 18840 18841 18842 18843
+        18844 18845 18846 18847 18848 18849 18850 18851
+        18852 18853 18855 18857 18859 18861 18863 18865
+        18866 18867 18869 18871 18873 18875 18877 18879
+        18881 18883 18885 18887 18888 18889 18891 18893
+        18895 18897 18899 18901 18903 18905 18907 18909
+        18911 18913 18914 18915 18916 18917 18918 18919
+        18920 18921 18923 18925 18927 18929 18931 18933
+        18935 18937 18939 18941 18943 18945 18947 18948))
 
 (ert-deftest ucs-normalize-part2 ()
   :tags '(:expensive-test)
diff --git a/test/lisp/md4-tests.el b/test/lisp/md4-tests.el
index fb7df652bc..d1f227cb90 100644
--- a/test/lisp/md4-tests.el
+++ b/test/lisp/md4-tests.el
@@ -29,7 +29,7 @@
 
 (defun md4-tests-digest->hex (str)
   "Print digest STR in hexadecimal."
-  (mapconcat (lambda (x) (format "%02x" x)) str ""))
+  (mapconcat (lambda (x) (format "%02x" x)) str))
 
 (ert-deftest md4-test-rfc1320 ()
   "Verify the test suite results in RFC 1320.
diff --git a/test/lisp/net/hmac-md5-tests.el b/test/lisp/net/hmac-md5-tests.el
index ce08dd89d1..09bbb8015e 100644
--- a/test/lisp/net/hmac-md5-tests.el
+++ b/test/lisp/net/hmac-md5-tests.el
@@ -48,7 +48,7 @@
   (should (equal (encode-hex-string
                   (hmac-md5 (decode-hex-string
                              (mapconcat (lambda (c) (concat (list c) "d"))
-                                        (make-string 50 ?c) ""))
+                                        (make-string 50 ?c)))
                             (decode-hex-string 
"0102030405060708090a0b0c0d0e0f10111213141516171819")))
                  "697eaf0aca3a3aea3a75164746ffaa79"))
 
diff --git a/test/lisp/net/mailcap-tests.el b/test/lisp/net/mailcap-tests.el
index 188706fc86..9e60a243b3 100644
--- a/test/lisp/net/mailcap-tests.el
+++ b/test/lisp/net/mailcap-tests.el
@@ -133,4 +133,409 @@
        (mailcap-view-file (ert-resource-file "test.test")))
      (should mailcap--test-result))))
 
+
+
+(ert-deftest mailcap-add-mailcap-entry-new-major ()
+  "Add a major entry not yet in ‘mailcap-mime-data’."
+  (let ((mailcap-mime-data))
+
+    ;; Add a new major entry to a empty ‘mailcap-mime-data’.
+    (mailcap-add-mailcap-entry "major1" "minor1"
+                               (list (cons 'viewer "viewer1"))
+                               'mailcap-mime-data)
+    (should (equal mailcap-mime-data
+                   '(("major1"
+                      ("minor1" . ((viewer . "viewer1")))))))
+
+    ;; Add a new major entry to a non-empty ‘mailcap-mime-data’.
+    (mailcap-add-mailcap-entry "major2" "minor2"
+                               (list (cons 'viewer "viewer2"))
+                               'mailcap-mime-data)
+    (should (equal mailcap-mime-data
+                   '(("major2"
+                      ("minor2" . ((viewer . "viewer2"))))
+                     ("major1"
+                      ("minor1" . ((viewer . "viewer1"))))))))
+
+  ;; Same spiel but with extra entries in INFO.
+  (let ((mailcap-mime-data))
+    ;; Add a new major entry to an empty ‘mailcap-mime-data’.
+    (mailcap-add-mailcap-entry "major1" "minor1"
+                               (list (cons 'viewer "viewer1")
+                                     (cons 'print "print1"))
+                               'mailcap-mime-data)
+    (should (equal mailcap-mime-data
+                   '(("major1"
+                      ("minor1" . ((viewer . "viewer1")
+                                   (print . "print1")))))))
+
+    ;; Add a new major entry to a non-empty ‘mailcap-mime-data’.
+    (mailcap-add-mailcap-entry "major2" "minor2"
+                               (list (cons 'viewer "viewer2")
+                                     (cons 'print "print2")
+                                     (cons 'compose "compose2"))
+                               'mailcap-mime-data)
+    (should (equal mailcap-mime-data
+                   '(("major2"
+                      ("minor2" . ((viewer . "viewer2")
+                                   (print . "print2")
+                                   (compose . "compose2"))))
+                     ("major1"
+                      ("minor1" . ((viewer . "viewer1")
+                                   (print . "print1")))))))))
+
+
+(ert-deftest mailcap-add-mailcap-entry-new-minor-to-empty-major ()
+  "Add a minor entry to a an empty major entry."
+  (let ((mailcap-mime-data (list (list "major"))))
+    (mailcap-add-mailcap-entry "major" "minor1"
+                               (list (cons 'viewer "viewer1")
+                                     (cons 'print "print1"))
+                               'mailcap-mime-data)
+    (should (equal mailcap-mime-data
+                   '(("major"
+                      ("minor1" . ((viewer . "viewer1")
+                                   (print . "print1")))))))))
+
+(ert-deftest mailcap-add-mailcap-entry-new-minor-to-non-empty-major ()
+  "Add a minor to a major entry containing already minor entries."
+  (let ((mailcap-mime-data
+         (list
+          (list "major"
+                (list "minor1"
+                      (cons 'viewer "viewer1")
+                      (cons 'test "test1")
+                      (cons 'print "print1"))))))
+
+    (mailcap-add-mailcap-entry "major" "minor2"
+                               (list (cons 'viewer "viewer2")
+                                     (cons 'test "test2")
+                                     (cons 'print "print2"))
+                               'mailcap-mime-data)
+    (should (equal mailcap-mime-data
+                   '(("major"
+                      ("minor2" . ((viewer . "viewer2")
+                                   (test . "test2")
+                                   (print . "print2")))
+                      ("minor1" . ((viewer . "viewer1")
+                                   (test . "test1")
+                                   (print . "print1")))))))
+
+    (mailcap-add-mailcap-entry "major" "minor3"
+                               (list (cons 'viewer "viewer3")
+                                     (cons 'test "test3")
+                                     (cons 'compose "compose3"))
+                               'mailcap-mime-data)
+    (should (equal mailcap-mime-data
+                   '(("major"
+                      ("minor3" . ((viewer . "viewer3")
+                                   (test . "test3")
+                                   (compose . "compose3")))
+                      ("minor2" . ((viewer . "viewer2")
+                                   (test . "test2")
+                                   (print . "print2")))
+                      ("minor1" . ((viewer . "viewer1")
+                                   (test . "test1")
+                                   (print . "print1")))))))))
+
+(ert-deftest mailcap-add-mailcap-entry-new-minor-to-various-major-positions ()
+  "Add a new minor entry to major entries at various positions
+in ‘mailcap-mime-data’."
+  (let ((mailcap-mime-data
+         (list
+          (list "major1"
+                (list "minor1.1"
+                      (cons 'viewer "viewer1.1")
+                      (cons 'print "print1.1")))
+          (list "major2"
+                (list "minor2.1"
+                      (cons 'viewer "viewer2.1")
+                      (cons 'print "print2.1")
+                      (cons 'compose "compose2.1")))
+          (list "major3"
+                (list "minor3.1"
+                      (cons 'viewer "viewer3.1")
+                      (cons 'compose "compose3.1")))
+          (list "major4"
+                (list "minor4.1"
+                      (cons 'viewer "viewer4.1")
+                      (cons 'edit "edit4.1"))))))
+
+    ;; Add a minor entry to a major mode at the front of
+    ;; ‘mailcap-mime-data’.
+    (mailcap-add-mailcap-entry "major1" "minor1.2"
+                               (list (cons 'viewer "viewer1.2")
+                                     (cons 'test "test1.2"))
+                               'mailcap-mime-data)
+    (should (equal mailcap-mime-data
+                   '(("major1"
+                      ("minor1.2" . ((viewer . "viewer1.2")
+                                     (test . "test1.2")))
+                      ("minor1.1" . ((viewer . "viewer1.1")
+                                     (print . "print1.1"))))
+                     ("major2"
+                      ("minor2.1" . ((viewer . "viewer2.1")
+                                     (print . "print2.1")
+                                     (compose . "compose2.1"))))
+                     ("major3"
+                      ("minor3.1" . ((viewer . "viewer3.1")
+                                     (compose . "compose3.1"))))
+                     ("major4"
+                      ("minor4.1" . ((viewer . "viewer4.1")
+                                     (edit . "edit4.1")))))))
+
+    ;; Add a minor entry to a major mode in the middle of
+    ;; ‘mailcap-mime-data’.
+    (mailcap-add-mailcap-entry "major3" "minor3.2"
+                               (list (cons 'viewer "viewer3.2")
+                                     (cons 'test "test3.2")
+                                     (cons 'compose "compose3.2"))
+                               'mailcap-mime-data)
+    (should (equal mailcap-mime-data
+                   '(("major1"
+                      ("minor1.2" . ((viewer . "viewer1.2")
+                                     (test . "test1.2")))
+                      ("minor1.1" . ((viewer . "viewer1.1")
+                                     (print . "print1.1"))))
+                     ("major2"
+                      ("minor2.1" . ((viewer . "viewer2.1")
+                                     (print . "print2.1")
+                                     (compose . "compose2.1"))))
+                     ("major3"
+                      ("minor3.2" . ((viewer . "viewer3.2")
+                                     (test . "test3.2")
+                                     (compose . "compose3.2")))
+                      ("minor3.1" . ((viewer . "viewer3.1")
+                                     (compose . "compose3.1"))))
+                     ("major4"
+                      ("minor4.1" . ((viewer . "viewer4.1")
+                                     (edit . "edit4.1")))))))
+
+    ;; Add a minor entry to a major mode at the end of
+    ;; ‘mailcap-mime-data’.
+    (mailcap-add-mailcap-entry "major4" "minor4.2"
+                               (list (cons 'viewer "viewer4.2")
+                                     (cons 'test "test4.2")
+                                     (cons 'print "print4.2")
+                                     (cons 'compose "compose4.2"))
+                               'mailcap-mime-data)
+    (should (equal mailcap-mime-data
+                   '(("major1"
+                      ("minor1.2" . ((viewer . "viewer1.2")
+                                     (test . "test1.2")))
+                      ("minor1.1" . ((viewer . "viewer1.1")
+                                     (print . "print1.1"))))
+                     ("major2"
+                      ("minor2.1" . ((viewer . "viewer2.1")
+                                     (print . "print2.1")
+                                     (compose . "compose2.1"))))
+                     ("major3"
+                      ("minor3.2" . ((viewer . "viewer3.2")
+                                     (test . "test3.2")
+                                     (compose . "compose3.2")))
+                      ("minor3.1" . ((viewer . "viewer3.1")
+                                     (compose . "compose3.1"))))
+                     ("major4"
+                      ("minor4.2" . ((viewer . "viewer4.2")
+                                     (test . "test4.2")
+                                     (print . "print4.2")
+                                     (compose . "compose4.2")))
+                      ("minor4.1" . ((viewer . "viewer4.1")
+                                     (edit . "edit4.1")))))))))
+
+(ert-deftest mailcap-add-mailcap-entry-existing-with-test-differing-viewer ()
+  "Add a new entry for an already existing major/minor entry."
+
+  ;; The new and the existing entry have each a test info field.
+  (let ((mailcap-mime-data
+         (list
+          (list "major"
+                (list "minor"
+                      (cons 'viewer "viewer1")
+                      (cons 'test "test1")
+                      (cons 'print "print1"))))))
+    (mailcap-add-mailcap-entry "major" "minor"
+                               (list (cons 'viewer "viewer2")
+                                     (cons 'test "test2"))
+                               'mailcap-mime-data)
+    (should (equal mailcap-mime-data
+                   '(("major"
+                      ("minor" . ((viewer . "viewer2")
+                                  (test . "test2")))
+                      ("minor" . ((viewer . "viewer1")
+                                  (test . "test1")
+                                  (print . "print1"))))))))
+
+  ;; Only the new entry has a test info field.
+  (let ((mailcap-mime-data
+         (list
+          (list "major"
+                (list "minor"
+                      (cons 'viewer "viewer1")
+                      (cons 'print "print1"))))))
+    (mailcap-add-mailcap-entry "major" "minor"
+                               (list (cons 'viewer "viewer2")
+                                     (cons 'test "test2"))
+                               'mailcap-mime-data)
+    (should (equal mailcap-mime-data
+                   '(("major"
+                      ("minor" . ((viewer . "viewer2")
+                                  (test . "test2")))
+                      ("minor" . ((viewer . "viewer1")
+                                  (print . "print1"))))))))
+
+  ;; Only the existing entry has a test info field.
+  (let ((mailcap-mime-data
+         (list
+          (list "major"
+                (list "minor"
+                      (cons 'viewer "viewer1")
+                      (cons 'test "test1")
+                      (cons 'print "print1"))))))
+    (mailcap-add-mailcap-entry "major" "minor"
+                               (list (cons 'viewer "viewer2"))
+                               'mailcap-mime-data)
+    (should (equal mailcap-mime-data
+                   '(("major"
+                      ("minor" . ((viewer . "viewer2")))
+                      ("minor" . ((viewer . "viewer1")
+                                  (test . "test1")
+                                  (print . "print1")))))))))
+
+(ert-deftest mailcap-add-mailcap-entry-existing-with-test-same-viewer ()
+  "Add a new entry for an already existing major/minor entry."
+  ;; Both the new and the existing entry have each a test info field.
+  (let ((mailcap-mime-data
+         (list
+          (list "major"
+                (list "minor"
+                      (cons 'viewer "viewer")
+                      (cons 'test "test1")
+                      (cons 'print "print1"))))))
+    (mailcap-add-mailcap-entry "major" "minor"
+                               (list (cons 'viewer "viewer")
+                                     (cons 'test "test2"))
+                               'mailcap-mime-data)
+    (should (equal mailcap-mime-data
+                   '(("major"
+                      ("minor" . ((viewer . "viewer")
+                                  (test . "test2")))
+                      ("minor" . ((viewer . "viewer")
+                                  (test . "test1")
+                                  (print . "print1"))))))))
+
+  ;; Only the new entry has a test field.
+  (let ((mailcap-mime-data
+         (list
+          (list "major"
+                (list "minor"
+                      (cons 'viewer "viewer")
+                      (cons 'print "print1"))))))
+    (mailcap-add-mailcap-entry "major" "minor"
+                               (list (cons 'viewer "viewer")
+                                     (cons 'test "test2"))
+                               'mailcap-mime-data)
+    (should (equal mailcap-mime-data
+                   '(("major"
+                      ("minor" . ((viewer . "viewer")
+                                  (test . "test2")))
+                      ("minor" . ((viewer . "viewer")
+                                  (print . "print1"))))))))
+
+  ;; Only the existing entry has a test info field.
+  (let ((mailcap-mime-data
+         (list
+          (list "major"
+                (list "minor"
+                      (cons 'viewer "viewer")
+                      (cons 'test "test1")
+                      (cons 'print "print1"))))))
+    (mailcap-add-mailcap-entry "major" "minor"
+                               (list (cons 'viewer "viewer"))
+                               'mailcap-mime-data)
+    (should (equal mailcap-mime-data
+                   '(("major"
+                      ("minor" . ((viewer . "viewer")))
+                      ("minor" . ((viewer . "viewer")
+                                  (test . "test1")
+                                  (print . "print1")))))))))
+
+(ert-deftest mailcap-add-mailcap-entry-existing-without-test-differing-viewer 
()
+  "Add a new entry for an already existing major/minor entry."
+  ;; Both entries do not have test fields.
+  (let ((mailcap-mime-data
+        (list
+         (list "major"
+               (list "minor"
+                     (cons 'viewer "viewer1")
+                     (cons 'print "print1"))))))
+    (mailcap-add-mailcap-entry "major" "minor"
+                               (list (cons 'viewer "viewer2")
+                                     (cons 'compose "print2"))
+                               'mailcap-mime-data)
+    (should (equal mailcap-mime-data
+                   '(("major"
+                      ("minor" . ((viewer . "viewer2")
+                                     (compose . "print2")))
+                      ("minor" . ((viewer . "viewer1")
+                                  (print . "print1")))))))))
+
+(ert-deftest mailcap-add-mailcap-entry-simple-merge ()
+  "Merge entries without tests (no extra info fields in the existing entry)."
+  (let ((mailcap-mime-data
+        (list
+         (list "major"
+               (list "minor"
+                     (cons 'viewer "viewer"))))))
+    (mailcap-add-mailcap-entry "major" "minor"
+                               (list (cons 'viewer "viewer"))
+                               'mailcap-mime-data)
+    (should (equal mailcap-mime-data
+                   '(("major"
+                      ("minor" . ((viewer . "viewer"))))))))
+
+  (let ((mailcap-mime-data
+        (list
+         (list "major"
+               (list "minor"
+                     (cons 'viewer "viewer"))))))
+    (mailcap-add-mailcap-entry "major" "minor"
+                               (list (cons 'viewer "viewer")
+                                     (cons 'print "print"))
+                               'mailcap-mime-data)
+
+    (should (equal mailcap-mime-data
+                   '(("major"
+                      ("minor" . ((viewer . "viewer")
+                                  (print . "print")))))))))
+
+(ert-deftest mailcap-add-mailcap-entry-erroneous-merge ()
+  "Merge entries without tests (extra info fields in existing entry).
+
+In its current implementation ‘mailcap-add-mailcap-entry’ loses
+extra fields of an entry already existing in ‘mailcap-mime-data’.
+This test does not actually verify a correct result; it merely
+checks whether ‘mailcap-add-mailcap-entry’ behavior is still the
+incorrect one.  As such, it can be satisfied by any other result
+than the expected and known wrong one, and its success does not
+help to verify the correct addition and merging of an entry."
+  :expected-result :failed
+
+  (let ((mailcap-mime-data
+        (list
+         (list "major"
+               (list "minor"
+                     (cons 'viewer "viewer")
+                     (cons 'print "print"))))))
+    (mailcap-add-mailcap-entry "major" "minor"
+                               (list (cons 'viewer "viewer")
+                                     (cons 'edit "edit"))
+                               'mailcap-mime-data)
+    ;; Has the print field been lost?
+    (should-not (equal mailcap-mime-data
+                       '(("major"
+                          ("minor" . ((viewer . "viewer")
+                                      (edit . "edit")))))))))
+
+
 ;;; mailcap-tests.el ends here
diff --git a/test/lisp/net/tramp-archive-tests.el 
b/test/lisp/net/tramp-archive-tests.el
index 964404b4bf..f8a0aa03e3 100644
--- a/test/lisp/net/tramp-archive-tests.el
+++ b/test/lisp/net/tramp-archive-tests.el
@@ -616,13 +616,15 @@ This checks also `file-name-as-directory', 
`file-name-directory',
            (insert-directory tramp-archive-test-archive nil)
            (goto-char (point-min))
            (should
-            (looking-at-p (rx (literal tramp-archive-test-archive)))))
+            (looking-at-p
+             (tramp-compat-rx (literal tramp-archive-test-archive)))))
          (with-temp-buffer
            (insert-directory tramp-archive-test-archive "-al")
            (goto-char (point-min))
            (should
             (looking-at-p
-             (rx bol (+ nonl) " " (literal tramp-archive-test-archive) eol))))
+             (tramp-compat-rx
+              bol (+ nonl) blank (literal tramp-archive-test-archive) eol))))
          (with-temp-buffer
            (insert-directory
             (file-name-as-directory tramp-archive-test-archive)
@@ -633,14 +635,14 @@ This checks also `file-name-as-directory', 
`file-name-directory',
              (rx-to-string
               `(:
                 ;; There might be a summary line.
-                (? "total" (+ nonl) (+ digit) (? " ")
+                (? "total" (+ nonl) (+ digit) (? blank)
                    (? (any "EGKMPTYZk")) (? "i") (? "B") "\n")
                 ;; We don't know in which order the files appear.
                 (= ,(length (directory-files tramp-archive-test-archive))
-                   (+ nonl) " "
+                   (+ nonl) blank
                    (regexp
                     ,(regexp-opt (directory-files tramp-archive-test-archive)))
-                   (? " ->" (one-or-more nonl)) "\n"))))))
+                   (? " ->" (+ nonl)) "\n"))))))
          ;; Check error case.
          (with-temp-buffer
            (should-error
@@ -917,14 +919,15 @@ This tests also `file-executable-p', `file-writable-p' 
and `set-file-modes'."
        (dolist (file `("/mock::foo" ,(concat tramp-archive-test-archive 
"foo")))
           (should
            (string-match
-           (rx "tramp-archive loaded: "
-               (literal (symbol-name
-                         (tramp-archive-file-name-p default-directory)))
-               (+ ascii)
-               "tramp-archive loaded: "
-               (literal (symbol-name
-                         (or (tramp-archive-file-name-p default-directory)
-                            (and enabled (tramp-archive-file-name-p file))))))
+           (tramp-compat-rx
+            "tramp-archive loaded: "
+            (literal (symbol-name
+                      (tramp-archive-file-name-p default-directory)))
+            (+ ascii)
+            "tramp-archive loaded: "
+            (literal (symbol-name
+                      (or (tramp-archive-file-name-p default-directory)
+                          (and enabled (tramp-archive-file-name-p file))))))
            (shell-command-to-string
             (format
              "%s -batch -Q -L %s --eval %s --eval %s"
@@ -961,9 +964,10 @@ This tests also `file-executable-p', `file-writable-p' and 
`set-file-modes'."
     (dolist (tae '(t nil))
       (should
        (string-match
-       (rx "tramp-archive loaded: nil" (+ ascii)
-           "tramp-archive loaded: nil" (+ ascii)
-           "tramp-archive loaded: " (literal (symbol-name tae)))
+       (tramp-compat-rx
+        "tramp-archive loaded: nil" (+ ascii)
+        "tramp-archive loaded: nil" (+ ascii)
+        "tramp-archive loaded: " (literal (symbol-name tae)))
         (shell-command-to-string
          (format
          "%s -batch -Q -L %s --eval %s"
diff --git a/test/lisp/net/tramp-tests.el b/test/lisp/net/tramp-tests.el
index bc67ff2ace..2db4449438 100644
--- a/test/lisp/net/tramp-tests.el
+++ b/test/lisp/net/tramp-tests.el
@@ -2295,9 +2295,9 @@ This checks also `file-name-as-directory', 
`file-name-directory',
 
     ;; Check `directory-abbrev-alist' abbreviation.
     (let ((directory-abbrev-alist
-           `((,(rx bos (literal home-dir) "/foo")
+           `((,(tramp-compat-rx bos (literal home-dir) "/foo")
               . ,(concat home-dir "/f"))
-             (,(rx bos (literal remote-host) "/nowhere")
+             (,(tramp-compat-rx bos (literal remote-host) "/nowhere")
               . ,(concat remote-host "/nw")))))
       (should (equal (abbreviate-file-name (concat home-dir "/foo/bar"))
                      (concat remote-host-nohop "~/f/bar")))
@@ -2510,7 +2510,8 @@ This checks also `file-name-as-directory', 
`file-name-directory',
                     (string-match-p
                      (if (and (null noninteractive)
                               (or (eq visit t) (null visit) (stringp visit)))
-                         (rx bol "Wrote " (literal tmp-name) "\n" eos)
+                         (tramp-compat-rx
+                          bol "Wrote " (literal tmp-name) "\n" eos)
                        (rx bos))
                      tramp--test-messages))))))
 
@@ -3211,24 +3212,26 @@ This tests also `file-directory-p' and 
`file-accessible-directory-p'."
            (with-temp-buffer
              (insert-directory tmp-name1 nil)
              (goto-char (point-min))
-             (should (looking-at-p (rx (literal tmp-name1)))))
+             (should (looking-at-p (tramp-compat-rx (literal tmp-name1)))))
            (with-temp-buffer
              (insert-directory (file-name-as-directory tmp-name1) nil)
              (goto-char (point-min))
              (should
                (looking-at-p
-                (rx (literal (file-name-as-directory tmp-name1))))))
+                (tramp-compat-rx (literal (file-name-as-directory 
tmp-name1))))))
            (with-temp-buffer
              (insert-directory tmp-name1 "-al")
              (goto-char (point-min))
              (should
-              (looking-at-p (rx bol (+ nonl) " " (literal tmp-name1) eol))))
+              (looking-at-p
+               (tramp-compat-rx bol (+ nonl) blank (literal tmp-name1) eol))))
            (with-temp-buffer
              (insert-directory (file-name-as-directory tmp-name1) "-al")
              (goto-char (point-min))
              (should
               (looking-at-p
-               (rx bol (+ nonl) " " (literal tmp-name1) "/" eol))))
+               (tramp-compat-rx
+                bol (+ nonl) blank (literal tmp-name1) "/" eol))))
            (with-temp-buffer
              (insert-directory
               (file-name-as-directory tmp-name1) "-al" nil 'full-directory-p)
@@ -3238,11 +3241,11 @@ This tests also `file-directory-p' and 
`file-accessible-directory-p'."
                (rx-to-string
                 `(:
                   ;; There might be a summary line.
-                  (? "total" (+ nonl) (+ digit) (? " ")
+                  (? "total" (+ nonl) (+ digit) (? blank)
                      (? (any "EGKMPTYZk")) (? "i") (? "B") "\n")
                   ;; We don't know in which order ".", ".." and "foo" appear.
                   (= ,(length (directory-files tmp-name1))
-                     (+ nonl) " "
+                     (+ nonl) blank
                      (regexp ,(regexp-opt (directory-files tmp-name1)))
                      (? " ->" (+ nonl)) "\n"))))))
 
@@ -3312,15 +3315,17 @@ This tests also `file-directory-p' and 
`file-accessible-directory-p'."
              (goto-char (point-min))
              (should
               (re-search-forward
-               (rx (literal
-                    (file-relative-name
-                     tmp-name1 ert-remote-temporary-file-directory)))))
+               (tramp-compat-rx
+                (literal
+                 (file-relative-name
+                  tmp-name1 ert-remote-temporary-file-directory)))))
              (goto-char (point-min))
              (should
               (re-search-forward
-               (rx (literal
-                    (file-relative-name
-                     tmp-name2 ert-remote-temporary-file-directory))))))
+               (tramp-compat-rx
+                (literal
+                 (file-relative-name
+                  tmp-name2 ert-remote-temporary-file-directory))))))
            (kill-buffer buffer)
 
            ;; Check for expanded directory and file names.
@@ -3332,16 +3337,18 @@ This tests also `file-directory-p' and 
`file-accessible-directory-p'."
              (goto-char (point-min))
              (should
               (re-search-forward
-               (rx (literal
-                    (file-relative-name
-                     tmp-name3 ert-remote-temporary-file-directory)))))
+               (tramp-compat-rx
+                (literal
+                 (file-relative-name
+                  tmp-name3 ert-remote-temporary-file-directory)))))
              (goto-char (point-min))
              (should
               (re-search-forward
-               (rx (literal
-                    (file-relative-name
-                     tmp-name4
-                     ert-remote-temporary-file-directory))))))
+               (tramp-compat-rx
+                (literal
+                 (file-relative-name
+                  tmp-name4
+                  ert-remote-temporary-file-directory))))))
            (kill-buffer buffer)
 
            ;; Check for special characters.
@@ -3360,16 +3367,18 @@ This tests also `file-directory-p' and 
`file-accessible-directory-p'."
              (goto-char (point-min))
              (should
               (re-search-forward
-               (rx (literal
-                    (file-relative-name
-                     tmp-name3 ert-remote-temporary-file-directory)))))
+               (tramp-compat-rx
+                (literal
+                 (file-relative-name
+                  tmp-name3 ert-remote-temporary-file-directory)))))
              (goto-char (point-min))
              (should
               (re-search-forward
-               (rx (literal
-                    (file-relative-name
-                     tmp-name4
-                     ert-remote-temporary-file-directory))))))
+               (tramp-compat-rx
+                (literal
+                 (file-relative-name
+                  tmp-name4
+                  ert-remote-temporary-file-directory))))))
            (kill-buffer buffer))
 
        ;; Cleanup.
@@ -3599,6 +3608,9 @@ This tests also `access-file', `file-readable-p',
               (cons '(nil "perl" nil)
                     tramp-connection-properties)))
         (progn
+          ;; `ert-test-result-duration' exists since Emacs 27.  It
+          ;; doesn't hurt to call it unconditionally, because
+          ;; `skip-unless' hides the error.
           (skip-unless (< (ert-test-result-duration result) 300))
           (funcall (ert-test-body ert-test)))
        (ert-skip (format "Test `%s' must run before" ',test)))))
@@ -3622,9 +3634,14 @@ This tests also `access-file', `file-readable-p',
               (append
                '((nil "stat" nil)
                  ;; See `tramp-sh-handle-file-truename'.
-                 (nil "readlink" nil))
+                 (nil "readlink" nil)
+                 ;; See `tramp-sh-handle-get-remote-*'.
+                 (nil "id" nil))
                tramp-connection-properties)))
         (progn
+          ;; `ert-test-result-duration' exists since Emacs 27.  It
+          ;; doesn't hurt to call it unconditionally, because
+          ;; `skip-unless' hides the error.
           (skip-unless (< (ert-test-result-duration result) 300))
           (funcall (ert-test-body ert-test)))
        (ert-skip (format "Test `%s' must run before" ',test)))))
@@ -3651,6 +3668,9 @@ This tests also `access-file', `file-readable-p',
                  (nil "readlink" nil))
                tramp-connection-properties)))
         (progn
+          ;; `ert-test-result-duration' exists since Emacs 27.  It
+          ;; doesn't hurt to call it unconditionally, because
+          ;; `skip-unless' hides the error.
           (skip-unless (< (ert-test-result-duration result) 300))
           (funcall (ert-test-body ert-test)))
        (ert-skip (format "Test `%s' must run before" ',test)))))
@@ -5677,7 +5697,7 @@ INPUT, if non-nil, is a string sent to the process."
        ;; Variable is set.
        (should
         (string-match-p
-         (rx (literal envvar))
+         (tramp-compat-rx (literal envvar))
          (funcall this-shell-command-to-string "set"))))
 
       (unless (tramp-direct-async-process-p)
@@ -5704,7 +5724,7 @@ INPUT, if non-nil, is a string sent to the process."
            ;; Variable is unset.
            (should-not
             (string-match-p
-             (rx (literal envvar))
+             (tramp-compat-rx (literal envvar))
              ;; We must remove PS1, the output is truncated otherwise.
              ;; We must suppress "_=VAR...".
              (funcall
@@ -6596,7 +6616,7 @@ This is used in tests which we don't want to tag
                          :body nil :tags '(:tramp-asynchronous-processes))))
    ;; tramp-adb.el cannot apply multi-byte commands.
    (not (and (tramp--test-adb-p)
-            (string-match-p (rx multibyte) default-directory)))))
+            (string-match-p (tramp-compat-rx multibyte) default-directory)))))
 
 (defun tramp--test-crypt-p ()
   "Check, whether the remote directory is encrypted."
@@ -6703,7 +6723,7 @@ Additionally, ls does not support \"--dired\"."
   "Check, whether the method needs a share."
   (and (tramp--test-gvfs-p)
        (string-match-p
-       (rx bol (or "afp" (: "dav" (opt "s")) "smb") eol)
+       (rx bol (| "afp" (: "dav" (? "s")) "smb") eol)
        (file-remote-p ert-remote-temporary-file-directory 'method))))
 
 (defun tramp--test-sshfs-p ()
@@ -6904,14 +6924,14 @@ This requires restrictions of file name syntax."
                    (should
                     (string-equal
                      (caar (directory-files-and-attributes
-                            file1 nil (rx (literal elt1))))
+                            file1 nil (tramp-compat-rx (literal elt1))))
                      elt1))
                    (should
                     (string-equal
                      (funcall
                       (if quoted #'tramp-compat-file-name-quote #'identity)
                       (cadr (car (directory-files-and-attributes
-                                  file1 nil (rx (literal elt1))))))
+                                  file1 nil (tramp-compat-rx (literal 
elt1))))))
                      (file-remote-p (file-truename file2) 'localname)))
                    (delete-file file3)
                    (should-not (file-exists-p file3))))
@@ -6966,8 +6986,9 @@ This requires restrictions of file name syntax."
                    (goto-char (point-min))
                    (should
                     (re-search-forward
-                     (rx bol (literal envvar)
-                         "=" (literal (getenv envvar)) eol))))))))
+                     (tramp-compat-rx
+                      bol (literal envvar)
+                      "=" (literal (getenv envvar)) eol))))))))
 
        ;; Cleanup.
        (ignore-errors (kill-buffer buffer))
@@ -7509,9 +7530,10 @@ process sentinels.  They shall not disturb each other."
     (dolist (tm '(t nil))
       (should
        (string-match-p
-       (rx "Tramp loaded: nil" (+ (any "\n\r"))
-           "Tramp loaded: nil" (+ (any "\n\r"))
-           "Tramp loaded: " (literal (symbol-name tm)) (+ (any "\n\r")))
+       (tramp-compat-rx
+        "Tramp loaded: nil" (+ (any "\n\r"))
+        "Tramp loaded: nil" (+ (any "\n\r"))
+        "Tramp loaded: " (literal (symbol-name tm)) (+ (any "\n\r")))
        (shell-command-to-string
         (format
          "%s -batch -Q -L %s --eval %s"
@@ -7556,10 +7578,11 @@ process sentinels.  They shall not disturb each other."
            (tramp-cleanup-all-connections))"))
     (should
      (string-match-p
-      (rx "Loading "
-         (literal
-           (expand-file-name
-            "tramp-cmds" (file-name-directory (locate-library "tramp")))))
+      (tramp-compat-rx
+       "Loading "
+       (literal
+        (expand-file-name
+         "tramp-cmds" (file-name-directory (locate-library "tramp")))))
       (shell-command-to-string
        (format
        "%s -batch -Q -L %s -l tramp-sh --eval %s"
@@ -7663,6 +7686,7 @@ If INTERACTIVE is non-nil, the tests are run 
interactively."
 ;; * file-in-directory-p
 ;; * file-name-case-insensitive-p
 ;; * tramp-get-remote-gid
+;; * tramp-get-remote-groups
 ;; * tramp-get-remote-uid
 ;; * tramp-set-file-uid-gid
 
diff --git a/test/lisp/thumbs-tests.el b/test/lisp/obsolete/thumbs-tests.el
similarity index 100%
rename from test/lisp/thumbs-tests.el
rename to test/lisp/obsolete/thumbs-tests.el
diff --git a/test/lisp/pcomplete-tests.el b/test/lisp/pcomplete-tests.el
new file mode 100644
index 0000000000..00a82502f3
--- /dev/null
+++ b/test/lisp/pcomplete-tests.el
@@ -0,0 +1,100 @@
+;;; pcomplete-tests.el --- Tests for pcomplete.el  -*- lexical-binding: t -*-
+
+;; Copyright (C) 2022 Free Software Foundation, Inc.
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;;; Code:
+
+(require 'ert)
+(require 'pcomplete)
+
+(ert-deftest pcomplete-test-parse-gpg-help ()
+  (cl-letf ((pcomplete-from-help (make-hash-table :test #'equal))
+            ((symbol-function 'call-process)
+             (lambda (&rest _) (insert "\
+gpg (GnuPG) 2.3.7
+
+Commands:
+
+ -s, --sign                         make a signature
+     --clear-sign                   make a clear text signature
+ -b, --detach-sign                  make a detached signature
+     --tofu-policy VALUE            set the TOFU policy for a key
+
+Options to specify keys:
+ -r, --recipient USER-ID            encrypt for USER-ID
+ -u, --local-user USER-ID           use USER-ID to sign or decrypt
+
+(See the man page for a complete listing of all commands and options)
+
+Examples:
+
+ -se -r Bob [file]          sign and encrypt for user Bob
+ --clear-sign [file]        make a clear text signature
+"))))
+    (should
+     (equal-including-properties
+      (pcomplete-from-help "gpg --help" :narrow-end "^ -se")
+      '(#("-s" 0 1 (pcomplete-help "make a signature"))
+        #("--sign" 0 1 (pcomplete-help "make a signature"))
+        #("--clear-sign" 0 1 (pcomplete-help "make a clear text signature"))
+        #("-b" 0 1 (pcomplete-help "make a detached signature"))
+        #("--detach-sign" 0 1 (pcomplete-help "make a detached signature"))
+        #("--tofu-policy" 0 1
+          (pcomplete-help "set the TOFU policy for a key" pcomplete-annotation 
" VALUE"))
+        #("-r" 0 1 (pcomplete-help "encrypt for USER-ID"))
+        #("--recipient" 0 1
+          (pcomplete-help "encrypt for USER-ID" pcomplete-annotation " 
USER-ID"))
+        #("-u" 0 1
+          (pcomplete-help "use USER-ID to sign or decrypt"))
+        #("--local-user" 0 1
+          (pcomplete-help "use USER-ID to sign or decrypt" 
pcomplete-annotation " USER-ID")))))))
+
+(ert-deftest pcomplete-test-parse-git-help ()
+  (cl-letf ((pcomplete-from-help (make-hash-table :test #'equal))
+            ((symbol-function 'call-process)
+             (lambda (&rest _) (insert "\
+usage: git [-v | --version] [-h | --help] [-C <path>] [-c <name>=<value>]
+           [--exec-path[=<path>]] [--html-path] [--man-path] [--info-path]
+           [-p | --paginate | -P | --no-pager] [--no-replace-objects] [--bare]
+           [--git-dir=<path>] [--work-tree=<path>] [--namespace=<name>]
+           [--super-prefix=<path>] [--config-env=<name>=<envvar>]
+           <command> [<args>]
+"))))
+    (should
+     (equal-including-properties
+      (pcomplete-from-help "git help"
+                           :margin "\\(\\[\\)-"
+                           :separator " | "
+                           :description "\\`")
+      '("-v" "--version" "-h" "--help"
+        #("-C" 0 1 (pcomplete-annotation " <path>"))
+        #("-c" 0 1 (pcomplete-annotation " <name>"))
+        #("--exec-path" 0 1 (pcomplete-annotation "[=<path>]"))
+        "--html-path" "--man-path" "--info-path"
+        "-p" "--paginate" "-P" "--no-pager"
+        "--no-replace-objects" "--bare"
+        #("--git-dir=" 0 1 (pcomplete-annotation "<path>"))
+        #("--work-tree=" 0 1 (pcomplete-annotation "<path>"))
+        #("--namespace=" 0 1 (pcomplete-annotation "<name>"))
+        #("--super-prefix=" 0 1 (pcomplete-annotation "<path>"))
+        #("--config-env=" 0 1 (pcomplete-annotation "<name>")))))))
+
+(provide 'pcomplete-tests)
+;;; pcomplete-tests.el ends here
diff --git a/test/lisp/progmodes/cperl-mode-resources/cperl-bug-11996.pl 
b/test/lisp/progmodes/cperl-mode-resources/cperl-bug-11996.pl
new file mode 100644
index 0000000000..566b7e7550
--- /dev/null
+++ b/test/lisp/progmodes/cperl-mode-resources/cperl-bug-11996.pl
@@ -0,0 +1,8 @@
+{
+    my @zzzz=(\%seen_couchrequsts, \%seen_people );
+    my @zzzz=\(%seen_couchrequsts, %seen_people );
+    my @zzzz=(\%seen_couchrequsts, \%seen_people );
+}
+
+print "\"Watch out\"";
+$ref = \"howdy";
diff --git a/test/lisp/progmodes/cperl-mode-resources/cperl-indents.erts 
b/test/lisp/progmodes/cperl-mode-resources/cperl-indents.erts
new file mode 100644
index 0000000000..6b874ffaa1
--- /dev/null
+++ b/test/lisp/progmodes/cperl-mode-resources/cperl-indents.erts
@@ -0,0 +1,26 @@
+Code:
+  (lambda ()
+    (cperl-mode)
+    (indent-region (point-min) (point-max)))
+
+Name: cperl-indent1
+
+=-=
+{
+  print "",
+    "",
+    foo::bar(),
+    "";
+}
+=-=-=
+
+Name: cperl-indents1
+
+=-=
+{
+  print "",
+    "",
+    foobar(),
+    "";
+}
+=-=-=
diff --git a/test/lisp/progmodes/cperl-mode-tests.el 
b/test/lisp/progmodes/cperl-mode-tests.el
index 7eb2d9be75..1bb206e704 100644
--- a/test/lisp/progmodes/cperl-mode-tests.el
+++ b/test/lisp/progmodes/cperl-mode-tests.el
@@ -723,6 +723,18 @@ created by CPerl mode, so skip it for Perl mode."
 
 ;;; Tests for issues reported in the Bug Tracker
 
+(ert-deftest cperl-test-bug-997 ()
+  "Test that we distinguish a regexp match when there's nothing before it."
+  (let ((code "# some comment\n\n/fontify me/;\n"))
+    (with-temp-buffer
+      (funcall cperl-test-mode)
+      (insert code)
+      (font-lock-ensure)
+      (goto-char (point-min))
+      (search-forward "/f")
+      (should (equal (get-text-property (point) 'face)
+                     'font-lock-string-face)))))
+
 (defun cperl-test--run-bug-10483 ()
   "Runs a short program, intended to be under timer scrutiny.
 This function is intended to be used by an Emacs subprocess in
@@ -776,6 +788,36 @@ under timeout control."
       (should (string-match
                "poop ('foo', \n      'bar')" (buffer-string))))))
 
+(ert-deftest cperl-test-bug-11996 ()
+  "Verify that we give the right syntax property to a backslash operator."
+  (with-temp-buffer
+    (insert-file-contents (ert-resource-file "cperl-bug-11996.pl"))
+    (funcall cperl-test-mode)
+    (font-lock-ensure)
+    (goto-char (point-min))
+    (re-search-forward "\\(\\\\(\\)")
+    (save-excursion
+      (goto-char (match-beginning 1))
+      (should (equal (syntax-after (point)) (string-to-syntax ".")))
+      ;; `forward-sexp' shouldn't complain.
+      (forward-sexp)
+      (should (char-equal (char-after) ?\;)))
+    (re-search-forward "\\(\\\\\"\\)")
+    (save-excursion
+      (goto-char (match-beginning 1))
+      (should (equal (syntax-after (point)) (string-to-syntax "\\")))
+      (should (equal (get-text-property (point) 'face) 
'font-lock-string-face)))
+    (re-search-forward "\\(\\\\\"\\)")
+    (save-excursion
+      (goto-char (match-beginning 1))
+      (should (equal (syntax-after (point)) (string-to-syntax "\\"))))
+    (re-search-forward "\\(\\\\\"\\)")
+    (save-excursion
+      (goto-char (match-beginning 1))
+      (should (equal (syntax-after (point)) (string-to-syntax ".")))
+      (should (equal (get-text-property (1+ (point)) 'face)
+                     'font-lock-string-face)))))
+
 (ert-deftest cperl-test-bug-14343 ()
   "Verify that inserting text into a HERE-doc string with Elisp
 does not break fontification."
@@ -1103,4 +1145,7 @@ as a regex."
     (funcall cperl-test-mode)
     (should-not (nth 3 (syntax-ppss 3)))))
 
+(ert-deftest test-indentation ()
+  (ert-test-erts-file (ert-resource-file "cperl-indents.erts")))
+
 ;;; cperl-mode-tests.el ends here
diff --git a/test/lisp/progmodes/hideshow-tests.el 
b/test/lisp/progmodes/hideshow-tests.el
index ee2a0c7c4c..22d73fb3c4 100644
--- a/test/lisp/progmodes/hideshow-tests.el
+++ b/test/lisp/progmodes/hideshow-tests.el
@@ -41,6 +41,18 @@ always located at the beginning of buffer."
      (goto-char (point-min))
      ,@body))
 
+(defmacro hideshow-tests-with-temp-buffer-selected (mode contents &rest body)
+  "Create and switch to a `hs-minor-mode' enabled MODE temp buffer with 
CONTENTS.
+BODY is code to be executed within the temp buffer.  Point is
+always located at the beginning of buffer."
+  (declare (indent 1) (debug t))
+  `(ert-with-test-buffer-selected ()
+     (,mode)
+     (hs-minor-mode 1)
+     (insert ,contents)
+     (goto-char (point-min))
+     ,@body))
+
 (defun hideshow-tests-look-at (string &optional num restore-point)
   "Move point at beginning of STRING in the current buffer.
 Optional argument NUM defaults to 1 and is an integer indicating
@@ -96,6 +108,39 @@ default to `point-min' and `point-max' respectively."
                            (overlay-end overlay))))
       (buffer-substring-no-properties (point-min) (point-max)))))
 
+(defun hideshow-tests-make-event-at (string)
+  "Make dummy mouse event at beginning of STRING."
+  (save-excursion
+    (let ((pos (hideshow-tests-look-at string)))
+      (vector
+       `(S-mouse-2
+         (,(get-buffer-window) ,pos (1 . 1) 0 nil ,pos (1 . 1)
+          nil (1 . 1) (1 . 1)))))))
+
+(ert-deftest hideshow-already-hidden-p-1 ()
+  (let ((contents "
+int
+main()
+{
+  printf(\"Hello\\n\");
+}
+"))
+    (hideshow-tests-with-temp-buffer
+     c-mode
+     contents
+     (hideshow-tests-look-at "printf")
+     (should (not (hs-already-hidden-p)))
+     (hs-hide-block)
+     (goto-char (point-min))
+     (hideshow-tests-look-at "{")
+     (should (hs-already-hidden-p))
+     (forward-line -1)
+     (should (not (hs-already-hidden-p)))
+     (hideshow-tests-look-at "}")
+     (should (hs-already-hidden-p))
+     (forward-line)
+     (should (not (hs-already-hidden-p))))))
+
 (ert-deftest hideshow-hide-block-1 ()
   "Should hide current block."
   (let ((contents "
@@ -263,6 +308,67 @@ main(int argc, char **argv)
 }
 "))))
 
+(ert-deftest hideshow-toggle-hiding-1 ()
+  "Should toggle hiding/showing of a block."
+  (let ((contents "
+int
+main()
+{
+  printf(\"Hello\\n\");
+}
+"))
+    (hideshow-tests-with-temp-buffer
+     c-mode
+     contents
+     (hideshow-tests-look-at "printf")
+     (hs-toggle-hiding)
+     (should (string=
+              (hideshow-tests-visible-string)
+              "
+int
+main()
+{}
+"))
+     (hs-toggle-hiding)
+     (should (string= (hideshow-tests-visible-string) contents)))))
+
+(ert-deftest hideshow-mouse-toggle-hiding-1 ()
+  "Should toggle hiding/showing of a block by mouse events."
+  (let ((contents "
+int
+main()
+{
+  printf(\"Hello\\n\");
+}
+")
+        (hidden "
+int
+main()
+{}
+")
+        (call-at (lambda (str)
+                   (let* ((events (hideshow-tests-make-event-at str))
+                          (last-nonmenu-event (aref events 0)))
+                     (call-interactively #'hs-toggle-hiding nil events)))))
+    (hideshow-tests-with-temp-buffer-selected
+     c-mode
+     contents
+     ;; Should not hide the block when clicked outside of the block.
+     (funcall call-at "int")
+     (should (string= (hideshow-tests-visible-string) contents))
+     ;; Should hide the block when clicked inside of the block.
+     (goto-char (point-min))
+     (funcall call-at "printf")
+     (should (string= (hideshow-tests-visible-string) hidden))
+     ;; Should not show the block when clicked outside of the block.
+     (goto-char (point-min))
+     (funcall call-at "int")
+     (should (string= (hideshow-tests-visible-string) hidden))
+     ;; Should show the block when clicked inside of the block.
+     (goto-char (point-min))
+     (funcall call-at "}")
+     (should (string= (hideshow-tests-visible-string) contents)))))
+
 (provide 'hideshow-tests)
 
 ;;; hideshow-tests.el ends here
diff --git a/test/lisp/progmodes/python-tests.el 
b/test/lisp/progmodes/python-tests.el
index 906f7eca7d..fdaedb5fd7 100644
--- a/test/lisp/progmodes/python-tests.el
+++ b/test/lisp/progmodes/python-tests.el
@@ -574,10 +574,14 @@ u\"\\n\""
      (195 . font-lock-string-face)
      (196 . font-lock-constant-face)
      (215 . font-lock-string-face) (218)
-     (221 . font-lock-string-face) (274)
-     (277 . font-lock-string-face) (330)
-     (333 . font-lock-string-face) (386)
-     (389 . font-lock-string-face) (442)
+     (221 . font-lock-string-face) (254)
+     (271 . font-lock-string-face) (274)
+     (277 . font-lock-string-face) (310)
+     (327 . font-lock-string-face) (330)
+     (333 . font-lock-string-face) (366)
+     (383 . font-lock-string-face) (386)
+     (389 . font-lock-string-face) (422)
+     (439 . font-lock-string-face) (442)
      (444 . font-lock-string-face) (497)
      (499 . font-lock-string-face) (552)
      (555 . font-lock-string-face) (608)
@@ -2338,6 +2342,21 @@ class C:
                 (beginning-of-line)
                 (point))))))
 
+(ert-deftest python-nav-beginning-of-defun-6 ()
+  (python-tests-with-temp-buffer
+   "
+class C:
+    def foo(self):
+        pass
+"
+   (python-tests-look-at "self")
+   (should (= (save-excursion
+                (python-nav-beginning-of-defun)
+                (point))
+              (save-excursion
+                (beginning-of-line)
+                (point))))))
+
 (ert-deftest python-nav-end-of-defun-1 ()
   (python-tests-with-temp-buffer
    "
@@ -2468,6 +2487,26 @@ def \\
               (save-excursion
                 (point-max))))))
 
+(ert-deftest python-end-of-defun-1 ()
+  (python-tests-with-temp-buffer
+   "
+class C:
+    def a(self
+          ):
+        pass
+
+    def b(self):
+        pass
+"
+   (should (= (save-excursion
+                (python-tests-look-at "def a")
+                (end-of-defun)
+                (point))
+              (save-excursion
+                (python-tests-look-at "def b")
+                (forward-line -1)
+                (point))))))
+
 (ert-deftest python-nav-backward-defun-1 ()
   (python-tests-with-temp-buffer
    "
@@ -5730,6 +5769,19 @@ def \\
    (should (not (python-info-looking-at-beginning-of-defun)))
    (should (not (python-info-looking-at-beginning-of-defun nil t)))))
 
+(ert-deftest python-info-looking-at-beginning-of-defun-3 ()
+  (python-tests-with-temp-buffer
+   "
+def foo(arg=\"default\"):  # Comment
+    pass
+"
+   (python-tests-look-at "arg")
+   (should (python-info-looking-at-beginning-of-defun))
+   (python-tests-look-at "default")
+   (should (python-info-looking-at-beginning-of-defun))
+   (python-tests-look-at "Comment")
+   (should (python-info-looking-at-beginning-of-defun))))
+
 (ert-deftest python-info-looking-at-beginning-of-block-1 ()
   (python-tests-with-temp-buffer
    "
diff --git a/test/lisp/so-long-tests/so-long-tests-helpers.el 
b/test/lisp/so-long-tests/so-long-tests-helpers.el
index 852e7811cc..79df532f89 100644
--- a/test/lisp/so-long-tests/so-long-tests-helpers.el
+++ b/test/lisp/so-long-tests/so-long-tests-helpers.el
@@ -41,14 +41,14 @@
     (should (eq so-long--active t))
     ;; pcase fails here in Emacs 24.
     (cl-case action
-      ('so-long-mode
+      (so-long-mode
        (should (eq major-mode 'so-long-mode))
        (so-long-tests-assert-overrides)
        (so-long-tests-assert-preserved))
-      ('so-long-minor-mode
+      (so-long-minor-mode
        (should (eq so-long-minor-mode t))
        (so-long-tests-assert-overrides))
-      ('longlines-mode
+      (longlines-mode
        (should (eq longlines-mode t))))))
 
 (defun so-long-tests-assert-reverted (action)
@@ -61,14 +61,14 @@
     (should (eq so-long--active nil))
     ;; pcase fails here in Emacs 24.
     (cl-case action
-      ('so-long-mode
+      (so-long-mode
        (should-not (eq major-mode 'so-long-mode))
        (so-long-tests-assert-overrides-reverted)
        (so-long-tests-assert-preserved))
-      ('so-long-minor-mode
+      (so-long-minor-mode
        (should-not (eq so-long-minor-mode t))
        (so-long-tests-assert-overrides-reverted))
-      ('longlines-mode
+      (longlines-mode
        (should-not (eq longlines-mode t))))))
 
 (defun so-long-tests-assert-and-revert (action)
diff --git a/test/lisp/sort-tests.el b/test/lisp/sort-tests.el
index 7f49cc38d1..d8d42452ec 100644
--- a/test/lisp/sort-tests.el
+++ b/test/lisp/sort-tests.el
@@ -28,7 +28,7 @@
   (mapconcat (lambda (_) (string (let ((c (random 52)))
                               (+ (if (> c 25) 71 65)
                                  c))))
-             (make-list n nil) ""))
+             (make-list n nil)))
 
 (defun sort-tests--insert-words-sort-and-compare (words separator function 
reverse less-predicate)
   (with-temp-buffer
diff --git a/test/lisp/subr-tests.el b/test/lisp/subr-tests.el
index 3d03057f56..347981e818 100644
--- a/test/lisp/subr-tests.el
+++ b/test/lisp/subr-tests.el
@@ -635,7 +635,7 @@ cf. Bug#25477."
   (let ((default "foo") res)
     (cl-letf (((symbol-function 'read-string)
                (lambda (_prompt &optional _init _hist def _inher-input) def)))
-      (setq res (read-passwd "pass: " 'confirm (mapconcat #'string default 
"")))
+      (setq res (read-passwd "pass: " 'confirm (mapconcat #'string default)))
       (should (string= default res)))))
 
 (ert-deftest subr-tests--gensym ()
@@ -968,7 +968,21 @@ See https://debbugs.gnu.org/cgi/bugreport.cgi?bug=19350.";
     (insert "Foo bar zot foobar")
     (should (= (replace-string-in-region "Foo" "new" (point-min))
                1))
-    (should (equal (buffer-string) "new bar zot foobar"))))
+    (should (equal (buffer-string) "new bar zot foobar")))
+
+  (with-temp-buffer
+    (insert "foo bar baz")
+    (should (= (replace-string-in-region "ba" "quux corge grault" (point-min))
+               2))
+    (should (equal (buffer-string)
+                    "foo quux corge graultr quux corge graultz")))
+
+  (with-temp-buffer
+    (insert "foo bar bar")
+    (should (= (replace-string-in-region " bar" "" (point-min) 8)
+               1))
+    (should (equal (buffer-string)
+                    "foo bar"))))
 
 (ert-deftest test-replace-regexp-in-region ()
   (with-temp-buffer
@@ -991,7 +1005,21 @@ See https://debbugs.gnu.org/cgi/bugreport.cgi?bug=19350.";
     (insert "Foo bar zot foobar")
     (should (= (replace-regexp-in-region "Fo+" "new" (point-min))
                1))
-    (should (equal (buffer-string) "new bar zot foobar"))))
+    (should (equal (buffer-string) "new bar zot foobar")))
+
+  (with-temp-buffer
+    (insert "foo bar baz")
+    (should (= (replace-regexp-in-region "ba." "quux corge grault" (point-min))
+               2))
+    (should (equal (buffer-string)
+                    "foo quux corge grault quux corge grault")))
+
+  (with-temp-buffer
+    (insert "foo bar bar")
+    (should (= (replace-regexp-in-region " bar" "" (point-min) 8)
+               1))
+    (should (equal (buffer-string)
+                    "foo bar"))))
 
 (ert-deftest test-with-existing-directory ()
   (let ((dir (make-temp-name "/tmp/not-exist-")))
@@ -1130,5 +1158,13 @@ final or penultimate step during initialization."))
       (should (equal (butlast l n)
                      (subr-tests--butlast-ref l n))))))
 
+(ert-deftest test-list-of-strings-p ()
+  (should-not (list-of-strings-p 1))
+  (should (list-of-strings-p nil))
+  (should (list-of-strings-p '("a" "b")))
+  (should-not (list-of-strings-p ["a" "b"]))
+  (should-not (list-of-strings-p '("a" nil "b")))
+  (should-not (list-of-strings-p '("a" "b" . "c"))))
+
 (provide 'subr-tests)
 ;;; subr-tests.el ends here
diff --git a/test/lisp/tabify-tests.el b/test/lisp/tabify-tests.el
index eaa3527df0..1c8940c30f 100644
--- a/test/lisp/tabify-tests.el
+++ b/test/lisp/tabify-tests.el
@@ -27,9 +27,9 @@
 (defun tabify-tests--test-changes (fun changes width)
   (with-temp-buffer
     (let ((tab-width width))
-      (insert (mapconcat #'car changes ""))
+      (insert (mapconcat #'car changes))
       (funcall fun (point-min) (point-max))
-      (should (equal (buffer-string) (mapconcat #'cadr changes ""))))))
+      (should (equal (buffer-string) (mapconcat #'cadr changes))))))
 
 (ert-deftest tabify-tests-untabify ()
   (let ((changes '(("***\n"        "***\n")
diff --git a/test/lisp/textmodes/reftex-tests.el 
b/test/lisp/textmodes/reftex-tests.el
index 97ff390817..67e0100475 100644
--- a/test/lisp/textmodes/reftex-tests.el
+++ b/test/lisp/textmodes/reftex-tests.el
@@ -334,6 +334,179 @@ And this should be % \\cite{ignored}.
                            #'string<)))
       (kill-buffer (file-name-nondirectory tex-file)))))
 
+(ert-deftest reftex-renumber-simple-labels ()
+  "Test `reftex-renumber-simple-labels'.
+The function must recognize labels defined with macros like
+\\label and the ones as key=value option in optional or mandatory
+argument of other macros or environments."
+  (ert-with-temp-directory temp-dir
+    (let ((tex-file (expand-file-name "renumber.tex" temp-dir)))
+      (with-temp-buffer
+        (insert "\
+\\documentclass{article}
+\\usepackage{tcolorbox}
+\\tcbuselibrary{theorems}
+\\usepackage{fancyvrb}
+\\usepackage{listings}
+
+\\begin{document}
+
+This is with tcolorbox package:
+\\begin{problem}[%
+    colback                = white          ,
+    colframe               = red!50!black   ,
+    fonttitle              = \\bfseries      ,
+    description delimiters = {\\flqq}{\\frqq} ,
+    label                  = {problem:2}]{Prove RH2}{}
+  Problem
+\\end{problem}
+
+This is with vanilla \\LaTeX:
+\\begin{equation}
+  \\label{eq:2}
+  2
+\\end{equation}
+By \\eqref{eq:2} and \\ref{problem:2}
+
+This is with tcolorbox package:
+\\begin{problem}[%
+    colback=white,
+    colframe=red!50!black,
+    fonttitle=\\bfseries,
+    theorem label supplement={hypertarget={XYZ-##1}},
+    theorem full label supplement={code={\\marginnote{##1}}},
+    label={problem:1}]{Prove RH1}{}
+  Problem
+\\end{problem}
+
+This is with vanilla \\LaTeX:
+\\begin{equation}
+  \\label{eq:1}
+  1
+\\end{equation}
+
+\\Cref{problem:1} and \\pageref{eq:1}.
+
+\\begin{problem}[label={problem:6}]{Some Problem}{}
+  Problem
+\\end{problem}
+
+\\Ref{problem:6}.
+
+This is with fancyvrb package:
+\\begin{Verbatim}[reflabel={lst:6}]
+Some Verb Content
+\\end{Verbatim}
+
+\\pageref{lst:6}
+
+This is with listings package:
+\\begin{lstlisting}[language=elisp,caption=Some Caption,label={lst:3}]
+(car (cons 1 '(2)))
+\\end{lstlisting}
+
+\\ref{lst:3}
+
+\\end{document}")
+        (write-region (point-min) (point-max) tex-file))
+      ;; The label prefix must be known to RefTeX:
+      (add-to-list 'reftex-label-alist
+                   '("problem" ?p "problem:" "~\\ref{%s}"
+                     nil nil nil)
+                   t)
+      (add-to-list 'reftex-label-alist
+                   '("Verbatim" ?l "lst:" "~\\ref{%s}"
+                     nil nil nil)
+                   t)
+      ;; The environments must be known to RefTeX otherwise the labels
+      ;; aren't parsed correctly:
+      (add-to-list 'reftex-label-regexps
+                   (concat "\\\\begin{\\(?:problem\\|Verbatim\\)}"
+                           "\\[[^][]*"
+                           "\\(?:{[^}{]*"
+                           "\\(?:{[^}{]*"
+                           "\\(?:{[^}{]*}[^}{]*\\)*"
+                           "}[^}{]*\\)*"
+                           "}[^][]*\\)*"
+                           "\\<\\(?:ref\\)?label[[:space:]]*=[[:space:]]*"
+                           "{?\\(?1:[^] ,}\r\n\t%]+\\)"
+                           "[^]]*\\]")
+                   t)
+      ;; Always run this after changing `reftex-label-regexps':
+      (reftex-compile-variables)
+      (find-file tex-file)
+      ;; Silence the user query:
+      (cl-letf (((symbol-function 'yes-or-no-p) #'always))
+        (reftex-renumber-simple-labels))
+      (should (string= (buffer-string)
+                       "\
+\\documentclass{article}
+\\usepackage{tcolorbox}
+\\tcbuselibrary{theorems}
+\\usepackage{fancyvrb}
+\\usepackage{listings}
+
+\\begin{document}
+
+This is with tcolorbox package:
+\\begin{problem}[%
+    colback                = white          ,
+    colframe               = red!50!black   ,
+    fonttitle              = \\bfseries      ,
+    description delimiters = {\\flqq}{\\frqq} ,
+    label                  = {problem:1}]{Prove RH2}{}
+  Problem
+\\end{problem}
+
+This is with vanilla \\LaTeX:
+\\begin{equation}
+  \\label{eq:1}
+  2
+\\end{equation}
+By \\eqref{eq:1} and \\ref{problem:1}
+
+This is with tcolorbox package:
+\\begin{problem}[%
+    colback=white,
+    colframe=red!50!black,
+    fonttitle=\\bfseries,
+    theorem label supplement={hypertarget={XYZ-##1}},
+    theorem full label supplement={code={\\marginnote{##1}}},
+    label={problem:2}]{Prove RH1}{}
+  Problem
+\\end{problem}
+
+This is with vanilla \\LaTeX:
+\\begin{equation}
+  \\label{eq:2}
+  1
+\\end{equation}
+
+\\Cref{problem:2} and \\pageref{eq:2}.
+
+\\begin{problem}[label={problem:3}]{Some Problem}{}
+  Problem
+\\end{problem}
+
+\\Ref{problem:3}.
+
+This is with fancyvrb package:
+\\begin{Verbatim}[reflabel={lst:1}]
+Some Verb Content
+\\end{Verbatim}
+
+\\pageref{lst:1}
+
+This is with listings package:
+\\begin{lstlisting}[language=elisp,caption=Some Caption,label={lst:2}]
+(car (cons 1 '(2)))
+\\end{lstlisting}
+
+\\ref{lst:2}
+
+\\end{document}"))
+      (kill-buffer (file-name-nondirectory tex-file)))))
+
 ;;; Autoload tests
 
 ;; Test to check whether reftex autoloading mechanisms are working
diff --git a/test/lisp/whitespace-tests.el b/test/lisp/whitespace-tests.el
index 2a59bfe9d8..fb53543c9e 100644
--- a/test/lisp/whitespace-tests.el
+++ b/test/lisp/whitespace-tests.el
@@ -20,8 +20,37 @@
 ;;; Code:
 
 (require 'ert)
+(require 'ert-x)
+(require 'faceup)
 (require 'whitespace)
 
+(defmacro whitespace-tests--with-test-buffer (style &rest body)
+  "Run BODY in a buffer with `whitespace-mode' style STYLE.
+The buffer is displayed in `selected-window', and
+`noninteractive' is set to nil even in batch mode.  If STYLE is
+nil, `whitespace-mode' is left disabled."
+  (declare (debug ((style form) def-body))
+           (indent 1))
+  `(ert-with-test-buffer-selected ()
+     ;; In case global-*-mode is enabled.
+     (whitespace-mode -1)
+     (font-lock-mode -1)
+     (let ((noninteractive nil)
+           (whitespace-style ,style))
+       (font-lock-mode 1)
+       ,(when style
+          '(whitespace-mode 1))
+       ,@body)))
+
+(defun whitespace-tests--faceup (&rest lines)
+  "Convenience wrapper around `faceup-test-font-lock-buffer'.
+Returns non-nil if the concatenated LINES match the current
+buffer's content."
+  (faceup-test-font-lock-buffer nil (apply #'concat lines)))
+(let ((x (get 'faceup-test-font-lock-buffer 'ert-explainer)))
+  (put 'whitespace-tests--faceup 'ert-explainer
+       (lambda (&rest lines) (funcall x nil (apply #'concat lines)))))
+
 (defun whitespace-tests--cleanup-string (string)
   (with-temp-buffer
     (insert string)
@@ -80,6 +109,224 @@
                             (whitespace-turn-off)
                             buffer-display-table))))))
 
+(ert-deftest whitespace-tests--empty-bob ()
+  (whitespace-tests--with-test-buffer '(face empty)
+    (electric-indent-mode -1)
+
+    ;; Insert some empty lines.  None of the lines should be
+    ;; highlighted even though point is on the last line because the
+    ;; entire buffer is empty lines.
+    (execute-kbd-macro (kbd "SPC RET C-q TAB RET RET SPC"))
+    (should (equal (buffer-string) " \n\t\n\n "))
+    (should (equal (line-number-at-pos) 4))
+    (should (whitespace-tests--faceup " \n"
+                                      "\t\n"
+                                      "\n"
+                                      " "))
+
+    ;; Adding content on the last line (and keeping point there)
+    ;; should cause the previous lines to be highlighted.  Note that
+    ;; the `whitespace-empty' face applies to the newline just before
+    ;; the last line, which has the desired property of extending the
+    ;; highlight the full width of the window.
+    (execute-kbd-macro (kbd "x"))
+    (should (equal (buffer-string) " \n\t\n\n x"))
+    (should (equal (line-number-at-pos) 4))
+    (should (whitespace-tests--faceup "«:whitespace-empty: \n"
+                                      "\t\n"
+                                      "\n"
+                                      "» x"))
+
+    ;; Lines should become un-highlighted as point moves up into the
+    ;; empty lines.
+    (execute-kbd-macro (kbd "<up>"))
+    (should (equal (line-number-at-pos) 3))
+    (should (whitespace-tests--faceup "«:whitespace-empty: \n"
+                                      "\t\n"
+                                      "»\n"
+                                      " x"))
+    (execute-kbd-macro (kbd "<up>"))
+    (should (equal (line-number-at-pos) 2))
+    (should (whitespace-tests--faceup "«:whitespace-empty: \n"
+                                      "»\t\n"
+                                      "\n"
+                                      " x"))
+    (execute-kbd-macro (kbd "<up> C-a"))
+    (should (equal (point) 1))
+    (should (whitespace-tests--faceup " \n"
+                                      "\t\n"
+                                      "\n"
+                                      " x"))
+
+    ;; Line 1 should be un-highlighted when point is in line 1 even if
+    ;; point is not bobp.
+    (execute-kbd-macro (kbd "<right>"))
+    (should (equal (line-number-at-pos) 1))
+    (should (> (point) 1))
+    (should (whitespace-tests--faceup " \n"
+                                      "\t\n"
+                                      "\n"
+                                      " x"))
+
+    ;; Make sure lines become re-highlighted as point moves down.
+    (execute-kbd-macro (kbd "<down>"))
+    (should (equal (line-number-at-pos) 2))
+    (should (whitespace-tests--faceup "«:whitespace-empty: \n"
+                                      "»\t\n"
+                                      "\n"
+                                      " x"))
+    (execute-kbd-macro (kbd "<down>"))
+    (should (equal (line-number-at-pos) 3))
+    (should (whitespace-tests--faceup "«:whitespace-empty: \n"
+                                      "\t\n"
+                                      "»\n"
+                                      " x"))
+    (execute-kbd-macro (kbd "<down>"))
+    (should (equal (line-number-at-pos) 4))
+    (should (whitespace-tests--faceup "«:whitespace-empty: \n"
+                                      "\t\n"
+                                      "\n"
+                                      "» x"))
+
+    ;; Inserting content on line 2 should un-highlight lines 2 and 3.
+    (execute-kbd-macro (kbd "<up> <up> C-e"))
+    (should (equal (line-number-at-pos) 2))
+    (should (equal (- (point) (line-beginning-position)) 1))
+    (execute-kbd-macro (kbd "y <down> <down>"))
+    (should (equal (line-number-at-pos) 4))
+    (should (whitespace-tests--faceup "«:whitespace-empty: \n"
+                                      "»\ty\n"
+                                      "\n"
+                                      " x"))
+
+    ;; Removing the content on line 2 should re-highlight lines 2 and
+    ;; 3.
+    (execute-kbd-macro (kbd "<up> <up> C-e"))
+    (should (equal (line-number-at-pos) 2))
+    (should (equal (- (point) (line-beginning-position)) 2))
+    (execute-kbd-macro (kbd "DEL <down> <down>"))
+    (should (equal (line-number-at-pos) 4))
+    (should (whitespace-tests--faceup "«:whitespace-empty: \n"
+                                      "\t\n"
+                                      "\n"
+                                      "» x"))))
+
+(ert-deftest whitespace-tests--empty-eob ()
+  (whitespace-tests--with-test-buffer '(face empty)
+    (electric-indent-mode -1)
+
+    ;; Insert some empty lines.  None of the lines should be
+    ;; highlighted even though point is on line 1 because the entire
+    ;; buffer is empty lines.
+    (execute-kbd-macro (kbd "RET RET C-q TAB RET SPC C-<home>"))
+    (should (equal (buffer-string) "\n\n\t\n "))
+    (should (equal (line-number-at-pos) 1))
+    (should (whitespace-tests--faceup "\n"
+                                      "\n"
+                                      "\t\n"
+                                      " "))
+
+    ;; Adding content on the first line (and keeping point there)
+    ;; should cause the subsequent lines to be highlighted.
+    (execute-kbd-macro (kbd "x"))
+    (should (equal (buffer-string) "x\n\n\t\n "))
+    (should (equal (line-number-at-pos) 1))
+    (should (whitespace-tests--faceup "x\n"
+                                      "«:whitespace-empty:\n"
+                                      "\t\n"
+                                      " »"))
+
+    ;; Lines should become un-highlighted as point moves down into the
+    ;; empty lines.
+    (execute-kbd-macro (kbd "<down>"))
+    (should (equal (line-number-at-pos) 2))
+    (should (whitespace-tests--faceup "x\n"
+                                      "\n"
+                                      "«:whitespace-empty:\t\n"
+                                      " »"))
+    (execute-kbd-macro (kbd "<down>"))
+    (should (equal (line-number-at-pos) 3))
+    (should (whitespace-tests--faceup "x\n"
+                                      "\n"
+                                      "\t\n"
+                                      "«:whitespace-empty: »"))
+    (execute-kbd-macro (kbd "C-<end>"))
+    (should (equal (line-number-at-pos) 4))
+    (should (eobp))
+    (should (equal (- (point) (line-beginning-position)) 1))
+    (should (whitespace-tests--faceup "x\n"
+                                      "\n"
+                                      "\t\n"
+                                      " "))
+
+    ;; The last line should be un-highlighted when point is in that
+    ;; line even if point is not eobp.
+    (execute-kbd-macro (kbd "<left>"))
+    (should (equal (line-number-at-pos) 4))
+    (should (not (eobp)))
+    (should (whitespace-tests--faceup "x\n"
+                                      "\n"
+                                      "\t\n"
+                                      " "))
+
+    ;; Make sure lines become re-highlighted as point moves up.
+    (execute-kbd-macro (kbd "<up>"))
+    (should (equal (line-number-at-pos) 3))
+    (should (whitespace-tests--faceup "x\n"
+                                      "\n"
+                                      "\t\n"
+                                      "«:whitespace-empty: »"))
+    (execute-kbd-macro (kbd "<up>"))
+    (should (equal (line-number-at-pos) 2))
+    (should (whitespace-tests--faceup "x\n"
+                                      "\n"
+                                      "«:whitespace-empty:\t\n"
+                                      " »"))
+    (execute-kbd-macro (kbd "<up>"))
+    (should (equal (line-number-at-pos) 1))
+    (should (whitespace-tests--faceup "x\n"
+                                      "«:whitespace-empty:\n"
+                                      "\t\n"
+                                      " »"))
+
+    ;; Inserting content on line 3 should un-highlight lines 2 and 3.
+    (execute-kbd-macro (kbd "<down> <down> C-a"))
+    (should (equal (line-number-at-pos) 3))
+    (should (equal (- (point) (line-beginning-position)) 0))
+    (execute-kbd-macro (kbd "y <up> <up>"))
+    (should (equal (line-number-at-pos) 1))
+    (should (whitespace-tests--faceup "x\n"
+                                      "\n"
+                                      "y\t\n"
+                                      "«:whitespace-empty: »"))
+
+    ;; Removing the content on line 3 should re-highlight lines 2 and
+    ;; 3.
+    (execute-kbd-macro (kbd "<down> <down> C-a"))
+    (should (equal (line-number-at-pos) 3))
+    (should (equal (- (point) (line-beginning-position)) 0))
+    (execute-kbd-macro (kbd "<deletechar> <up> <up>"))
+    (should (equal (line-number-at-pos) 1))
+    (should (whitespace-tests--faceup "x\n"
+                                      "«:whitespace-empty:\n"
+                                      "\t\n"
+                                      " »"))))
+
+(ert-deftest whitespace-tests--empty-bob-eob-read-only-buffer ()
+  (whitespace-tests--with-test-buffer '()
+    (insert "\nx\n\n")
+    (should (equal (buffer-string) "\nx\n\n"))
+    (setq-local buffer-read-only t)
+    (goto-char 2)
+    (should (equal (line-number-at-pos) 2))
+    (should (equal (- (point) (line-beginning-position)) 0))
+    (let ((whitespace-style '(face empty)))
+      (whitespace-mode 1)
+      (should (whitespace-tests--faceup "«:whitespace-empty:\n"
+                                        "»x\n"
+                                        "«:whitespace-empty:\n"
+                                        "»")))))
+
 (provide 'whitespace-tests)
 
 ;;; whitespace-tests.el ends here
diff --git a/test/lisp/xdg-tests.el b/test/lisp/xdg-tests.el
index e8e103348b..882160743a 100644
--- a/test/lisp/xdg-tests.el
+++ b/test/lisp/xdg-tests.el
@@ -59,6 +59,16 @@
   (should (equal (xdg-desktop-strings " ") nil))
   (should (equal (xdg-desktop-strings "a; ;") '("a" " "))))
 
+(ert-deftest xdg-current-desktop ()
+  (let ((env (getenv "XDG_CURRENT_DESKTOP")))
+    (unwind-protect
+        (progn
+          (setenv "XDG_CURRENT_DESKTOP" "KDE")
+          (should (equal (xdg-current-desktop) '("KDE")))
+          (setenv "XDG_CURRENT_DESKTOP" "ubuntu:GNOME")
+          (should (equal (xdg-current-desktop) '("ubuntu" "GNOME"))))
+      (setenv "XDG_CURRENT_DESKTOP" env))))
+
 (ert-deftest xdg-mime-associations ()
   "Test reading MIME associations from files."
   (let* ((apps (ert-resource-file "mimeapps.list"))
diff --git a/test/manual/BidiCharacterTest.txt 
b/test/manual/BidiCharacterTest.txt
index c30d077861..619d4b4412 100644
--- a/test/manual/BidiCharacterTest.txt
+++ b/test/manual/BidiCharacterTest.txt
@@ -1,14 +1,14 @@
-# BidiCharacterTest-14.0.0.txt
-# Date: 2020-03-30, 23:56:00 GMT [LI]
-# © 2020 Unicode®, Inc.
-# For terms of use, see http://www.unicode.org/terms_of_use.html
+# BidiCharacterTest-15.0.0.txt
+# Date: 2022-05-03, 18:46:00 GMT [LI]
+# © 2022 Unicode®, Inc.
+# For terms of use, see https://www.unicode.org/terms_of_use.html
 #
 # Unicode Character Database
-# For documentation, see http://www.unicode.org/reports/tr44/
+# For documentation, see https://www.unicode.org/reports/tr44/
 #
 # This file provides a conformance test for implementations of the
 # Unicode Bidirectional Algorithm, specified in UAX #9: Unicode
-# Bidirectional Algorithm, at http://www.unicode.org/reports/tr9/
+# Bidirectional Algorithm, at https://www.unicode.org/reports/tr9/
 #
 # The test data has been generated with a few constraints. Each test case
 # is a single paragraph, so the test data does not contain any characters
diff --git a/test/src/image-tests.el b/test/manual/image-tests.el
similarity index 68%
copy from test/src/image-tests.el
copy to test/manual/image-tests.el
index 36278f4b9f..400657132c 100644
--- a/test/src/image-tests.el
+++ b/test/manual/image-tests.el
@@ -1,8 +1,9 @@
-;;; image-tests.el --- Tests for image.c  -*- lexical-binding: t -*-
+;;; image-tests.el --- tests for image.c  -*- lexical-binding: t; -*-
 
 ;; Copyright (C) 2021-2022 Free Software Foundation, Inc.
 
 ;; Author: Stefan Kangas <stefankangas@gmail.com>
+;; Keywords: internal
 
 ;; This file is part of GNU Emacs.
 
@@ -21,23 +22,23 @@
 
 ;;; Commentary:
 
-;; Most of these tests will only run in a GUI session, and not with
-;; "make check".  Run them manually in an interactive session with
-;; `M-x eval-buffer' followed by `M-x ert'.
+;; These tests will only run in a GUI session.  You must run them
+;; manually in an interactive session with, for example, `M-x
+;; eval-buffer' followed by `M-x ert'.
+;;
+;; To run them from the command line instead, try:
+;;     ./src/emacs -Q -l test/manual/image-tests.el -eval "(ert t)"
 
 ;;; Code:
 
-(require 'ert)
-
-(defmacro image-skip-unless (format)
-  `(skip-unless (and (display-images-p)
-                     (image-type-available-p ,format))))
-
-;;;; Images
+(defmacro image-skip-unless (format &rest condition)
+  `(skip-unless (and (and (display-images-p)
+                          (image-type-available-p ,format))
+                     ,@condition)))
 
 (defconst image-tests--images
   `((gif . ,(expand-file-name "test/data/image/black.gif"
-                               source-directory))
+                              source-directory))
     (jpeg . ,(expand-file-name "test/data/image/black.jpg"
                                source-directory))
     (pbm . ,(find-image '((:file "splash.svg" :type svg))))
@@ -51,6 +52,64 @@
     (xbm . ,(find-image '((:file "gnus/gnus.xbm" :type xbm))))
     (xpm . ,(find-image '((:file "splash.xpm" :type xpm))))))
 
+
+;;;; Load image
+
+(defmacro image-tests-make-load-image-test (type)
+  `(ert-deftest ,(intern (format "image-tests-load-image/%s"
+                                 (eval type t)))
+       ()
+     (image-skip-unless ,type)
+     (let* ((img (cdr (assq ,type image-tests--images)))
+            (file (if (listp img)
+                      (plist-get (cdr img) :file)
+                    img)))
+       (find-file file))
+     (should (equal major-mode 'image-mode))
+     ;; Cleanup
+     (kill-buffer (current-buffer))))
+
+(image-tests-make-load-image-test 'gif)
+(image-tests-make-load-image-test 'jpeg)
+(image-tests-make-load-image-test 'pbm)
+(image-tests-make-load-image-test 'png)
+(image-tests-make-load-image-test 'svg)
+(image-tests-make-load-image-test 'tiff)
+(image-tests-make-load-image-test 'webp)
+(image-tests-make-load-image-test 'xbm)
+(image-tests-make-load-image-test 'xpm)
+
+(ert-deftest image-tests-load-image/svg-too-big ()
+  (with-temp-buffer
+    (let* ((max-image-size 0)
+           (messages-buffer-name (buffer-name (current-buffer)))
+           (img (cdr (assq 'svg image-tests--images)))
+           (file (if (listp img)
+                     (plist-get (cdr img) :file)
+                   img)))
+      (save-excursion (find-file file))
+      (should (string-match-p "invalid image size" (buffer-string)))
+      ;; no annoying newlines
+      (should-not (string-match-p "^[ \t\n\r]+$" (buffer-string)))
+      ;; no annoying double error reporting
+      (should-not (string-match-p "error parsing" (buffer-string))))))
+
+(ert-deftest image-tests-load-image/svg-invalid ()
+  (with-temp-buffer
+    (let ((messages-buffer-name (buffer-name (current-buffer))))
+      (with-temp-buffer
+        (pop-to-buffer (current-buffer))
+        (insert (propertize " "
+                            'display '(image :data
+                                             "invalid foo bar"
+                                             :type svg)))
+        (redisplay))
+      ;; librsvg error: "... Start tag expected, '<' not found [3 times]"
+      (should (string-match-p "[Ee]rror.+Start tag expected" (buffer-string)))
+      ;; no annoying newlines
+      (should-not (string-match-p "^[ \t\n\r]+$" (buffer-string))))))
+
+
 ;;;; image-test-size
 
 (declare-function image-size "image.c" (spec &optional pixels frame))
@@ -122,10 +181,7 @@
   (skip-unless (display-images-p))
   (should-error (image-size 'invalid-spec)))
 
-(ert-deftest image-tests-image-size/error-on-nongraphical-display ()
-  (skip-unless (not (display-images-p)))
-  (should-error (image-size 'invalid-spec)))
-
+
 ;;;; image-mask-p
 
 (declare-function image-mask-p "image.c" (spec &optional frame))
@@ -174,10 +230,7 @@
   (skip-unless (display-images-p))
   (should-error (image-mask-p 'invalid-spec)))
 
-(ert-deftest image-tests-image-mask-p/error-on-nongraphical-display ()
-  (skip-unless (not (display-images-p)))
-  (should-error (image-mask-p (cdr (assq 'xpm image-tests--images)))))
-
+
 ;;;; image-metadata
 
 (declare-function image-metadata "image.c" (spec &optional frame))
@@ -186,9 +239,11 @@
 ;;       contain metadata.
 
 (ert-deftest image-tests-image-metadata/gif ()
-  (image-skip-unless 'gif)
-  (should-not (image-metadata
-               (create-image (cdr (assq 'gif image-tests--images))))))
+  (image-skip-unless 'gif
+                (not (bound-and-true-p w32-use-native-image-API)))
+  (should (memq 'delay
+                (image-metadata
+                 (create-image (cdr (assq 'gif image-tests--images)))))))
 
 (ert-deftest image-tests-image-metadata/jpeg ()
   (image-skip-unless 'jpeg)
@@ -214,8 +269,9 @@
 
 (ert-deftest image-tests-image-metadata/webp ()
   (image-skip-unless 'webp)
-  (should-not (image-metadata
-               (create-image (cdr (assq 'webp image-tests--images))))))
+  (should (memq 'delay
+                (image-metadata
+                 (create-image (cdr (assq 'webp image-tests--images)))))))
 
 (ert-deftest image-tests-image-metadata/xbm ()
   (image-skip-unless 'xbm)
@@ -229,23 +285,4 @@
   (skip-unless (display-images-p))
   (should-not (image-metadata 'invalid-spec)))
 
-(ert-deftest image-tests-image-metadata/error-on-nongraphical-display ()
-  (skip-unless (not (display-images-p)))
-  (should-error (image-metadata (cdr (assq 'xpm image-tests--images)))))
-
-;;;; ImageMagick
-
-(ert-deftest image-tests-imagemagick-types ()
-  (skip-unless (fboundp 'imagemagick-types))
-  (when (fboundp 'imagemagick-types)
-    (should (listp (imagemagick-types)))))
-
-;;;; Initialization
-
-(ert-deftest image-tests-init-image-library ()
-  (skip-unless (fboundp 'init-image-library))
-  (declare-function init-image-library "image.c" (type))
-  (should (init-image-library 'pbm)) ; built-in
-  (should-not (init-image-library 'invalid-image-type)))
-
-;;; image-tests.el ends here
+;;; image-size-tests.el ends here
diff --git a/test/src/buffer-tests.el b/test/src/buffer-tests.el
index 3c6a9208ff..558d05de14 100644
--- a/test/src/buffer-tests.el
+++ b/test/src/buffer-tests.el
@@ -22,6 +22,199 @@
 (require 'ert)
 (require 'ert-x)
 (require 'cl-lib)
+(require 'let-alist)
+
+(defun overlay-tests-start-recording-modification-hooks (overlay)
+  "Start recording modification hooks on OVERLAY.
+
+Always overwrites the `insert-in-front-hooks',
+`modification-hooks' and `insert-behind-hooks' properties.  Any
+recorded history from a previous call is erased.
+
+The history is stored in a property on the overlay itself.  Call
+`overlay-tests-get-recorded-modification-hooks' to retrieve the
+recorded calls conveniently."
+  (dolist (hooks-property '(insert-in-front-hooks
+                            modification-hooks
+                            insert-behind-hooks))
+    (overlay-put
+     overlay
+     hooks-property
+     (list (lambda (ov &rest args)
+             (message "  %S called on %S with args %S" hooks-property ov args)
+             (should inhibit-modification-hooks)
+             (should (eq ov overlay))
+             (push (list hooks-property args)
+                   (overlay-get overlay
+                                'recorded-modification-hook-calls)))))
+    (overlay-put overlay 'recorded-modification-hook-calls nil)))
+
+(defun overlay-tests-get-recorded-modification-hooks (overlay)
+  "Extract the recorded calls made to modification hooks on OVERLAY.
+
+Must be preceded by a call to
+`overlay-tests-start-recording-modification-hooks' on OVERLAY.
+
+Returns a list.  Each element of the list represents a recorded
+call to a particular modification hook.
+
+Each call is itself a sub-list where the first element is a
+symbol matching the modification hook property (one of
+`insert-in-front-hooks', `modification-hooks' or
+`insert-behind-hooks') and the second element is the list of
+arguments passed to the hook.  The first hook argument, the
+overlay itself, is omitted to make test result verification
+easier."
+  (reverse (overlay-get overlay
+                        'recorded-modification-hook-calls)))
+
+(ert-deftest overlay-modification-hooks ()
+  "Test the basic functionality of overlay modification hooks.
+
+This exercises hooks registered on the `insert-in-front-hooks',
+`modification-hooks' and `insert-behind-hooks' overlay
+properties."
+    ;; This is a data driven test loop.  Each test case is described
+    ;; by an alist.  The test loop initializes a new temporary buffer
+    ;; for each case, creates an overlay, registers modification hooks
+    ;; on the overlay, modifies the buffer, and then verifies which
+    ;; modification hooks (if any) were called for the overlay, as
+    ;; well as which arguments were passed to the hooks.
+    ;;
+    ;; The following keys are available in the alist:
+    ;;
+    ;; `buffer-text': the initial buffer text of the temporary buffer.
+    ;; Defaults to "1234".
+    ;;
+    ;; `overlay-beg' and `overlay-end': the begin and end positions of
+    ;; the overlay under test.  Defaults to 2 and 4 respectively.
+    ;;
+    ;; `insert-at': move to the given position and insert the string
+    ;; "x" into the test case's buffer.
+    ;;
+    ;; `replace': replace the first occurrence of the given string in
+    ;; the test case's buffer with "x".  The test will fail if the
+    ;; string is not found.
+    ;;
+    ;; `expected-calls': a description of the expected buffer
+    ;; modification hooks.  See
+    ;; `overlay-tests-get-recorded-modification-hooks' for the format.
+    ;; May be omitted, in which case the test will insist that no
+    ;; modification hooks are called.
+    ;;
+    ;; The test will fail itself in the degenerate case where no
+    ;; buffer modifications are requested.
+    (dolist (test-case
+             '(
+               ;; Remember that the default buffer text is "1234" and
+               ;; the default overlay begins at position 2 and ends at
+               ;; position 4.  Most of the test cases below assume
+               ;; this.
+
+               ;; TODO: (info "(elisp) Special Properties") says this
+               ;; about `modification-hooks': "Furthermore, insertion
+               ;; will not modify any existing character, so this hook
+               ;; will only be run when removing some characters,
+               ;; replacing them with others, or changing their
+               ;; text-properties."  So, why are modification-hooks
+               ;; being called when inserting at position 3 below?
+               ((insert-at . 1))
+               ((insert-at . 2)
+                (expected-calls . ((insert-in-front-hooks (nil 2 2))
+                                   (insert-in-front-hooks (t 2 3 0)))))
+               ((insert-at . 3)
+                (expected-calls . ((modification-hooks (nil 3 3))
+                                   (modification-hooks (t 3 4 0)))))
+               ((insert-at . 4)
+                (expected-calls . ((insert-behind-hooks (nil 4 4))
+                                   (insert-behind-hooks (t 4 5 0)))))
+               ((insert-at . 5))
+
+               ;; Replacing text never calls `insert-in-front-hooks'
+               ;; or `insert-behind-hooks'.  It calls
+               ;; `modification-hooks' if the overlay covers any text
+               ;; that has changed.
+               ((replace . "1"))
+               ((replace . "2")
+                (expected-calls . ((modification-hooks (nil 2 3))
+                                   (modification-hooks (t 2 3 1)))))
+               ((replace . "3")
+                (expected-calls . ((modification-hooks (nil 3 4))
+                                   (modification-hooks (t 3 4 1)))))
+               ((replace . "4"))
+               ((replace . "12")
+                (expected-calls . ((modification-hooks (nil 1 3))
+                                   (modification-hooks (t 1 2 2)))))
+               ((replace . "23")
+                (expected-calls . ((modification-hooks (nil 2 4))
+                                   (modification-hooks (t 2 3 2)))))
+               ((replace . "34")
+                (expected-calls . ((modification-hooks (nil 3 5))
+                                   (modification-hooks (t 3 4 2)))))
+               ((replace . "123")
+                (expected-calls . ((modification-hooks (nil 1 4))
+                                   (modification-hooks (t 1 2 3)))))
+               ((replace . "234")
+                (expected-calls . ((modification-hooks (nil 2 5))
+                                   (modification-hooks (t 2 3 3)))))
+               ((replace . "1234")
+                (expected-calls . ((modification-hooks (nil 1 5))
+                                   (modification-hooks (t 1 2 4)))))
+
+               ;; Inserting at the position of a zero-length overlay
+               ;; calls both `insert-in-front-hooks' and
+               ;; `insert-behind-hooks'.
+               ((buffer-text . "") (overlay-beg . 1) (overlay-end . 1)
+                (insert-at . 1)
+                (expected-calls . ((insert-in-front-hooks
+                                    (nil 1 1))
+                                   (insert-behind-hooks
+                                    (nil 1 1))
+                                   (insert-in-front-hooks
+                                    (t 1 2 0))
+                                   (insert-behind-hooks
+                                    (t 1 2 0)))))))
+      (message "BEGIN overlay-modification-hooks test-case %S" test-case)
+
+      ;; All three hooks ignore the overlay's `front-advance' and
+      ;; `rear-advance' option, so test both ways while expecting the same
+      ;; result.
+      (dolist (advance '(nil t))
+        (message "  advance is %S" advance)
+        (let-alist test-case
+          (with-temp-buffer
+            ;; Set up the temporary buffer and overlay as specified by
+            ;; the test case.
+            (insert (or .buffer-text "1234"))
+            (let ((overlay (make-overlay
+                            (or .overlay-beg 2)
+                            (or .overlay-end 4)
+                            nil
+                            advance advance)))
+              (message "  (buffer-string) is %S" (buffer-string))
+              (message "  overlay is %S" overlay)
+              (overlay-tests-start-recording-modification-hooks overlay)
+
+              ;; Modify the buffer, possibly inducing calls to the
+              ;; overlay's modification hooks.
+              (should (or .insert-at .replace))
+              (when .insert-at
+                (goto-char .insert-at)
+                (insert "x")
+                (message "  inserted \"x\" at %S, buffer-string now %S"
+                         .insert-at (buffer-string)))
+              (when .replace
+                (goto-char (point-min))
+                (search-forward .replace)
+                (replace-match "x")
+                (message "  replaced %S with \"x\"" .replace))
+
+              ;; Verify that the expected and actual modification hook
+              ;; calls match.
+              (should (equal
+                       .expected-calls
+                       (overlay-tests-get-recorded-modification-hooks
+                        overlay)))))))))
 
 (ert-deftest overlay-modification-hooks-message-other-buf ()
   "Test for bug#21824.
diff --git a/test/src/casefiddle-tests.el b/test/src/casefiddle-tests.el
index eb096f2112..652af41729 100644
--- a/test/src/casefiddle-tests.el
+++ b/test/src/casefiddle-tests.el
@@ -57,7 +57,7 @@
                     errors)))
           (setq expected (cdr expected)))))
     (when errors
-      (ert-fail (mapconcat (lambda (line) line) (nreverse errors) "")))))
+      (ert-fail (mapconcat #'identity (nreverse errors))))))
 
 
 (defconst casefiddle-tests--characters
@@ -98,7 +98,7 @@
                      errors)))
            (setq props (cdr props) tabs (cdr tabs) expected (cdr expected)))))
      (when errors
-       (mapconcat (lambda (line) line) (nreverse errors) "")))))
+       (mapconcat #'identity (nreverse errors))))))
 
 
 (ert-deftest casefiddle-tests-casing-character ()
@@ -116,7 +116,7 @@
                      errors)))
            (setq funcs (cdr funcs) expected (cdr expected)))))
      (when errors
-       (mapconcat (lambda (line) line) (nreverse errors) "")))))
+       (mapconcat (lambda (line) line) (nreverse errors))))))
 
 
 (ert-deftest casefiddle-tests-casing-word ()
diff --git a/test/src/comp-tests.el b/test/src/comp-tests.el
index 1b239cec79..1edbd1777c 100644
--- a/test/src/comp-tests.el
+++ b/test/src/comp-tests.el
@@ -860,21 +860,26 @@ Return a list of results."
 
 (cl-eval-when (compile eval load)
   (defconst comp-tests-type-spec-tests
-    `(
+    ;; Why we quote everything here, you ask?  So that values of
+    ;; `most-positive-fixnum' and `most-negative-fixnum', which can be
+    ;; architecture-dependent, do not end up hardcoded in the
+    ;; resulting byte-compiled file, and thus we could run the same
+    ;; .elc file on several architectures without fear.
+    '(
       ;; 1
       ((defun comp-tests-ret-type-spec-f (x)
          x)
-       t)
+       't)
 
       ;; 2
       ((defun comp-tests-ret-type-spec-f ()
          1)
-       (integer 1 1))
+       '(integer 1 1))
 
       ;; 3
       ((defun comp-tests-ret-type-spec-f (x)
          (if x 1 3))
-       (or (integer 1 1) (integer 3 3)))
+       '(or (integer 1 1) (integer 3 3)))
 
       ;; 4
       ((defun comp-tests-ret-type-spec-f (x)
@@ -883,7 +888,7 @@ Return a list of results."
                (setf y 1)
              (setf y 2))
            y))
-       (integer 1 2))
+       '(integer 1 2))
 
       ;; 5
       ((defun comp-tests-ret-type-spec-f (x)
@@ -892,73 +897,73 @@ Return a list of results."
                (setf y 1)
              (setf y 3))
            y))
-       (or (integer 1 1) (integer 3 3)))
+       '(or (integer 1 1) (integer 3 3)))
 
       ;; 6
       ((defun comp-tests-ret-type-spec-f (x)
          (if x
              (list x)
            3))
-       (or cons (integer 3 3)))
+       '(or cons (integer 3 3)))
 
       ;; 7
       ((defun comp-tests-ret-type-spec-f (x)
          (if x
              'foo
            3))
-       (or (member foo) (integer 3 3)))
+       '(or (member foo) (integer 3 3)))
 
       ;; 8
       ((defun comp-tests-ret-type-spec-f (x)
          (if (eq x 3)
              x
            'foo))
-       (or (member foo) (integer 3 3)))
+       '(or (member foo) (integer 3 3)))
 
       ;; 9
       ((defun comp-tests-ret-type-spec-f (x)
          (if (eq 3 x)
              x
            'foo))
-       (or (member foo) (integer 3 3)))
+       '(or (member foo) (integer 3 3)))
 
       ;; 10
       ((defun comp-tests-ret-type-spec-f (x)
          (if (eql x 3)
              x
            'foo))
-       (or (member foo) (integer 3 3)))
+       '(or (member foo) (integer 3 3)))
 
       ;; 11
       ((defun comp-tests-ret-type-spec-f (x)
          (if (eql 3 x)
              x
            'foo))
-       (or (member foo) (integer 3 3)))
+       '(or (member foo) (integer 3 3)))
 
       ;; 12
       ((defun comp-tests-ret-type-spec-f (x)
          (if (eql x 3)
              'foo
            x))
-       (not (integer 3 3)))
+       '(not (integer 3 3)))
 
       ;; 13
       ((defun comp-tests-ret-type-spec-f (x y)
          (if (= x y)
              x
            'foo))
-       (or (member foo) marker number))
+       '(or (member foo) marker number))
 
       ;; 14
       ((defun comp-tests-ret-type-spec-f (x)
          (comp-hint-fixnum x))
-       (integer ,most-negative-fixnum ,most-positive-fixnum))
+       `(integer ,most-negative-fixnum ,most-positive-fixnum))
 
       ;; 15
       ((defun comp-tests-ret-type-spec-f (x)
          (comp-hint-cons x))
-       cons)
+       'cons)
 
       ;; 16
       ((defun comp-tests-ret-type-spec-f (x)
@@ -966,7 +971,7 @@ Return a list of results."
            (when x
              (setf y 4))
            y))
-       (or null (integer 4 4)))
+       '(or null (integer 4 4)))
 
       ;; 17
       ((defun comp-tests-ret-type-spec-f ()
@@ -974,7 +979,7 @@ Return a list of results."
                (y 3))
            (setf x y)
            y))
-       (integer 3 3))
+       '(integer 3 3))
 
       ;; 18
       ((defun comp-tests-ret-type-spec-f (x)
@@ -982,120 +987,120 @@ Return a list of results."
            (when x
              (setf y x))
            y))
-       t)
+       't)
 
       ;; 19
       ((defun comp-tests-ret-type-spec-f (x y)
          (eq x y))
-       boolean)
+       'boolean)
 
       ;; 20
       ((defun comp-tests-ret-type-spec-f (x)
          (when x
            'foo))
-       (or (member foo) null))
+       '(or (member foo) null))
 
       ;; 21
       ((defun comp-tests-ret-type-spec-f (x)
          (unless x
            'foo))
-       (or (member foo) null))
+       '(or (member foo) null))
 
       ;; 22
       ((defun comp-tests-ret-type-spec-f (x)
         (when (> x 3)
           x))
-       (or null float (integer 4 *)))
+       '(or null float (integer 4 *)))
 
       ;; 23
       ((defun comp-tests-ret-type-spec-f (x)
         (when (>= x 3)
           x))
-       (or null float (integer 3 *)))
+       '(or null float (integer 3 *)))
 
       ;; 24
       ((defun comp-tests-ret-type-spec-f (x)
         (when (< x 3)
           x))
-       (or null float (integer * 2)))
+       '(or null float (integer * 2)))
 
       ;; 25
       ((defun comp-tests-ret-type-spec-f (x)
         (when (<= x 3)
           x))
-       (or null float (integer * 3)))
+       '(or null float (integer * 3)))
 
       ;; 26
       ((defun comp-tests-ret-type-spec-f (x)
         (when (> 3 x)
           x))
-       (or null float (integer * 2)))
+       '(or null float (integer * 2)))
 
       ;; 27
       ((defun comp-tests-ret-type-spec-f (x)
         (when (>= 3 x)
           x))
-       (or null float (integer * 3)))
+       '(or null float (integer * 3)))
 
       ;; 28
       ((defun comp-tests-ret-type-spec-f (x)
         (when (< 3 x)
           x))
-       (or null float (integer 4 *)))
+       '(or null float (integer 4 *)))
 
       ;; 29
       ((defun comp-tests-ret-type-spec-f (x)
         (when (<= 3 x)
           x))
-       (or null float (integer 3 *)))
+       '(or null float (integer 3 *)))
 
       ;; 30
       ((defun comp-tests-ret-type-spec-f (x)
          (let ((y 3))
           (when (> x y)
             x)))
-       (or null float (integer 4 *)))
+       '(or null float (integer 4 *)))
 
       ;; 31
       ((defun comp-tests-ret-type-spec-f (x)
          (let ((y 3))
           (when (> y x)
             x)))
-       (or null float (integer * 2)))
+       '(or null float (integer * 2)))
 
       ;; 32
       ((defun comp-tests-ret-type-spec-f (x)
          (when (and (> x 3)
                    (< x 10))
           x))
-       (or null float (integer 4 9)))
+       '(or null float (integer 4 9)))
 
       ;; 33
       ((defun comp-tests-ret-type-spec-f (x)
          (when (or (> x 3)
                    (< x 10))
           x))
-       (or null float integer))
+       '(or null float integer))
 
       ;; 34
       ((defun comp-tests-ret-type-spec-f (x)
          (when (or (< x 3)
                    (> x 10))
           x))
-       (or null float (integer * 2) (integer 11 *)))
+       '(or null float (integer * 2) (integer 11 *)))
 
       ;; 35 No float range support.
       ((defun comp-tests-ret-type-spec-f (x)
         (when (> x 1.0)
           x))
-       (or null marker number))
+       '(or null marker number))
 
       ;; 36
       ((defun comp-tests-ret-type-spec-f (x y)
          (when (and (> x 3)
                     (> y 2))
            (+ x y)))
-       (or null float (integer 7 *)))
+       '(or null float (integer 7 *)))
 
       ;; 37
       ;; SBCL: (OR REAL NULL)
@@ -1103,14 +1108,14 @@ Return a list of results."
          (when (and (<= x 3)
                     (<= y 2))
            (+ x y)))
-       (or null float (integer * 5)))
+       '(or null float (integer * 5)))
 
       ;; 38
       ((defun comp-tests-ret-type-spec-f (x y)
          (when (and (< 1 x 5)
                    (< 1 y 5))
            (+ x y)))
-       (or null float (integer 4 8)))
+       '(or null float (integer 4 8)))
 
       ;; 39
       ;; SBCL gives: (OR REAL NULL)
@@ -1118,7 +1123,7 @@ Return a list of results."
         (when (and (<= 1 x 10)
                    (<= 2 y 3))
           (+ x y)))
-       (or null float (integer 3 13)))
+       '(or null float (integer 3 13)))
 
       ;; 40
       ;; SBCL: (OR REAL NULL)
@@ -1126,42 +1131,42 @@ Return a list of results."
         (when (and (<= 1 x 10)
                    (<= 2 y 3))
           (- x y)))
-       (or null float (integer -2 8)))
+       '(or null float (integer -2 8)))
 
       ;; 41
       ((defun comp-tests-ret-type-spec-f (x y)
          (when (and (<= 1 x)
                     (<= 2 y 3))
            (- x y)))
-       (or null float (integer -2 *)))
+       '(or null float (integer -2 *)))
 
       ;; 42
       ((defun comp-tests-ret-type-spec-f (x y)
          (when (and (<= 1 x 10)
                     (<= 2 y))
            (- x y)))
-       (or null float (integer * 8)))
+       '(or null float (integer * 8)))
 
       ;; 43
       ((defun comp-tests-ret-type-spec-f (x y)
         (when (and (<= x 10)
                    (<= 2 y))
           (- x y)))
-       (or null float (integer * 8)))
+       '(or null float (integer * 8)))
 
       ;; 44
       ((defun comp-tests-ret-type-spec-f (x y)
          (when (and (<= x 10)
                     (<= y 3))
            (- x y)))
-       (or null float integer))
+       '(or null float integer))
 
       ;; 45
       ((defun comp-tests-ret-type-spec-f (x y)
          (when (and (<= 2 x)
                     (<= 3 y))
            (- x y)))
-       (or null float integer))
+       '(or null float integer))
 
       ;; 46
       ;; SBCL: (OR (RATIONAL (6) (30)) (SINGLE-FLOAT 6.0 30.0)
@@ -1174,63 +1179,63 @@ Return a list of results."
                    (< 1 j 5)
                    (< 1 k 5))
            (+ x y z i j k)))
-       (or null float (integer 12 24)))
+       '(or null float (integer 12 24)))
 
       ;; 47
       ((defun comp-tests-ret-type-spec-f (x)
          (when (<= 1 x 5)
            (1+ x)))
-       (or null float (integer 2 6)))
+       '(or null float (integer 2 6)))
 
       ;;48
       ((defun comp-tests-ret-type-spec-f (x)
          (when (<= 1 x 5)
            (1- x)))
-       (or null float (integer 0 4)))
+       '(or null float (integer 0 4)))
 
       ;; 49
       ((defun comp-tests-ret-type-spec-f ()
          (error "Foo"))
-       nil)
+       'nil)
 
       ;; 50
       ((defun comp-tests-ret-type-spec-f (x)
          (if (stringp x)
             x
            'bar))
-       (or (member bar) string))
+       '(or (member bar) string))
 
       ;; 51
       ((defun comp-tests-ret-type-spec-f (x)
          (if (stringp x)
              'bar
            x))
-       (not string))
+       '(not string))
 
       ;; 52
       ((defun comp-tests-ret-type-spec-f (x)
          (if (integerp x)
              x
            'bar))
-       (or (member bar) integer))
+       '(or (member bar) integer))
 
       ;; 53
       ((defun comp-tests-ret-type-spec-f (x)
          (when (integerp x)
            x))
-       (or null integer))
+       '(or null integer))
 
       ;; 54
       ((defun comp-tests-ret-type-spec-f (x)
          (unless (symbolp x)
            x))
-       t)
+       't)
 
       ;; 55
       ((defun comp-tests-ret-type-spec-f (x)
          (unless (integerp x)
            x))
-       (not integer))
+       '(not integer))
 
       ;; 56
       ((defun comp-tests-ret-type-spec-f (x)
@@ -1238,7 +1243,7 @@ Return a list of results."
            (1 (message "one"))
            (5 (message "five")))
          x)
-       t
+       't
        ;; FIXME improve `comp-cond-cstrs-target-mvar' to cross block
        ;; boundary if necessary as this should return:
        ;; (or (integer 1 1) (integer 5 5))
@@ -1250,7 +1255,7 @@ Return a list of results."
                     (eql x 3))
            (error "Not foo or 3"))
          x)
-       (or (member foo) (integer 3 3)))
+       '(or (member foo) (integer 3 3)))
 
       ;;58
       ((defun comp-tests-ret-type-spec-f (x y)
@@ -1259,7 +1264,7 @@ Return a list of results."
                   (<= x y))
              x
            (error "")))
-       (integer 0 *))
+       '(integer 0 *))
 
       ;; 59
       ((defun comp-tests-ret-type-spec-f (x y)
@@ -1268,7 +1273,7 @@ Return a list of results."
                   (<= x y))
              x
            (error "")))
-       (or float (integer 3 10)))
+       '(or float (integer 3 10)))
 
       ;; 60
       ((defun comp-tests-ret-type-spec-f (x y)
@@ -1277,56 +1282,56 @@ Return a list of results."
                             (>= x y))
              x
            (error "")))
-       (or float (integer 3 10)))
+       '(or float (integer 3 10)))
 
       ;; 61
       ((defun comp-tests-ret-type-spec-f (x)
         (if (= x 1.0)
              x
            (error "")))
-       (or (member 1.0) (integer 1 1)))
+       '(or (member 1.0) (integer 1 1)))
 
       ;; 62
       ((defun comp-tests-ret-type-spec-f (x)
         (if (= x 1.0)
              x
            (error "")))
-       (or (member 1.0) (integer 1 1)))
+       '(or (member 1.0) (integer 1 1)))
 
       ;; 63
       ((defun comp-tests-ret-type-spec-f (x)
         (if (= x 1.1)
              x
            (error "")))
-       (member 1.1))
+       '(member 1.1))
 
       ;; 64
       ((defun comp-tests-ret-type-spec-f (x)
         (if (= x 1)
              x
            (error "")))
-       (or (member 1.0) (integer 1 1)))
+       '(or (member 1.0) (integer 1 1)))
 
       ;; 65
       ((defun comp-tests-ret-type-spec-f (x)
         (if (= x 1)
              x
            (error "")))
-       (or (member 1.0) (integer 1 1)))
+       '(or (member 1.0) (integer 1 1)))
 
       ;; 66
       ((defun comp-tests-ret-type-spec-f (x)
         (if (eql x 0.0)
             x
           (error "")))
-       float)
+       'float)
 
       ;; 67
       ((defun comp-tests-ret-type-spec-f (x)
         (if (equal x '(1 2 3))
             x
           (error "")))
-       cons)
+       'cons)
 
       ;; 68
       ((defun comp-tests-ret-type-spec-f (x)
@@ -1335,7 +1340,7 @@ Return a list of results."
              x
            (error "")))
        ;; Conservative (see cstr relax in `comp-cstr-=').
-       (or (member 1.0) (integer 1 1)))
+       '(or (member 1.0) (integer 1 1)))
 
       ;; 69
       ((defun comp-tests-ret-type-spec-f (x)
@@ -1344,7 +1349,7 @@ Return a list of results."
              x
            (error "")))
        ;; Conservative (see cstr relax in `comp-cstr-=').
-       (or (member 1.0) (integer 1 1)))
+       '(or (member 1.0) (integer 1 1)))
 
       ;; 70
       ((defun comp-tests-ret-type-spec-f (x y)
@@ -1353,14 +1358,14 @@ Return a list of results."
                  (= x y))
              x
            (error "")))
-       (or float integer))
+       '(or float integer))
 
       ;; 71
       ((defun comp-tests-ret-type-spec-f (x)
          (if (= x 0.0)
              x
            (error "")))
-       (or (member -0.0 0.0) (integer 0 0)))
+       '(or (member -0.0 0.0) (integer 0 0)))
 
       ;; 72
       ((defun comp-tests-ret-type-spec-f (x)
@@ -1369,27 +1374,27 @@ Return a list of results."
          (unless (eql x -0.0)
            (error ""))
          x)
-       float)
+       'float)
 
       ;; 73
       ((defun comp-tests-ret-type-spec-f (x)
          (when (eql x 1.0)
           (error ""))
          x)
-       t)
+       't)
 
       ;; 74
       ((defun comp-tests-ret-type-spec-f (x)
          (if (eq x 0)
             (error "")
           (1+ x)))
-       number)))
+       'number)))
 
   (defun comp-tests-define-type-spec-test (number x)
     `(comp-deftest ,(intern (format "ret-type-spec-%d" number)) ()
                    ,(format "Type specifier test number %d." number)
                    (let ((comp-ctxt (make-comp-cstr-ctxt)))
-                     (comp-tests-check-ret-type-spec ',(car x) ',(cadr x))))))
+                     (comp-tests-check-ret-type-spec ',(car x) ,(cadr x))))))
 
 (defmacro comp-tests-define-type-spec-tests ()
   "Define all type specifier tests."
diff --git a/test/src/data-tests.el b/test/src/data-tests.el
index 0f84b2fb77..463a894d09 100644
--- a/test/src/data-tests.el
+++ b/test/src/data-tests.el
@@ -200,8 +200,7 @@ this is exactly representable and is greater than
             nibbles)
       (setf v (nthcdr 4 v)))
     (mapconcat (lambda (n) (format "%X" n))
-               (nreverse nibbles)
-               "")))
+               (nreverse nibbles))))
 
 (defun test-bool-vector-count-consecutive-tc (desc)
   "Run a test case for `bool-vector-count-consecutive'.
diff --git a/test/src/emacs-module-resources/mod-test.c 
b/test/src/emacs-module-resources/mod-test.c
index 187af821c2..b47a0b7a39 100644
--- a/test/src/emacs-module-resources/mod-test.c
+++ b/test/src/emacs-module-resources/mod-test.c
@@ -24,7 +24,6 @@ along with GNU Emacs.  If not, see 
<https://www.gnu.org/licenses/>.  */
 
 #include <errno.h>
 #include <limits.h>
-#include <stdbool.h>
 #include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
diff --git a/test/src/eval-tests.el b/test/src/eval-tests.el
index 1b2ad99360..bb2f04e8ee 100644
--- a/test/src/eval-tests.el
+++ b/test/src/eval-tests.el
@@ -108,26 +108,6 @@ Bug#24912."
     (should-error (eval (cons 'cond clauses) nil))
     (should-error (eval (cons 'cond clauses) t))))
 
-(defun eval-tests--exceed-specbind-limit ()
-  (defvar eval-tests--var1)
-  (defvar eval-tests--var2)
-  ;; Bind two variables, to make extra sure we hit the
-  ;; `max-specpdl-size' limit before the `max-lisp-eval-depth' limit.
-  (let ((eval-tests--var1 1)
-        (eval-tests--var2 2))
-    ;; Recurse until we hit the limit.
-    (eval-tests--exceed-specbind-limit)))
-
-(ert-deftest eval-exceed-specbind-with-signal-hook ()
-  "Test for Bug#30481.
-Check that Emacs doesn't crash when exceeding specbind limit with
-`signal-hook-function' bound.  NOTE: Without the fix for
-Bug#30481, this test can appear to pass, but cause a
-crash/abort/malloc assert failure on the next test."
-  (let ((max-specpdl-size (/ max-lisp-eval-depth 2))
-        (signal-hook-function #'ignore))
-    (should-error (eval-tests--exceed-specbind-limit))))
-
 (ert-deftest defvar/bug31072 ()
   "Check that Bug#31072 is fixed."
   (should-error (eval '(defvar 1) t) :type 'wrong-type-argument))
diff --git a/test/src/fns-tests.el b/test/src/fns-tests.el
index a84cce3ad4..9a2bd5cef3 100644
--- a/test/src/fns-tests.el
+++ b/test/src/fns-tests.el
@@ -131,47 +131,54 @@
     (should (equal [t t t t t nil nil nil nil nil] (vconcat (nreverse A))))))
 
 (defconst fns-tests--string-lessp-cases
-  '((a 97 error)
-    (97 "a" error)
-    ("abc" "abd" t)
-    ("abd" "abc" nil)
-    (abc "abd" t)
-    ("abd" abc nil)
-    (abc abd t)
-    (abd abc nil)
-    ("" "" nil)
-    ("" " " t)
-    (" " "" nil)
-    ("abc" "abcd" t)
-    ("abcd" "abc" nil)
-    ("abc" "abc" nil)
-    (abc abc nil)
-    ("\0" "" nil)
-    ("" "\0" t)
-    ("~" "\x80" t)
-    ("\x80" "\x80" nil)
-    ("\xfe" "\xff" t)
-    ("Munchen" "München" t)
-    ("München" "Munchen" nil)
-    ("München" "München" nil)
-    ("Ré" "Réunion" t)))
-
+  `(("abc" < "abd")
+    (abc < "abd")
+    (abc < abd)
+    ("" = "")
+    ("" < " ")
+    ("abc" < "abcd")
+    ("abc" = "abc")
+    (abc = abc)
+    ("" < "\0")
+    ("~" < "\x80")
+    ("\x80" = "\x80")
+    ("\xfe" < "\xff")
+    ("Munchen" < "München")
+    ("München" = "München")
+    ("Ré" < "Réunion")
+    ("abc" = ,(string-to-multibyte "abc"))
+    (,(string-to-multibyte "abc") = ,(string-to-multibyte "abc"))
+    ("abc" < ,(string-to-multibyte "abd"))
+    (,(string-to-multibyte "abc") < "abd")
+    (,(string-to-multibyte "abc") < ,(string-to-multibyte "abd"))
+    (,(string-to-multibyte "\x80") = ,(string-to-multibyte "\x80"))
+
+    ;; Cases concerning the ordering of raw bytes: these are
+    ;; troublesome because the current `string<' order is not very useful as
+    ;; it equates unibyte 80..FF with multibyte U+0080..00FF, and is also
+    ;; inconsistent with `string=' (see bug#58168).
+    ;;("\x80" < ,(string-to-multibyte "\x80"))
+    ;;("\xff" < ,(string-to-multibyte "\x80"))
+    ;;("ü" < "\xfc")
+    ;;("ü" < ,(string-to-multibyte "\xfc"))
+    )
+  "List of (A REL B) where REL is the relation (`<' or `=') between A and B.")
 
 (ert-deftest fns-tests-string-lessp ()
   ;; Exercise both `string-lessp' and its alias `string<', both directly
   ;; and in a function (exercising its bytecode).
-  (dolist (lessp (list #'string-lessp #'string<
-                       (lambda (a b) (string-lessp a b))
-                       (lambda (a b) (string< a b))))
-    (ert-info ((prin1-to-string lessp) :prefix "function: ")
+  (dolist (fun (list #'string-lessp #'string<
+                     (lambda (a b) (string-lessp a b))
+                     (lambda (a b) (string< a b))))
+    (ert-info ((prin1-to-string fun) :prefix "function: ")
+      (should-error (funcall fun 'a 97))
+      (should-error (funcall fun 97 "a"))
       (dolist (case fns-tests--string-lessp-cases)
         (ert-info ((prin1-to-string case) :prefix "case: ")
-          (pcase case
-            (`(,x ,y error)
-             (should-error (funcall lessp x y)))
-            (`(,x ,y ,expected)
-             (should (equal (funcall lessp x y) expected)))))))))
-
+          (pcase-let ((`(,x ,rel ,y) case))
+            (cl-assert (memq rel '(< =)))
+            (should (equal (funcall fun x y) (eq rel '<)))
+            (should (equal (funcall fun y x) nil))))))))
 
 (ert-deftest fns-tests-compare-strings ()
   (should-error (compare-strings))
@@ -614,9 +621,9 @@
   (should (string= (mapconcat #'identity '("Ä" "ø" "☭" "தமிழ்") "_漢字_")
                    "Ä_漢字_ø_漢字_☭_漢字_தமிழ்"))
   ;; vector
-  (should (string= (mapconcat #'identity ["a" "b"] "") "ab"))
+  (should (string= (mapconcat #'identity ["a" "b"]) "ab"))
   ;; bool-vector
-  (should (string= (mapconcat #'identity [nil nil] "") ""))
+  (should (string= (mapconcat #'identity [nil nil]) ""))
   (should-error (mapconcat #'identity [nil nil t])
                 :type 'wrong-type-argument))
 
@@ -1412,6 +1419,41 @@
     (should (equal (take 5 list) '(a b c b c)))
     (should (equal (take 10 list) '(a b c b c b c b c b)))
 
-    (should (equal (ntake 10 list) '(a b)))))
+    (should (equal (ntake 10 list) '(a b))))
+
+  ;; Bignum N argument.
+  (let ((list (list 'a 'b 'c)))
+    (should (equal (take (+ most-positive-fixnum 1) list) '(a b c)))
+    (should (equal (take (- most-negative-fixnum 1) list) nil))
+    (should (equal (ntake (+ most-positive-fixnum 1) list) '(a b c)))
+    (should (equal (ntake (- most-negative-fixnum 1) list) nil))
+    (should (equal list '(a b c)))))
+
+(ert-deftest fns--copy-alist ()
+  (dolist (orig '(nil
+                  ((a . 1) (b . 2) (a . 3))
+                  (a (b . 3) ((c) (d)))))
+    (ert-info ((prin1-to-string orig) :prefix "orig: ")
+      (let ((copy (copy-alist orig)))
+        (should (equal orig copy))
+        (while orig
+          (should-not (eq orig copy))
+          ;; Check that cons pairs are copied but nothing else.
+          (let ((orig-elt (car orig))
+                (copy-elt (car copy)))
+            (if (atom orig-elt)
+                (should (eq orig-elt copy-elt))
+              (should-not (eq orig-elt copy-elt))
+              (should (eq (car orig-elt) (car copy-elt)))
+              (should (eq (cdr orig-elt) (cdr copy-elt)))))
+          (setq orig (cdr orig))
+          (setq copy (cdr copy))))))
+
+  (should-error (copy-alist 'a)
+                :type 'wrong-type-argument)
+  (should-error (copy-alist [(a . 1) (b . 2) (a . 3)])
+                :type 'wrong-type-argument)
+  (should-error (copy-alist "abc")
+                :type 'wrong-type-argument))
 
 ;;; fns-tests.el ends here
diff --git a/test/src/image-tests.el b/test/src/image-tests.el
index 36278f4b9f..d1a4dad37b 100644
--- a/test/src/image-tests.el
+++ b/test/src/image-tests.el
@@ -19,25 +19,17 @@
 ;; You should have received a copy of the GNU General Public License
 ;; along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.
 
-;;; Commentary:
-
-;; Most of these tests will only run in a GUI session, and not with
-;; "make check".  Run them manually in an interactive session with
-;; `M-x eval-buffer' followed by `M-x ert'.
-
 ;;; Code:
 
 (require 'ert)
 
-(defmacro image-skip-unless (format)
-  `(skip-unless (and (display-images-p)
-                     (image-type-available-p ,format))))
-
-;;;; Images
+(declare-function image-size "image.c" (spec &optional pixels frame))
+(declare-function image-mask-p "image.c" (spec &optional frame))
+(declare-function image-metadata "image.c" (spec &optional frame))
 
 (defconst image-tests--images
   `((gif . ,(expand-file-name "test/data/image/black.gif"
-                               source-directory))
+                              source-directory))
     (jpeg . ,(expand-file-name "test/data/image/black.jpg"
                                source-directory))
     (pbm . ,(find-image '((:file "splash.svg" :type svg))))
@@ -51,197 +43,23 @@
     (xbm . ,(find-image '((:file "gnus/gnus.xbm" :type xbm))))
     (xpm . ,(find-image '((:file "splash.xpm" :type xpm))))))
 
-;;;; image-test-size
-
-(declare-function image-size "image.c" (spec &optional pixels frame))
-
-(ert-deftest image-tests-image-size/gif ()
-  (image-skip-unless 'gif)
-  (pcase (image-size (create-image (cdr (assq 'gif image-tests--images))))
-    (`(,a . ,b)
-     (should (floatp a))
-     (should (floatp b)))))
-
-(ert-deftest image-tests-image-size/jpeg ()
-  (image-skip-unless 'jpeg)
-  (pcase (image-size (create-image (cdr (assq 'jpeg image-tests--images))))
-    (`(,a . ,b)
-     (should (floatp a))
-     (should (floatp b)))))
-
-(ert-deftest image-tests-image-size/pbm ()
-  (image-skip-unless 'pbm)
-  (pcase (image-size (cdr (assq 'pbm image-tests--images)))
-    (`(,a . ,b)
-     (should (floatp a))
-     (should (floatp b)))))
-
-(ert-deftest image-tests-image-size/png ()
-  (image-skip-unless 'png)
-  (pcase (image-size (cdr (assq 'png image-tests--images)))
-    (`(,a . ,b)
-     (should (floatp a))
-     (should (floatp b)))))
-
-(ert-deftest image-tests-image-size/svg ()
-  (image-skip-unless 'svg)
-  (pcase (image-size (cdr (assq 'svg image-tests--images)))
-    (`(,a . ,b)
-     (should (floatp a))
-     (should (floatp b)))))
-
-(ert-deftest image-tests-image-size/tiff ()
-  (image-skip-unless 'tiff)
-  (pcase (image-size (create-image (cdr (assq 'tiff image-tests--images))))
-    (`(,a . ,b)
-     (should (floatp a))
-     (should (floatp b)))))
-
-(ert-deftest image-tests-image-size/webp ()
-  (image-skip-unless 'webp)
-  (pcase (image-size (create-image (cdr (assq 'webp image-tests--images))))
-    (`(,a . ,b)
-     (should (floatp a))
-     (should (floatp b)))))
-
-(ert-deftest image-tests-image-size/xbm ()
-  (image-skip-unless 'xbm)
-  (pcase (image-size (cdr (assq 'xbm image-tests--images)))
-    (`(,a . ,b)
-     (should (floatp a))
-     (should (floatp b)))))
-
-(ert-deftest image-tests-image-size/xpm ()
-  (image-skip-unless 'xpm)
-  (pcase (image-size (cdr (assq 'xpm image-tests--images)))
-    (`(,a . ,b)
-     (should (floatp a))
-     (should (floatp b)))))
-
-(ert-deftest image-tests-image-size/error-on-invalid-spec ()
-  (skip-unless (display-images-p))
-  (should-error (image-size 'invalid-spec)))
-
 (ert-deftest image-tests-image-size/error-on-nongraphical-display ()
   (skip-unless (not (display-images-p)))
   (should-error (image-size 'invalid-spec)))
 
-;;;; image-mask-p
-
-(declare-function image-mask-p "image.c" (spec &optional frame))
-
-(ert-deftest image-tests-image-mask-p/gif ()
-  (image-skip-unless 'gif)
-  (should-not (image-mask-p (create-image
-                             (cdr (assq 'gif image-tests--images))))))
-
-(ert-deftest image-tests-image-mask-p/jpeg ()
-  (image-skip-unless 'jpeg)
-  (should-not (image-mask-p (create-image
-                             (cdr (assq 'jpeg image-tests--images))))))
-
-(ert-deftest image-tests-image-mask-p/pbm ()
-  (image-skip-unless 'pbm)
-  (should-not (image-mask-p (cdr (assq 'pbm image-tests--images)))))
-
-(ert-deftest image-tests-image-mask-p/png ()
-  (image-skip-unless 'png)
-  (should-not (image-mask-p (cdr (assq 'png image-tests--images)))))
-
-(ert-deftest image-tests-image-mask-p/svg ()
-  (image-skip-unless 'svg)
-  (should-not (image-mask-p (cdr (assq 'svg image-tests--images)))))
-
-(ert-deftest image-tests-image-mask-p/tiff ()
-  (image-skip-unless 'tiff)
-  (should-not (image-mask-p (create-image
-                             (cdr (assq 'tiff image-tests--images))))))
-
-(ert-deftest image-tests-image-mask-p/webp ()
-  (image-skip-unless 'webp)
-  (should-not (image-mask-p (create-image
-                             (cdr (assq 'webp image-tests--images))))))
-
-(ert-deftest image-tests-image-mask-p/xbm ()
-  (image-skip-unless 'xbm)
-  (should-not (image-mask-p (cdr (assq 'xbm image-tests--images)))))
-
-(ert-deftest image-tests-image-mask-p/xpm ()
-  (image-skip-unless 'xpm)
-  (should-not (image-mask-p (cdr (assq 'xpm image-tests--images)))))
-
-(ert-deftest image-tests-image-mask-p/error-on-invalid-spec ()
-  (skip-unless (display-images-p))
-  (should-error (image-mask-p 'invalid-spec)))
-
 (ert-deftest image-tests-image-mask-p/error-on-nongraphical-display ()
   (skip-unless (not (display-images-p)))
   (should-error (image-mask-p (cdr (assq 'xpm image-tests--images)))))
 
-;;;; image-metadata
-
-(declare-function image-metadata "image.c" (spec &optional frame))
-
-;; TODO: These tests could be expanded with files that actually
-;;       contain metadata.
-
-(ert-deftest image-tests-image-metadata/gif ()
-  (image-skip-unless 'gif)
-  (should-not (image-metadata
-               (create-image (cdr (assq 'gif image-tests--images))))))
-
-(ert-deftest image-tests-image-metadata/jpeg ()
-  (image-skip-unless 'jpeg)
-  (should-not (image-metadata
-               (create-image (cdr (assq 'jpeg image-tests--images))))))
-
-(ert-deftest image-tests-image-metadata/pbm ()
-  (image-skip-unless 'pbm)
-  (should-not (image-metadata (cdr (assq 'pbm image-tests--images)))))
-
-(ert-deftest image-tests-image-metadata/png ()
-  (image-skip-unless 'png)
-  (should-not (image-metadata (cdr (assq 'png image-tests--images)))))
-
-(ert-deftest image-tests-image-metadata/svg ()
-  (image-skip-unless 'svg)
-  (should-not (image-metadata (cdr (assq 'svg image-tests--images)))))
-
-(ert-deftest image-tests-image-metadata/tiff ()
-  (image-skip-unless 'tiff)
-  (should-not (image-metadata
-               (create-image (cdr (assq 'tiff image-tests--images))))))
-
-(ert-deftest image-tests-image-metadata/webp ()
-  (image-skip-unless 'webp)
-  (should-not (image-metadata
-               (create-image (cdr (assq 'webp image-tests--images))))))
-
-(ert-deftest image-tests-image-metadata/xbm ()
-  (image-skip-unless 'xbm)
-  (should-not (image-metadata (cdr (assq 'xbm image-tests--images)))))
-
-(ert-deftest image-tests-image-metadata/xpm ()
-  (image-skip-unless 'xpm)
-  (should-not (image-metadata (cdr (assq 'xpm image-tests--images)))))
-
-(ert-deftest image-tests-image-metadata/nil-on-invalid-spec ()
-  (skip-unless (display-images-p))
-  (should-not (image-metadata 'invalid-spec)))
-
 (ert-deftest image-tests-image-metadata/error-on-nongraphical-display ()
   (skip-unless (not (display-images-p)))
   (should-error (image-metadata (cdr (assq 'xpm image-tests--images)))))
 
-;;;; ImageMagick
-
 (ert-deftest image-tests-imagemagick-types ()
   (skip-unless (fboundp 'imagemagick-types))
   (when (fboundp 'imagemagick-types)
     (should (listp (imagemagick-types)))))
 
-;;;; Initialization
-
 (ert-deftest image-tests-init-image-library ()
   (skip-unless (fboundp 'init-image-library))
   (declare-function init-image-library "image.c" (type))
diff --git a/test/src/print-tests.el b/test/src/print-tests.el
index 5c349342eb..faab196f22 100644
--- a/test/src/print-tests.el
+++ b/test/src/print-tests.el
@@ -190,7 +190,8 @@ otherwise, use a different charset."
   "Printing observes `print-continuous-numbering'."
   ;; cl-print does not support print-continuous-numbering.
   :expected-result (if (eq (symbol-function #'print-tests--prin1-to-string)
-                           #'cl-prin1-to-string) :failed :passed)
+                           #'cl-prin1-to-string)
+                       :failed :passed)
   (let* ((x (list 1))
          (y "hello")
          (g (gensym))
@@ -201,7 +202,8 @@ otherwise, use a different charset."
           (print-number-table nil))
       (should (string-match
                "(#1=(1) #1# #2=\"hello\" #2#)(#3=#:g[[:digit:]]+ #3#)(#1# #2# 
#3#)#2#$"
-               (mapconcat #'print-tests--prin1-to-string `((,x ,x ,y ,y) (,g 
,g) (,x ,y ,g) ,y) ""))))
+               (mapconcat #'print-tests--prin1-to-string
+                          `((,x ,x ,y ,y) (,g ,g) (,x ,y ,g) ,y)))))
 
     ;; This is the special case for byte-compile-output-docform
     ;; mentioned in a comment in print_preprocess.  When
diff --git a/test/src/process-tests.el b/test/src/process-tests.el
index 6e1e148332..7d3d9eb72b 100644
--- a/test/src/process-tests.el
+++ b/test/src/process-tests.el
@@ -134,12 +134,12 @@ process to complete."
     (should (equal 1 (with-current-buffer stdout-buffer
                       (point-max))))
     (should (equal "hello stdout!\n"
-                  (mapconcat #'identity (nreverse stdout-output) "")))
+                  (mapconcat #'identity (nreverse stdout-output))))
     (should stderr-sentinel-called)
     (should (equal 1 (with-current-buffer stderr-buffer
                       (point-max))))
     (should (equal "hello stderr!\n"
-                  (mapconcat #'identity (nreverse stderr-output) ""))))))
+                  (mapconcat #'identity (nreverse stderr-output)))))))
 
 (ert-deftest set-process-filter-t ()
   "Test setting process filter to t and back." ;; Bug#36591



reply via email to

[Prev in Thread] Current Thread [Next in Thread]