emacs-diffs
[Top][All Lists]
Advanced

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

feature/android a496509ced 1/2: Update Android port


From: Po Lu
Subject: feature/android a496509ced 1/2: Update Android port
Date: Thu, 19 Jan 2023 09:20:42 -0500 (EST)

branch: feature/android
commit a496509cedb17109d0e6297a74e2ff8ed526333c
Author: Po Lu <luangruo@yahoo.com>
Commit: Po Lu <luangruo@yahoo.com>

    Update Android port
    
    * .gitignore: Add new files.
    * INSTALL.android: Explain how to build Emacs for ancient
    versions of Android.
    * admin/merge-gnulib (GNULIB_MODULES): Add getdelim.
    * build-aux/config.guess (timestamp, version):
    * build-aux/config.sub (timestamp, version): Autoupdate.
    * configure.ac (BUILD_DETAILS, ANDROID_MIN_SDK):
    (ANDROID_STUBIFY): Allow specifying CFLAGS via ANDROID_CFLAGS.
    Add new configure tests for Android API version when not
    explicitly specified.
    
    * doc/emacs/android.texi (Android): Add reference to ``Other
    Input Devices''.
    (Android File System): Remove restrictions on directory-files on
    the assets directory.
    * doc/emacs/emacs.texi (Top): Add Other Input Devices to menu.
    * doc/emacs/input.texi (Other Input Devices): New node.
    * doc/lispref/commands.texi (Touchscreen Events): Document
    changes to touchscreen input events.
    * doc/lispref/frames.texi (Pop-Up Menus): Likewise.
    * etc/NEWS: Announce changes.
    * java/Makefile.in: Use lib-src/asset-directory-tool to generate
    an `directory-tree' file placed in /assets.
    * java/debug.sh: Large adjustments to support Android 2.2 and
    later.
    
    * java/org/gnu/emacs/EmacsContextMenu.java (inflateMenuItems):
    * java/org/gnu/emacs/EmacsCopyArea.java (perform):
    * java/org/gnu/emacs/EmacsDialog.java (toAlertDialog):
    * java/org/gnu/emacs/EmacsDrawLine.java (perform):
    * java/org/gnu/emacs/EmacsDrawRectangle.java (perform):
    * java/org/gnu/emacs/EmacsDrawable.java (EmacsDrawable):
    * java/org/gnu/emacs/EmacsFillPolygon.java (perform):
    * java/org/gnu/emacs/EmacsFillRectangle.java (perform):
    * java/org/gnu/emacs/EmacsGC.java (EmacsGC):
    * java/org/gnu/emacs/EmacsPixmap.java (EmacsPixmap):
    (destroyHandle):
    * java/org/gnu/emacs/EmacsSdk7FontDriver.java (draw): Avoid
    redundant canvas saves and restores.
    * java/org/gnu/emacs/EmacsService.java (run):
    * java/org/gnu/emacs/EmacsView.java (EmacsView):
    (handleDirtyBitmap):
    * java/org/gnu/emacs/EmacsWindow.java (changeWindowBackground)
    (EmacsWindow): Make compatible with Android 2.2 and later.
    
    * lib-src/Makefile.in (DONT_INSTALL): Add asset-directory-tool
    on Android.:(asset-directory-tool{EXEEXT}): New target.
    * lib-src/asset-directory-tool.c (struct directory_tree, xmalloc)
    (main_1, main_2, main): New file.
    
    * lib, m4: Merge from gnulib.  This will be reverted before
    merging to master.
    
    * lisp/button.el (button-map):
    (push-button):
    * lisp/frame.el (display-popup-menus-p): Improve touchscreen
    support.
    * lisp/subr.el (event-start):
    (event-end): Handle touchscreen events.
    * lisp/touch-screen.el (touch-screen-handle-timeout):
    (touch-screen-handle-point-update):
    (touch-screen-handle-point-up):
    (touch-screen-track-tap):
    (touch-screen-track-drag):
    (touch-screen-drag-mode-line-1):
    (touch-screen-drag-mode-line): New functions.
    ([mode-line touchscreen-begin]):
    ([bottom-divider touchscreen-begin]): Bind new events.
    
    * lisp/wid-edit.el (widget-event-point):
    (widget-keymap):
    (widget-event-start):
    (widget-button--check-and-call-button):
    (widget-button-click): Improve touchscreen support.
    
    * src/alloc.c (make_lisp_symbol): Avoid ICE on Android NDK GCC.
    (mark_pinned_symbols): Likewise.
    
    * src/android.c (struct android_emacs_window): New struct.
    (window_class): New variable.
    (android_run_select_thread): Add workaround for Android platform
    bug.
    (android_extract_long, android_scan_directory_tree): New
    functions.
    (android_file_access_p): Use those functions instead.
    (android_init_emacs_window): New function.
    (android_init_emacs_gc_class): Update signature of `markDirty'.
    (android_change_gc, android_set_clip_rectangles): Tell the GC
    whether or not clip rects were dirtied.
    (android_swap_buffers): Do not look up method every time.
    (struct android_dir): Adjust for new directory tree lookup.
    (android_opendir, android_readdir, android_closedir): Likewise.
    (android_four_corners_bilinear): Fix coding style.
    (android_ftruncate): New function.
    * src/android.h: Update prototypes.  Replace ftruncate with
    android_ftruncate when necessary.
    
    * src/androidterm.c (handle_one_android_event): Pacify GCC.  Fix
    touch screen tool bar bug.
    * src/emacs.c (using_utf8): Fix compilation error.
    * src/fileio.c (Ffile_system_info): Return Qnil when fsusage.o
    is not built.
    * src/filelock.c (BOOT_TIME_FILE): Fix definition for Android.
    * src/frame.c (Fx_parse_geometry): Fix uninitialized variable
    uses.
    * src/keyboard.c (lispy_function_keys): Fix `back'.
    * src/menu.c (x_popup_menu_1): Handle touch screen events.
    (Fx_popup_menu): Document changes.
    
    * src/sfnt.c (main): Improve tests.
    
    * src/sfntfont-android.c (sfntfont_android_put_glyphs): Fix
    minor problem.
    (init_sfntfont_android): Check for
    HAVE_DECL_ANDROID_GET_DEVICE_API_LEVEL.
    * src/sfntfont.c (struct sfnt_font_desc): New fields `adstyle'
    and `languages'.
    (sfnt_parse_style): Append tokens to adstyle.
    (sfnt_parse_languages): New function.
    (sfnt_enum_font_1): Parse supported languages and adstyle.
    (sfntfont_list_1): Handle new fields.
    (sfntfont_text_extents): Fix uninitialized variable use.
    (syms_of_sfntfont, mark_sfntfont): Adjust accordingly.
---
 .gitignore                                      |   2 +
 INSTALL.android                                 |  35 ++
 admin/merge-gnulib                              |   4 +-
 build-aux/config.guess                          |   6 +-
 build-aux/config.sub                            |   6 +-
 configure.ac                                    | 160 ++++++-
 doc/emacs/android.texi                          |  11 +-
 doc/emacs/emacs.texi                            |   6 +
 doc/emacs/input.texi                            |  60 +++
 doc/lispref/commands.texi                       |  46 +-
 doc/lispref/frames.texi                         |   6 +-
 etc/NEWS                                        |  20 +
 java/Makefile.in                                |  37 +-
 java/debug.sh                                   | 155 ++++---
 java/org/gnu/emacs/EmacsContextMenu.java        |  20 +-
 java/org/gnu/emacs/EmacsCopyArea.java           |  11 +-
 java/org/gnu/emacs/EmacsDialog.java             |   3 -
 java/org/gnu/emacs/EmacsDrawLine.java           |  11 +-
 java/org/gnu/emacs/EmacsDrawRectangle.java      |  45 +-
 java/org/gnu/emacs/EmacsDrawable.java           |   2 +-
 java/org/gnu/emacs/EmacsFillPolygon.java        |  11 +-
 java/org/gnu/emacs/EmacsFillRectangle.java      |  12 +-
 java/org/gnu/emacs/EmacsGC.java                 |  33 +-
 java/org/gnu/emacs/EmacsPixmap.java             |  77 +++-
 java/org/gnu/emacs/EmacsSdk7FontDriver.java     |  11 +-
 java/org/gnu/emacs/EmacsService.java            |   7 +-
 java/org/gnu/emacs/EmacsView.java               |  34 +-
 java/org/gnu/emacs/EmacsWindow.java             |  80 +++-
 lib-src/Makefile.in                             |  11 +
 lib-src/asset-directory-tool.c                  | 280 ++++++++++++
 lib/faccessat.c                                 |   4 -
 lib/fpending.c                                  |   2 +-
 lib/getdelim.c                                  | 147 ++++++
 lib/getline.c                                   |  27 ++
 lib/gnulib.mk.in                                |  40 ++
 lib/stdalign.in.h                               | 109 +----
 lib/stdio-impl.h                                |   8 +-
 lisp/button.el                                  |  30 +-
 lisp/frame.el                                   |   8 +-
 lisp/subr.el                                    |  14 +-
 lisp/touch-screen.el                            | 189 +++++++-
 lisp/wid-edit.el                                | 122 +++--
 m4/getdelim.m4                                  | 111 +++++
 m4/getline.m4                                   | 109 +++++
 m4/gnulib-comp.m4                               |  32 ++
 m4/stdalign.m4                                  | 127 ++++--
 m4/unistd_h.m4                                  |   1 +
 m4/utimens.m4                                   |   3 +-
 m4/utimensat.m4                                 |   4 +-
 m4/xattr.m4                                     |  42 +-
 src/alloc.c                                     |  34 +-
 src/android.c                                   | 569 +++++++++++++++++++-----
 src/android.h                                   |   8 +
 src/androidterm.c                               |   5 +-
 src/emacs.c                                     |   8 +
 src/fileio.c                                    |  15 +
 src/filelock.c                                  |  12 +-
 src/frame.c                                     |   2 +
 src/keyboard.c                                  |   6 +-
 src/menu.c                                      |   9 +-
 src/sfnt.c                                      |  10 +-
 src/sfntfont-android.c                          |   4 +-
 src/sfntfont.c                                  | 111 ++++-
 xcompile/lib/faccessat.c                        |   4 -
 xcompile/lib/getdelim.c                         | 147 ++++++
 xcompile/lib/getline.c                          |  27 ++
 xcompile/lib/gnulib.mk.in                       |  42 ++
 xcompile/lib/qcopy-acl.c                        |  36 +-
 xcompile/lib/verify.h                           |   8 +-
 xcompile/malloc/dynarray-skeleton.c             | 528 ++++++++++++++++++++++
 xcompile/malloc/dynarray.h                      | 177 ++++++++
 xcompile/malloc/dynarray_at_failure.c           |  40 ++
 xcompile/malloc/dynarray_emplace_enlarge.c      |  77 ++++
 xcompile/malloc/dynarray_finalize.c             |  66 +++
 xcompile/malloc/dynarray_resize.c               |  68 +++
 xcompile/malloc/dynarray_resize_clear.c         |  39 ++
 xcompile/malloc/scratch_buffer.h                | 135 ++++++
 xcompile/malloc/scratch_buffer_dupfree.c        |  41 ++
 xcompile/malloc/scratch_buffer_grow.c           |  56 +++
 xcompile/malloc/scratch_buffer_grow_preserve.c  |  67 +++
 xcompile/malloc/scratch_buffer_set_array_size.c |  64 +++
 81 files changed, 4118 insertions(+), 628 deletions(-)

diff --git a/.gitignore b/.gitignore
index cf73995840..3bc40c6748 100644
--- a/.gitignore
+++ b/.gitignore
@@ -231,6 +231,7 @@ ID
 # Executables.
 *.exe
 a.out
+lib-src/asset-directory-tool
 lib-src/be-resources
 lib-src/blessmail
 lib-src/ctags
@@ -253,6 +254,7 @@ nextstep/GNUstep/Emacs.base/Resources/Info-gnustep.plist
 src/bootstrap-emacs
 src/emacs
 src/emacs-[0-9]*
+src/sfnt
 src/Emacs
 src/temacs
 src/dmpstruct.h
diff --git a/INSTALL.android b/INSTALL.android
index ee54b053a9..63c856fe56 100644
--- a/INSTALL.android
+++ b/INSTALL.android
@@ -59,6 +59,41 @@ built for.
 The generated package can be uploaded onto an SD card (or similar
 medium) and installed on-device.
 
+BUILDING WITH OLD NDK VERSIONS
+
+Building Emacs with an old version of the Android NDK requires special
+setup.  This is because there is no separate C compiler binary for
+each version of Android in those versions of the NDK.
+
+Before running `configure', you must identify three variables:
+
+  - What kind of Android system you are building Emacs for.
+
+  - The minimum API version of Android you want to build Emacs for.
+
+  - The locations of the system root and include files for that
+    version of Android in the NDK.
+
+That information must then be specified as arguments to the NDK C
+compiler.  For example:
+
+  ./configure [...] \
+     ANDROID_CC="i686-linux-android-gcc \
+                --sysroot=/path/to/ndk/platforms/android-14/arch-x86/"
+     ANDROID_CFLAGS="-isystem /path/to/ndk/sysroot/usr/include \
+                    -isystem 
/path/to/ndk/sysroot/usr/include/i686-linux-android \
+                    -D__ANDROID_API__=14"
+
+Where __ANDROID_API__ and the version identifier in
+"platforms/android-14" defines the version of Android you are building
+for, and the include directories specify the paths to the relevant
+Android headers.  In addition, it may be necessary to specify
+"-gdwarf-2", due to a bug in the Android NDK.
+
+Emacs is known to build for Android 2.2 (API version 8) or later, and
+run on Android 2.3 or later.  It is supposed to run on Android 2.2 as
+well.
+
 
 This file is part of GNU Emacs.
 
diff --git a/admin/merge-gnulib b/admin/merge-gnulib
index b99e38672a..38f1418c75 100755
--- a/admin/merge-gnulib
+++ b/admin/merge-gnulib
@@ -37,7 +37,7 @@ GNULIB_MODULES='
   fchmodat fcntl fcntl-h fdopendir file-has-acl
   filemode filename filevercmp flexmember fpieee
   free-posix fstatat fsusage fsync futimens
-  getloadavg getopt-gnu getrandom gettime gettimeofday gitlog-to-changelog
+  getline getloadavg getopt-gnu getrandom gettime gettimeofday 
gitlog-to-changelog
   ieee754-h ignore-value intprops largefile libgmp lstat
   manywarnings memmem-simple mempcpy memrchr memset_explicit
   minmax mkostemp mktime
@@ -141,7 +141,7 @@ cp -- "$gnulib_srcdir"/lib/af_alg.h \
 ./autogen.sh
 
 # Finally, update the files in lib/ to xcompile/lib.
-rsync "$src"/lib "$src"/xcompile
+rsync -r "$src"/lib "$src"/xcompile
 
 # Remove unnecessary files.
 rm -f "$src"/xcompile/lib/*.mk.in "$src"/xcompile/lib/Makefile.in
diff --git a/build-aux/config.guess b/build-aux/config.guess
index 980b020838..69188da73d 100755
--- a/build-aux/config.guess
+++ b/build-aux/config.guess
@@ -1,10 +1,10 @@
 #! /bin/sh
 # Attempt to guess a canonical system name.
-#   Copyright 1992-2022 Free Software Foundation, Inc.
+#   Copyright 1992-2023 Free Software Foundation, Inc.
 
 # shellcheck disable=SC2006,SC2268 # see below for rationale
 
-timestamp='2022-09-17'
+timestamp='2023-01-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
@@ -60,7 +60,7 @@ version="\
 GNU config.guess ($timestamp)
 
 Originally written by Per Bothner.
-Copyright 1992-2022 Free Software Foundation, Inc.
+Copyright 1992-2023 Free Software Foundation, Inc.
 
 This is free software; see the source for copying conditions.  There is NO
 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
diff --git a/build-aux/config.sub b/build-aux/config.sub
index baf1512b3c..d6731d655c 100755
--- a/build-aux/config.sub
+++ b/build-aux/config.sub
@@ -1,10 +1,10 @@
 #! /bin/sh
 # Configuration validation subroutine script.
-#   Copyright 1992-2022 Free Software Foundation, Inc.
+#   Copyright 1992-2023 Free Software Foundation, Inc.
 
 # shellcheck disable=SC2006,SC2268 # see below for rationale
 
-timestamp='2022-09-17'
+timestamp='2023-01-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
@@ -76,7 +76,7 @@ Report bugs and patches to <config-patches@gnu.org>."
 version="\
 GNU config.sub ($timestamp)
 
-Copyright 1992-2022 Free Software Foundation, Inc.
+Copyright 1992-2023 Free Software Foundation, Inc.
 
 This is free software; see the source for copying conditions.  There is NO
 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
diff --git a/configure.ac b/configure.ac
index 92cf58fdf2..1f5618a172 100644
--- a/configure.ac
+++ b/configure.ac
@@ -31,8 +31,19 @@ if test "$XCONFIGURE" = "android"; then
   # Android!
   AC_MSG_NOTICE([called to recursively configure Emacs \
 for Android.])
-  # Set CC to ANDROID_CC.
+  # Set CC to ANDROID_CC and CFLAGS to ANDROID_CFLAGS.
   CC=$ANDROID_CC
+  # Set -Wno-implicit-function-declaration.  Building Emacs for older
+  # versions of Android requires configure tests to fail if the
+  # functions are not defined, as the Android library in the NDK
+  # defines subroutines that are not available in the headers being
+  # used.
+  CFLAGS="$ANDROID_CFLAGS -Werror=implicit-function-declaration"
+  # Don't explicitly enable support for large files unless Emacs is
+  # being built for API 21 or later.  Otherwise, mmap does not work.
+  if test "$ANDROID_SDK" -lt "21"; then
+    enable_largefile=no
+  fi
 fi
 
 dnl Set emacs_config_options to the options of 'configure', quoted for the 
shell,
@@ -758,7 +769,10 @@ tools such as aapt, dx, and aidl):
 
 The cross-compiler should then be specified:
 
-              ANDROID_CC=/path/to/armv7a-linux-androideabi19-clang])
+              ANDROID_CC=/path/to/armv7a-linux-androideabi19-clang
+
+In addition, you may pass any special arguments to the cross-compiler
+via the ANDROID_CFLAGS environment variable.])
 elif test "$with_android" = "no" || test "$with_android" = ""; then
   ANDROID=no
 else
@@ -808,6 +822,29 @@ EOF
 a valid path to android.jar.  See config.log for more details.])
   fi
 
+  AC_CACHE_CHECK([whether or not android.jar is new enough],
+    [emacs_cv_android_s_or_later],
+    AS_IF([rm -f conftest.class
+cat << EOF > conftest.java
+
+import android.os.Build;
+
+class conftest
+{
+  private static int test = Build.VERSION_CODES.S;
+}
+
+EOF
+("$JAVAC" -classpath "$with_android" -target 1.7 -source 1.7 conftest.java \
+  -d . >&AS_MESSAGE_LOG_FD 2>&1) && test -s conftest.class && rm -f 
conftest.class],
+          [emacs_cv_android_s_or_later=yes],
+          [emacs_cv_android_s_or_later=no]))
+
+  if test "$emacs_cv_android_s_or_later" = "no"; then
+    AC_MSG_ERROR([Emacs must be built with an android.jar file produced for \
+Android 13 (S) or later.])
+  fi
+
   ANDROID_JAR="$with_android"
 
   AC_PATH_PROGS([AAPT], [aapt], [], "${SDK_BUILD_TOOLS}:$PATH")
@@ -845,6 +882,7 @@ for your machine.  For example:
   AC_MSG_CHECKING([for the kind of Android system Emacs is being built for])
   cc_target=`${ANDROID_CC} -v 2>&1 | sed -n 's/Target: //p'`
   case "$cc_target" in
+[
     *i[3-6]86*) android_abi=x86
       ;;
     *x86_64*) android_abi=x86_64
@@ -857,10 +895,13 @@ for your machine.  For example:
       ;;
     *arm*) android_abi=armeabi
       ;;
+]
     *) AC_MSG_ERROR([configure could not determine the type of Android \
 binary Emacs is being configured for.  Please port this configure script \
 to your Android system, or verify that you specified the correct compiler \
-in the ANDROID_CC variable when you ran configure.])
+in the ANDROID_CC variable when you ran configure.
+
+The compiler target is: $cc_target])
       ;;
   esac
   AC_MSG_RESULT([$android_abi])
@@ -879,18 +920,121 @@ in the ANDROID_CC variable when you ran configure.])
     AC_MSG_RESULT([$android_sdk])
     ANDROID_MIN_SDK=$android_sdk
   else
-    AC_MSG_RESULT([unknown ($cc_target); assuming 8])
-    AC_MSG_WARN([configure could not determine the versions of Android \
+    # This is probably GCC.
+    [ cat << EOF > conftest.c
+#include <android/api-level.h>
+extern const char *foo;
+
+int
+main (void)
+{
+#if __ANDROID_API__ < 7
+   foo = "emacs_api_6";
+#elif __ANDROID_API__ < 8
+   foo = "emacs_api_7";
+#elif __ANDROID_API__ < 9
+   foo = "emacs_api_8";
+#elif __ANDROID_API__ < 10
+  foo = "emacs_api_9";
+#elif __ANDROID_API__ < 11
+  foo = "emacs_api_10";
+#elif __ANDROID_API__ < 12
+  foo = "emacs_api_11";
+#elif __ANDROID_API__ < 13
+  foo = "emacs_api_12";
+#elif __ANDROID_API__ < 14
+   foo = "emacs_api_13";
+#elif __ANDROID_API__ < 15
+   foo = "emacs_api_14";
+#elif __ANDROID_API__ < 16
+   foo = "emacs_api_15";
+#elif __ANDROID_API__ < 17
+   foo = "emacs_api_16";
+#elif __ANDROID_API__ < 18
+   foo = "emacs_api_17";
+#elif __ANDROID_API__ < 19
+   foo = "emacs_api_18";
+#elif __ANDROID_API__ < 20
+   foo = "emacs_api_19";
+#elif __ANDROID_API__ < 21
+   foo = "emacs_api_20";
+#elif __ANDROID_API__ < 22
+   foo = "emacs_api_21";
+#elif __ANDROID_API__ < 23
+   foo = "emacs_api_22";
+#elif __ANDROID_API__ < 24
+   foo = "emacs_api_23";
+#elif __ANDROID_API__ < 25
+   foo = "emacs_api_24";
+#elif __ANDROID_API__ < 26
+   foo = "emacs_api_25";
+#elif __ANDROID_API__ < 27
+   foo = "emacs_api_26";
+#elif __ANDROID_API__ < 28
+   foo = "emacs_api_27";
+#elif __ANDROID_API__ < 29
+   foo = "emacs_api_28";
+#elif __ANDROID_API__ < 30
+   foo = "emacs_api_29";
+#elif __ANDROID_API__ < 31
+   foo = "emacs_api_30";
+#elif __ANDROID_API__ < 32
+   foo = "emacs_api_31";
+#elif __ANDROID_API__ < 33
+   foo = "emacs_api_32";
+#elif __ANDROID_API__ < 34
+   foo = "emacs_api_33";
+#else
+   foo = "emacs_api_future";
+#endif
+}
+EOF]
+
+    AC_CACHE_VAL([emacs_cv_android_api],
+                [$ANDROID_CC $ANDROID_CFLAGS -c conftest.c -o conftest.o \
+                 && emacs_cv_android_api=`grep -ao -E                    \
+                    "emacs_api_([[0-9][0-9]]?|future)" conftest.o`])
+    android_sdk="$emacs_cv_android_api"
+    rm -rf conftest.c conftest.o
+
+    # If this version of the NDK requires __ANDROID_API__ to be
+    # specified, then complain to the user.
+    if test "$android_sdk" = "emacs_api_future"; then
+       AC_MSG_ERROR([The version of Android to build for was not specified.
+You must tell the Android compiler what version of Android to build for,
+by defining the __ANDROID_API__ preprocessor macro in ANDROID_CC, like so:
+
+    ANDROID_CC="/path/to/ndk/arm-linux-android-gcc -D__ANDROID_API__=8"])
+    fi
+
+    if test -n "$android_sdk"; then
+       android_sdk=`echo $android_sdk | sed -n 's/emacs_api_//p'`
+       AC_MSG_RESULT([$android_sdk])
+       ANDROID_MIN_SDK=$android_sdk
+    else
+      AC_MSG_RESULT([unknown ($cc_target); assuming 8])
+      AC_MSG_ERROR([configure could not determine the versions of Android \
 a binary built with this compiler will run on.  The generated application \
 package will likely install on older systems but crash on startup.])
+      android_sdk=8
+    fi
   fi
   AC_SUBST([ANDROID_MIN_SDK])
 
+  # Now tell java/Makefile if Emacs is being built for Android 4.3 or
+  # earlier.
+  ANDROID_SDK_18_OR_EARLIER=
+  if test "$android_sdk" -lt "18"; then
+     ANDROID_SDK_18_OR_EARLIER=yes
+  fi
+  AC_SUBST([ANDROID_SDK_18_OR_EARLIER])
+
   # Save confdefs.h and config.log for now.
   mv -f confdefs.h _confdefs.h
   mv -f config.log _config.log
 
-  AS_IF([XCONFIGURE=android ANDROID_CC="$ANDROID_CC" $0], [],
+  AS_IF([XCONFIGURE=android ANDROID_CC="$ANDROID_CC" \
+         ANDROID_SDK="$android_sdk" $0], [],
     [AC_MSG_ERROR([Failed to cross-configure Emacs for android.])])
 
   # Now set ANDROID to yes.
@@ -1989,7 +2133,6 @@ AC_CACHE_CHECK([for math library],
          d = frexp (d, &i);
          d = ldexp (d, i);
          d = log (d);
-         d = log2 (d);
          d = log10 (d);
          d = pow (d, d);
          d = rint (d);
@@ -2283,6 +2426,9 @@ for Android, but all API calls need to be stubbed out])
     # Link with the sfnt font library and sfntfont.o, along with
     # sfntfont-android.o.
     ANDROID_OBJ="$ANDROID_OBJ sfnt.o sfntfont.o sfntfont-android.o"
+
+    # Check for some functions not always present in the NDK.
+    AC_CHECK_DECLS([android_get_device_api_level])
   fi
 fi
 
diff --git a/doc/emacs/android.texi b/doc/emacs/android.texi
index 8806a2a2bf..01a2e68a97 100644
--- a/doc/emacs/android.texi
+++ b/doc/emacs/android.texi
@@ -1,5 +1,5 @@
 @c This is part of the Emacs manual.
-@c Copyright (C) 2021--2023 Free Software Foundation, Inc.
+@c Copyright (C) 2023 Free Software Foundation, Inc.
 @c See file emacs.texi for copying conditions.
 @node Android
 @appendix Emacs and Android
@@ -9,6 +9,10 @@
 Alliance.  This section describes the peculiarities of using Emacs on
 an Android device running Android 2.2 or later.
 
+  Android devices commonly rely on user input through a touch screen
+or digitizer device.  For more information about using them with
+Emacs, @pxref{other Input Devices}.
+
 @menu
 * What is Android?::   Preamble.
 * Android Startup::     Starting up Emacs on Android.
@@ -108,11 +112,6 @@ There are no @file{.} and @file{..} directories inside the
 @item
 Files in the @file{/assets} directory are always read only, and have
 to be completely read in to memory each time they are opened.
-
-@item
-@code{directory-files} does not return a useful value on the
-@file{/assets} directory itself, and does not return subdirectories
-inside subdirectories of the @file{/assets} directory.
 @end itemize
 
   Aside from the @file{/assets} directory, Android programs normally
diff --git a/doc/emacs/emacs.texi b/doc/emacs/emacs.texi
index d21d09bf92..9fd169cd5c 100644
--- a/doc/emacs/emacs.texi
+++ b/doc/emacs/emacs.texi
@@ -225,6 +225,7 @@ Appendices
 * Haiku::               Using Emacs on Haiku.
 * Android::             Using Emacs on Android.
 * Microsoft Windows::   Using Emacs on Microsoft Windows and MS-DOS.
+* Other Input Devices::        Using Emacs with other input devices.
 * Manifesto::           What's GNU?  Gnu's Not Unix!
 
 * Glossary::            Terms used in this manual.
@@ -1265,6 +1266,10 @@ Emacs and Android
 * Android Environment:: Running Emacs under Android.
 * Android Fonts::      Font selection under Android.
 
+Emacs and unconventional input devices
+
+* Touchscreens::       Using Emacs on touchscreens.
+
 Emacs and Microsoft Windows/MS-DOS
 
 * Windows Startup::     How to start Emacs on Windows.
@@ -1636,6 +1641,7 @@ Lisp programming.
 @include macos.texi
 @include haiku.texi
 @include android.texi
+@include input.texi
 @c Includes msdos-xtra.
 @include msdos.texi
 @include gnu.texi
diff --git a/doc/emacs/input.texi b/doc/emacs/input.texi
new file mode 100644
index 0000000000..0e029c401e
--- /dev/null
+++ b/doc/emacs/input.texi
@@ -0,0 +1,60 @@
+@c This is part of the Emacs manual.
+@c Copyright (C) 2023 Free Software Foundation, Inc.
+@c See file emacs.texi for copying conditions.
+@node Other Input Devices
+@appendix Emacs and unconventional input devices
+@cindex other input devices
+
+  Emacs was originally developed with the assumption that users will
+be sitting in front of a desktop computer, with a keyboard and perhaps
+a suitable pointing device such as a mouse.
+
+  However, recent developments in the X Window System, and in other
+operating systems such as Android, mean that this assumption no longer
+holds true.  As a result, Emacs now has support for other kinds of
+input devices, which is detailed here.
+
+@menu
+* Touchscreens::               Using Emacs on touchscreens.
+@end menu
+
+@node Touchscreens
+@section Using Emacs on touchscreens
+@cindex touchscreens
+
+  Touchscreen input works by having the user press tools onto the
+screen, which can be his own fingers, or a pointing device such as a
+stylus, in order to manipulate the contents there in.
+
+  When running under the X Window System or Android, Emacs
+automatically detects and maps the following touchscreen gestures to
+common actions:
+
+@itemize @bullet
+@item
+@cindex tapping, touchscreens
+  ``Tapping'', meaning to briefly place and lift a tool from the
+display, will result in Emacs selecting the window that was tapped,
+and executing any command bound to @code{mouse-1} at that location in
+the window.  If the tap happened on top of a link (@pxref{Mouse
+References}), then Emacs will follow the link instead.
+
+@item
+@cindex scrolling, touchscreens
+  ``Scrolling'', meaning to place a tool on the display and move it up
+or down, will result in Emacs scrolling the window contents in the
+direction where the tool moves.
+
+@item
+@cindex dragging, touchscreens
+  ``Dragging'', meaning to place a tool on the display and leave it
+there for a while before moving the tool around, will make Emacs set
+the point to where the tool was and begin selecting text under the
+tool as it moves around, much like what would happen if @code{mouse-1}
+were to be held down.  @xref{Mouse Commands}.
+@end itemize
+
+@vindex touch-screen-delay
+  By default, Emacs considers a tool as having been left on the
+display for a while after 0.7 seconds, but this can be changed by
+customizing the variable @code{touch-screen-delay}.
diff --git a/doc/lispref/commands.texi b/doc/lispref/commands.texi
index 6d1ce145fb..59367a2cec 100644
--- a/doc/lispref/commands.texi
+++ b/doc/lispref/commands.texi
@@ -1999,24 +1999,24 @@ each point is represented by a cons of an arbitrary 
number identifying
 the point and a mouse position list (@pxref{Click Events}) specifying
 the position of the finger when the event occurred.
 
-In addition, @code{touchscreen-begin} events also have imaginary
-prefixes keys added by @code{read-key-sequence} when they originate on
-top of a special part of a frame or window.  @xref{Key Sequence
-Input}.  The reason the other touch screen events do not undergo this
-treatment is that they are rarely useful without being used in tandem
-from their corresponding @code{touchscreen-begin} events.
-
 @table @code
 @cindex @code{touchscreen-begin} event
 @item (touchscreen-begin @var{point})
 This event is sent when @var{point} is created by the user pressing a
 finger against the touchscreen.
 
+These events also have imaginary prefixes keys added by
+@code{read-key-sequence} when they originate on top of a special part
+of a frame or window.  @xref{Key Sequence Input}.  The reason the
+other touch screen events do not undergo this treatment is that they
+are rarely useful without being used in tandem from their
+corresponding @code{touchscreen-begin} events.
+
 @cindex @code{touchscreen-update} event
 @item (touchscreen-update @var{points})
 This event is sent when a point on the touchscreen has changed
 position.  @var{points} is a list of touch points containing the
-up-to-date positions of each touch point currently on the touchscreen.
+up-to-date positions of each touch point currently on the touchscxcompile/reen.
 
 @cindex @code{touchscreen-end} event
 @item (touchscreen-end @var{point})
@@ -2030,6 +2030,36 @@ generate any corresponding @code{touchscreen-begin} or
 @code{touchscreen-end} events; instead, the menu bar may be displayed
 when @code{touchscreen-end} should have been delivered.
 
+@cindex handling touch screen events
+@cindex tap and drag, touch screen gestures
+Emacs provides two functions to handle touch screen events.  They are
+intended to be used by a command bound to @code{touchscreen-begin} to
+handle common gestures.
+
+@defun touch-screen-track-tap event &optional update data
+This function is used to track a single ``tap'' gesture originating
+from the @code{touchscreen-begin} event @var{event}, often used to
+set the point or to activate a button.  It waits for a
+@code{touchscreen-end} event with the same touch identifier to arrive,
+at which point it returns @code{t}, signifying the end of the gesture.
+
+If a @code{touchscreen-update} event arrives in the mean time and
+contains at least one touchpoint with the same identifier as in
+@var{event}, the function @var{update} is called with two arguments,
+the list of touchpoints in that @code{touchscreen-update} event, and
+@var{data}.
+
+If any other event arrives in the mean time, @code{nil} is returned.
+The caller should not perform any action in that case.
+@end defun
+
+@defun touch-screen-track-drag event update &optional data
+This function is used to track a single ``drag'' gesture originating
+from the @code{touchscreen-begin} event @code{event}.  Currently, it
+behaves identically to @code{touch-screen-track-tap}, but differences
+are anticipated in the future.
+@end defun
+
 @node Focus Events
 @subsection Focus Events
 @cindex focus event
diff --git a/doc/lispref/frames.texi b/doc/lispref/frames.texi
index fb96b96ec8..2ceafab7a6 100644
--- a/doc/lispref/frames.texi
+++ b/doc/lispref/frames.texi
@@ -3725,9 +3725,9 @@ This function displays a pop-up menu and returns an 
indication of
 what selection the user makes.
 
 The argument @var{position} specifies where on the screen to put the
-top left corner of the menu.  It can be either a mouse button event
-(which says to put the menu where the user actuated the button) or a
-list of this form:
+top left corner of the menu.  It can be either a mouse button or
+@code{touchscreen-begin} event (which says to put the menu where the
+user actuated the button) or a list of this form:
 
 @example
 ((@var{xoffset} @var{yoffset}) @var{window})
diff --git a/etc/NEWS b/etc/NEWS
index cde6783349..a309cd17a9 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -24,6 +24,12 @@ applies, and please also update docstrings as needed.
 
 * Installation Changes in Emacs 30.1
 
+** Emacs has been ported to the Android operating system.
+This requires Emacs to be compiled on another computer.  The Android
+NDK, SDK, and a suitable Java compiler must also be installed.
+
+See the file 'INSTALL.android' for more details.
+
 
 * Startup Changes in Emacs 30.1
 
@@ -53,6 +59,10 @@ trash when deleting.  Default is nil.
 
 * Editing Changes in Emacs 30.1
 
+** Emacs now has better support for touchscreen events.
+Many touch screen gestures are now implemented, as is support for
+tapping buttons and opening menus.
+
 ** New helper variable 'transpose-sexps-function'.
 Emacs now can set this variable to customize the behavior of the
 'transpose-sexps' function.
@@ -189,6 +199,16 @@ This user option has been obsoleted in Emacs 27, use
 
 * Lisp Changes in Emacs 30.1
 
++++
+** 'x-popup-menu' now understands touch screen events.
+When a 'touchscreen-begin' or 'touchscreen-end' event is passed as the
+POSITION argument, it will behave as if that event was a mouse event.
+
+** New functions for handling touch screen events.
+The new functions 'touch-screen-track-tap' and
+'touch-screen-track-drag' handle tracking common touch screen gestures
+from within a command.
+
 ** New or changed byte-compilation warnings
 
 ---
diff --git a/java/Makefile.in b/java/Makefile.in
index c539fb0f1f..22c912fdce 100644
--- a/java/Makefile.in
+++ b/java/Makefile.in
@@ -21,6 +21,10 @@ top_builddir = @top_builddir@
 top_srcdir = @top_srcdir@
 version = @version@
 
+# This is the host lib-src and lib, not the cross compiler's lib-src.
+libsrc = ../lib-src
+EXEEXT = @EXEEXT@
+
 -include ${top_builddir}/src/verbose.mk
 
 SHELL = @SHELL@
@@ -29,14 +33,25 @@ AAPT = @AAPT@
 D8 = @D8@
 ZIPALIGN = @ZIPALIGN@
 JARSIGNER = @JARSIGNER@
+JARSIGNER_FLAGS =
 ANDROID_JAR = @ANDROID_JAR@
 ANDROID_ABI = @ANDROID_ABI@
+ANDROID_SDK_18_OR_EARLIER = @ANDROID_SDK_18_OR_EARLIER@
 
 WARN_JAVAFLAGS = -Xlint:deprecation
 JAVAFLAGS = -classpath "$(ANDROID_JAR):." -target 1.7 -source 1.7 \
   $(WARN_JAVAFLAGS)
 
-SIGN_EMACS = -keystore emacs.keystore -storepass emacs1
+# Android 4.3 and earlier require Emacs to be signed with a different
+# digital signature algorithm.
+
+ifneq (,$(ANDROID_SDK_18_OR_EARLIER))
+JARSIGNER_FLAGS = -sigalg MD5withRSA -digestalg SHA1
+else
+JARSIGNER_FLAGS =
+endif
+
+SIGN_EMACS = -keystore emacs.keystore -storepass emacs1 $(JARSIGNER_FLAGS)
 
 JAVA_FILES = $(shell find . -type f -name *.java)
 CLASS_FILES = $(foreach file,$(JAVA_FILES),$(basename $(file)).class)
@@ -82,7 +97,14 @@ CROSS_LIBS = ../xcompile/src/libemacs.so
 ../xcompile/lib-src/ctags ../xcompile/lib-src/ebrowse &:
        make -C ../xcompile lib-src/$(notdir $@)
 
-emacs.apk-in: $(CROSS_BINS) $(CROSS_LIBS) AndroidManifest.xml
+# This is needed to generate the ``.directory-tree'' file used by the
+# Android emulations of readdir and faccessat.
+
+$(libsrc)/asset-directory-tool:
+       $(MAKE) -C $(libsrc) $(notdir $@)
+
+emacs.apk-in: $(CROSS_BINS) $(CROSS_LIBS) $(libsrc)/asset-directory-tool \
+  AndroidManifest.xml
 # Make the working directory for this stuff
        rm -rf install_temp
        mkdir -p install_temp/lib/$(ANDROID_ABI)
@@ -106,6 +128,9 @@ emacs.apk-in: $(CROSS_BINS) $(CROSS_LIBS) 
AndroidManifest.xml
          rm -rf $${subdir}/[mM]akefile*[.-]in ;                \
          rm -rf $${subdir}/Makefile;                           \
        done
+# Generate the directory tree for those directories.
+       $(libsrc)/asset-directory-tool install_temp/assets      \
+         install_temp/assets/directory-tree
 # Install architecture dependents to lib/$(ANDROID_ABI).  This
 # perculiar naming scheme is required to make Android preserve these
 # binaries upon installation.
@@ -120,10 +145,12 @@ emacs.apk-in: $(CROSS_BINS) $(CROSS_LIBS) 
AndroidManifest.xml
            cp -f $$file install_temp/lib/$(ANDROID_ABI);       \
          fi                                                    \
        done
-# Package everything.
-       $(AAPT) package -I "$(ANDROID_JAR)" -F $@ -f -M AndroidManifest.xml
+# Package everything.  Specifying the assets on this command line is
+# necessary for AAssetManager_getNextFileName to work on old versions
+# of Android.
+       $(AAPT) package -I "$(ANDROID_JAR)" -F $@ -f -M AndroidManifest.xml \
+         -A install_temp/assets
        pushd install_temp; $(AAPT) add ../$@ `find lib -type f`; popd
-       pushd install_temp; $(AAPT) add ../$@ `find assets -type f`; popd
        rm -rf install_temp
 
 # Makefile itself.
diff --git a/java/debug.sh b/java/debug.sh
index aa80aeeebc..7008664c04 100755
--- a/java/debug.sh
+++ b/java/debug.sh
@@ -93,7 +93,7 @@ while [ $# -gt 0 ]; do
     shift
 done
 
-if [ -z $devices ]; then
+if [ -z "$devices" ]; then
     echo "No devices are available."
     exit 1
 fi
@@ -117,25 +117,43 @@ if [ -z $app_data_dir ]; then
    echo "Is it installed?"
 fi
 
-echo "Found application data directory at $app_data_dir..."
-
-# Find which PIDs are associated with org.gnu.emacs
-package_uid=`adb -s $device shell run-as $package id -u`
-
-if [ -z $package_uid ]; then
-    echo "Failed to obtain UID of packages named $package"
-    exit 1
-fi
-
-# First, run ps -u $package_uid -o PID,CMD to fetch the list of
-# process IDs.
-package_pids=`adb -s $device shell run-as $package ps -u $package_uid -o 
PID,CMD`
-
-# Next, remove lines matching "ps" itself.
-package_pids=`awk -- '{
-  if (!match ($0, /(PID|ps)/))
-    print $1
-}' <<< $package_pids`
+echo "Found application data directory at" "$app_data_dir"
+
+# Generate an awk script to extract PIDs from Android ps output.  It
+# is enough to run `ps' as the package user on newer versions of
+# Android, but that doesn't work on Android 2.3.
+cat << EOF > tmp.awk
+BEGIN {
+  pid = 0;
+  pid_column = 2;
+}
+
+{
+  # Remove any trailing carriage return from the input line.
+  gsub ("\r", "", \$NF)
+
+  # If this is line 1, figure out which column contains the PID.
+  if (NR == 1)
+    {
+      for (n = 1; n <= NF; ++n)
+       {
+         if (\$n == "PID")
+           pid_column=n;
+       }
+    }
+  else if (\$NF == "$package")
+   print \$pid_column
+}
+EOF
+
+# Make sure that file disappears once this script exits.
+trap "rm -f $(pwd)/tmp.awk" 0
+
+# First, run ps to fetch the list of process IDs.
+package_pids=`adb -s $device shell ps`
+
+# Next, extract the list of PIDs currently running.
+package_pids=`awk -f tmp.awk <<< $package_pids`
 
 if [ "$attach_existing" != "yes" ]; then
     # Finally, kill each existing process.
@@ -149,19 +167,20 @@ if [ "$attach_existing" != "yes" ]; then
     echo "Starting activity $activity and attaching debugger"
 
     # Exit if the activity could not be started.
-    adb -s $device shell am start -D "$package/$activity"
+    adb -s $device shell am start -D -n "$package/$activity"
     if [ ! $? ]; then
        exit 1;
     fi
 
+    # Sleep for a bit.  Otherwise, the process may not have started
+    # yet.
+    sleep 1
+
     # Now look for processes matching the package again.
-    package_pids=`adb -s $device shell run-as $package ps -u $package_uid -o 
PID,CMD`
+    package_pids=`adb -s $device shell ps`
 
     # Next, remove lines matching "ps" itself.
-    package_pids=`awk -- '{
-  if (!match ($0, /(PID|ps)/))
-    print $1
-}' <<< $package_pids`
+    package_pids=`awk -f tmp.awk <<< $package_pids`
 fi
 
 pid=$package_pids
@@ -170,10 +189,10 @@ num_pids=`wc -w <<< "$package_pids"`
 if [ $num_pids -gt 1 ]; then
     echo "More than one process was started:"
     echo ""
-    adb -s $device shell run-as $package ps -u $package_uid | awk -- '{
-      if (!match ($0, /ps/))
-        print $0
-    }'
+    adb -s $device shell run-as $package ps | awk -- "{
+      if (!match (\$0, /ps/) && match (\$0, /$package/))
+        print \$0
+    }"
     echo ""
     printf "Which one do you want to attach to? "
     read pid
@@ -182,10 +201,12 @@ elif [ -z $package_pids ]; then
     exit 1
 fi
 
-# This isn't necessary when attaching gdb to an existing process.
+# If either --jdb was specified or debug.sh is not connecting to an
+# existing process, then store a suitable JDB invocation in
+# jdb_command.  GDB will then run JDB to unblock the application from
+# the wait dialog after startup.
+
 if [ "$jdb" = "yes" ] || [ "$attach_existing" != yes ]; then
-    # Start JDB to make the wait dialog disappear.
-    echo "Attaching JDB to unblock the application."
     adb -s $device forward --remove-all
     adb -s $device forward "tcp:$jdb_port" "jdwp:$pid"
 
@@ -203,20 +224,42 @@ if [ "$jdb" = "yes" ] || [ "$attach_existing" != yes ]; 
then
        $jdb_command
        exit 1
     fi
+fi
 
-    exec 4<> /tmp/file-descriptor-stamp
+if [ -n "$jdb_command" ]; then
+    echo "Starting JDB to unblock application."
 
-    # Now run JDB with IO redirected to file descriptor 4 in a subprocess.
-    $jdb_command <&4 >&4 &
+    # Start JDB to unblock the application.
+    coproc JDB { $jdb_command; }
 
-    character=
-    # Next, wait until the prompt is found.
-    while read -n1 -u 4 character; do
-       if [ "$character" = ">" ]; then
-           echo "JDB attached successfully"
-           break;
+    # Tell JDB to first suspend all threads.
+    echo "suspend" >&${JDB[1]}
+
+    # Tell JDB to print a magic string once the program is
+    # initialized.
+    echo "print \"__verify_jdb_has_started__\"" >&${JDB[1]}
+
+    # Now wait for JDB to give the string back.
+    line=
+    while :; do
+       read -u ${JDB[0]} line
+       if [ ! $? ]; then
+           echo "Failed to read JDB output"
+           exit 1
        fi
+
+       case "$line" in
+           *__verify_jdb_has_started__*)
+               # Android only polls for a Java debugger every 200ms, so
+               # the debugger must be connected for at least that long.
+               echo "Pausing 1 second for the program to continue."
+               sleep 1
+               break
+               ;;
+       esac
     done
+
+    # Note that JDB does not exit until GDB is fully attached!
 fi
 
 # See if gdbserver has to be uploaded
@@ -234,18 +277,19 @@ fi
 
 echo "Attaching gdbserver to $pid on $device..."
 exec 5<> /tmp/file-descriptor-stamp
+rm -f /tmp/file-descriptor-stamp
 
 if [ -z "$gdbserver" ]; then
     adb -s $device shell run-as $package $gdbserver_bin --once \
-       "+debug.$package_uid.socket" --attach $pid >&5 &
-    gdb_socket="localfilesystem:$app_data_dir/debug.$package_uid.socket"
+       "+debug.$package.socket" --attach $pid >&5 &
+    gdb_socket="localfilesystem:$app_data_dir/debug.$package.socket"
 else
     # Normally the program cannot access $gdbserver_bin when it is
     # placed in /data/local/tmp.
     adb -s $device shell $gdbserver_bin --once \
-       "+/data/local/tmp/debug.$package_uid.socket" \
+       "+/data/local/tmp/debug.$package.socket" \
        --attach $pid >&5 &
-    gdb_socket="localfilesystem:/data/local/tmp/debug.$package_uid.socket"
+    gdb_socket="localfilesystem:/data/local/tmp/debug.$package.socket"
 fi
 
 # Wait until gdbserver successfully runs.
@@ -256,7 +300,7 @@ while read -u 5 line; do
            break;
            ;;
        *error* | *Error* | failed )
-           echo $line
+           echo "GDB error:" $line
            exit 1
            ;;
        * )
@@ -264,19 +308,18 @@ while read -u 5 line; do
     esac
 done
 
-if [ "$attach_existing" != "yes" ]; then
-    # Send EOF to JDB to make it go away.  This will also cause
-    # Android to allow Emacs to continue executing.
-    echo "Making JDB go away..."
-    echo "exit" >&4
-    read -u 4 line
-    echo "JDB has gone away with $line"
+# Now that GDB is attached, tell the Java debugger to resume execution
+# and then exit.
+
+if [ -n "$jdb_command" ]; then
+    echo "resume" >&${JDB[1]}
+    echo "exit" >&${JDB[1]}
 fi
 
 # Forward the gdb server port here.
 adb -s $device forward "tcp:$gdb_port" $gdb_socket
 if [ ! $? ]; then
-    echo "Failed to forward $app_data_dir/debug.$package_uid.socket"
+    echo "Failed to forward $app_data_dir/debug.$package.socket"
     echo "to $gdb_port!  Perhaps you need to specify a different port"
     echo "with --port?"
     exit 1;
@@ -284,4 +327,4 @@ fi
 
 # Finally, start gdb with any extra arguments needed.
 cd "$oldpwd"
-gdb --eval-command "" --eval-command "target remote localhost:$gdb_port" 
$gdbargs
+gdb --eval-command "target remote localhost:$gdb_port" $gdbargs
diff --git a/java/org/gnu/emacs/EmacsContextMenu.java 
b/java/org/gnu/emacs/EmacsContextMenu.java
index 00e204c994..ac67ebe4aa 100644
--- a/java/org/gnu/emacs/EmacsContextMenu.java
+++ b/java/org/gnu/emacs/EmacsContextMenu.java
@@ -168,10 +168,22 @@ public class EmacsContextMenu
       {
        if (item.subMenu != null)
          {
-           /* This is a submenu.  Create the submenu and add the
-              contents of the menu to it.  */
-           submenu = menu.addSubMenu (item.itemName);
-           item.subMenu.inflateMenuItems (submenu);
+           try
+             {
+               /* This is a submenu.  On versions of Android which
+                  support doing so, create the submenu and add the
+                  contents of the menu to it.  */
+               submenu = menu.addSubMenu (item.itemName);
+             }
+           catch (UnsupportedOperationException exception)
+             {
+               /* This version of Android has a restriction
+                  preventing submenus from being added to submenus.
+                  Inflate everything into the parent menu
+                  instead.  */
+               item.subMenu.inflateMenuItems (menu);
+               continue;
+             }
 
            /* This is still needed to set wasSubmenuSelected.  */
            menuItem = submenu.getItem ();
diff --git a/java/org/gnu/emacs/EmacsCopyArea.java 
b/java/org/gnu/emacs/EmacsCopyArea.java
index 5d72a7860c..7a97d70679 100644
--- a/java/org/gnu/emacs/EmacsCopyArea.java
+++ b/java/org/gnu/emacs/EmacsCopyArea.java
@@ -66,19 +66,11 @@ public class EmacsCopyArea
 
     paint = gc.gcPaint;
 
-    canvas = destination.lockCanvas ();
+    canvas = destination.lockCanvas (gc);
 
     if (canvas == null)
       return;
 
-    canvas.save ();
-
-    if (gc.real_clip_rects != null)
-      {
-       for (i = 0; i < gc.real_clip_rects.length; ++i)
-         canvas.clipRect (gc.real_clip_rects[i]);
-      }
-
     /* A copy must be created or drawBitmap could end up overwriting
        itself.  */
     srcBitmap = source.getBitmap ();
@@ -189,7 +181,6 @@ public class EmacsCopyArea
        maskBitmap.recycle ();
       }
 
-    canvas.restore ();
     destination.damageRect (rect);
   }
 }
diff --git a/java/org/gnu/emacs/EmacsDialog.java 
b/java/org/gnu/emacs/EmacsDialog.java
index 5bc8efa597..7d88a23c58 100644
--- a/java/org/gnu/emacs/EmacsDialog.java
+++ b/java/org/gnu/emacs/EmacsDialog.java
@@ -168,9 +168,6 @@ public class EmacsDialog implements 
DialogInterface.OnDismissListener
            button = buttons.get (1);
            dialog.setButton (DialogInterface.BUTTON_NEUTRAL,
                              button.name, button);
-           buttonView
-             = dialog.getButton (DialogInterface.BUTTON_NEUTRAL);
-           buttonView.setEnabled (button.enabled);
          }
 
        if (size >= 3)
diff --git a/java/org/gnu/emacs/EmacsDrawLine.java 
b/java/org/gnu/emacs/EmacsDrawLine.java
index 8941d4c217..827feb96df 100644
--- a/java/org/gnu/emacs/EmacsDrawLine.java
+++ b/java/org/gnu/emacs/EmacsDrawLine.java
@@ -49,19 +49,11 @@ public class EmacsDrawLine
                     Math.min (y, y2 + 1),
                     Math.max (x2 + 1, x),
                     Math.max (y2 + 1, y));
-    canvas = drawable.lockCanvas ();
+    canvas = drawable.lockCanvas (gc);
 
     if (canvas == null)
       return;
 
-    canvas.save ();
-
-    if (gc.real_clip_rects != null)
-      {
-       for (i = 0; i < gc.real_clip_rects.length; ++i)
-         canvas.clipRect (gc.real_clip_rects[i]);
-      }
-
     paint.setStyle (Paint.Style.STROKE);
 
     if (gc.clip_mask == null)
@@ -71,7 +63,6 @@ public class EmacsDrawLine
 
     /* DrawLine with clip mask not implemented; it is not used by
        Emacs.  */
-    canvas.restore ();
     drawable.damageRect (rect);
   }
 }
diff --git a/java/org/gnu/emacs/EmacsDrawRectangle.java 
b/java/org/gnu/emacs/EmacsDrawRectangle.java
index c29d413f66..695a8c6ea4 100644
--- a/java/org/gnu/emacs/EmacsDrawRectangle.java
+++ b/java/org/gnu/emacs/EmacsDrawRectangle.java
@@ -23,6 +23,7 @@ import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Paint;
 import android.graphics.Rect;
+import android.graphics.RectF;
 
 import android.util.Log;
 
@@ -36,51 +37,31 @@ public class EmacsDrawRectangle
     Paint maskPaint, paint;
     Canvas maskCanvas;
     Bitmap maskBitmap;
+    Rect rect;
     Rect maskRect, dstRect;
     Canvas canvas;
     Bitmap clipBitmap;
-    Rect clipRect;
 
     /* TODO implement stippling.  */
     if (gc.fill_style == EmacsGC.GC_FILL_OPAQUE_STIPPLED)
       return;
 
-    canvas = drawable.lockCanvas ();
+    canvas = drawable.lockCanvas (gc);
 
     if (canvas == null)
       return;
 
-    canvas.save ();
-
-    if (gc.real_clip_rects != null)
-      {
-       for (i = 0; i < gc.real_clip_rects.length; ++i)
-         canvas.clipRect (gc.real_clip_rects[i]);
-      }
-
-    /* Clip to the clipRect because some versions of Android draw an
-       overly wide line.  */
-    clipRect = new Rect (x, y, x + width + 1,
-                        y + height + 1);
-    canvas.clipRect (clipRect);
-
     paint = gc.gcPaint;
+    paint.setStyle (Paint.Style.STROKE);
+    rect = new Rect (x, y, x + width, y + height);
 
     if (gc.clip_mask == null)
-      {
-       /* canvas.drawRect just doesn't work on Android, producing
-          different results on various devices.  Do a 5 point
-          PolyLine instead.  */
-       canvas.drawLine ((float) x, (float) y, (float) x + width,
-                        (float) y, paint);
-       canvas.drawLine ((float) x + width, (float) y,
-                        (float) x + width, (float) y + height,
-                        paint);
-       canvas.drawLine ((float) x + width, (float) y + height,
-                        (float) x, (float) y + height, paint);
-       canvas.drawLine ((float) x, (float) y + height,
-                        (float) x, (float) y, paint);
-      }
+      /* Use canvas.drawRect with a RectF.  That seems to reliably
+        get PostScript behavior.  */
+      canvas.drawRect (new RectF (x + 0.5f, y + 0.5f,
+                                 x + width + 0.5f,
+                                 y + height + 0.5f),
+                      paint);
     else
       {
        /* Drawing with a clip mask involves calculating the
@@ -137,7 +118,7 @@ public class EmacsDrawRectangle
        maskBitmap.recycle ();
       }
 
-    canvas.restore ();
-    drawable.damageRect (clipRect);
+    drawable.damageRect (new Rect (x, y, x + width + 1,
+                                  y + height + 1));
   }
 }
diff --git a/java/org/gnu/emacs/EmacsDrawable.java 
b/java/org/gnu/emacs/EmacsDrawable.java
index 6a6199ff21..f2f8885e97 100644
--- a/java/org/gnu/emacs/EmacsDrawable.java
+++ b/java/org/gnu/emacs/EmacsDrawable.java
@@ -25,7 +25,7 @@ import android.graphics.Canvas;
 
 public interface EmacsDrawable
 {
-  public Canvas lockCanvas ();
+  public Canvas lockCanvas (EmacsGC gc);
   public void damageRect (Rect damageRect);
   public Bitmap getBitmap ();
   public boolean isDestroyed ();
diff --git a/java/org/gnu/emacs/EmacsFillPolygon.java 
b/java/org/gnu/emacs/EmacsFillPolygon.java
index 42b73886df..22e2dd0d8a 100644
--- a/java/org/gnu/emacs/EmacsFillPolygon.java
+++ b/java/org/gnu/emacs/EmacsFillPolygon.java
@@ -41,21 +41,13 @@ public class EmacsFillPolygon
     RectF rectF;
     int i;
 
-    canvas = drawable.lockCanvas ();
+    canvas = drawable.lockCanvas (gc);
 
     if (canvas == null)
       return;
 
     paint = gc.gcPaint;
 
-    canvas.save ();
-
-    if (gc.real_clip_rects != null)
-      {
-       for (i = 0; i < gc.real_clip_rects.length; ++i)
-         canvas.clipRect (gc.real_clip_rects[i]);
-      }
-
     /* Build the path from the given array of points.  */
     path = new Path ();
 
@@ -83,7 +75,6 @@ public class EmacsFillPolygon
     if (gc.clip_mask == null)
       canvas.drawPath (path, paint);
 
-    canvas.restore ();
     drawable.damageRect (rect);
 
     /* FillPolygon with clip mask not implemented; it is not used by
diff --git a/java/org/gnu/emacs/EmacsFillRectangle.java 
b/java/org/gnu/emacs/EmacsFillRectangle.java
index 7cc55d3db9..aed0a540c8 100644
--- a/java/org/gnu/emacs/EmacsFillRectangle.java
+++ b/java/org/gnu/emacs/EmacsFillRectangle.java
@@ -32,7 +32,6 @@ public class EmacsFillRectangle
   perform (EmacsDrawable drawable, EmacsGC gc,
           int x, int y, int width, int height)
   {
-    int i;
     Paint maskPaint, paint;
     Canvas maskCanvas;
     Bitmap maskBitmap;
@@ -45,19 +44,11 @@ public class EmacsFillRectangle
     if (gc.fill_style == EmacsGC.GC_FILL_OPAQUE_STIPPLED)
       return;
 
-    canvas = drawable.lockCanvas ();
+    canvas = drawable.lockCanvas (gc);
 
     if (canvas == null)
       return;
 
-    canvas.save ();
-
-    if (gc.real_clip_rects != null)
-      {
-       for (i = 0; i < gc.real_clip_rects.length; ++i)
-         canvas.clipRect (gc.real_clip_rects[i]);
-      }
-
     paint = gc.gcPaint;
     rect = new Rect (x, y, x + width, y + height);
 
@@ -120,7 +111,6 @@ public class EmacsFillRectangle
        maskBitmap.recycle ();
       }
 
-    canvas.restore ();
     drawable.damageRect (rect);
   }
 }
diff --git a/java/org/gnu/emacs/EmacsGC.java b/java/org/gnu/emacs/EmacsGC.java
index c579625f3f..bdc27a1ca5 100644
--- a/java/org/gnu/emacs/EmacsGC.java
+++ b/java/org/gnu/emacs/EmacsGC.java
@@ -47,6 +47,14 @@ public class EmacsGC extends EmacsHandleObject
   public EmacsPixmap clip_mask, stipple;
   public Paint gcPaint;
 
+  /* ID incremented every time the clipping rectangles of any GC
+     changes.  */
+  private static long clip_serial;
+
+  /* The value of clipRectID after the last time this GCs clip
+     rectangles changed.  0 if there are no clip rectangles.  */
+  public long clipRectID;
+
   static
   {
     xorAlu = new PorterDuffXfermode (Mode.XOR);
@@ -75,23 +83,28 @@ public class EmacsGC extends EmacsHandleObject
      recompute real_clip_rects.  */
 
   public void
-  markDirty ()
+  markDirty (boolean clipRectsChanged)
   {
     int i;
 
-    if ((ts_origin_x != 0 || ts_origin_y != 0)
-       && clip_rects != null)
+    if (clipRectsChanged)
       {
-       real_clip_rects = new Rect[clip_rects.length];
-
-       for (i = 0; i < clip_rects.length; ++i)
+       if ((ts_origin_x != 0 || ts_origin_y != 0)
+           && clip_rects != null)
          {
-           real_clip_rects[i] = new Rect (clip_rects[i]);
-           real_clip_rects[i].offset (ts_origin_x, ts_origin_y);
+           real_clip_rects = new Rect[clip_rects.length];
+
+           for (i = 0; i < clip_rects.length; ++i)
+             {
+               real_clip_rects[i] = new Rect (clip_rects[i]);
+               real_clip_rects[i].offset (ts_origin_x, ts_origin_y);
+             }
          }
+       else
+         real_clip_rects = clip_rects;
+
+       clipRectID = ++clip_serial;
       }
-    else
-      real_clip_rects = clip_rects;
 
     gcPaint.setStrokeWidth (1f);
     gcPaint.setColor (foreground | 0xff000000);
diff --git a/java/org/gnu/emacs/EmacsPixmap.java 
b/java/org/gnu/emacs/EmacsPixmap.java
index 85931c2abd..a83d8f2554 100644
--- a/java/org/gnu/emacs/EmacsPixmap.java
+++ b/java/org/gnu/emacs/EmacsPixmap.java
@@ -42,6 +42,14 @@ public class EmacsPixmap extends EmacsHandleObject
   /* The canvas used to draw to BITMAP.  */
   public Canvas canvas;
 
+  /* Whether or not GC should be explicitly triggered upon
+     release.  */
+  private boolean needCollect;
+
+  /* ID used to determine whether or not the GC clip rects
+     changed.  */
+  private long gcClipRectID;
+
   public
   EmacsPixmap (short handle, int colors[], int width,
               int height, int depth)
@@ -83,18 +91,41 @@ public class EmacsPixmap extends EmacsHandleObject
     switch (depth)
       {
       case 1:
-       bitmap = Bitmap.createBitmap (width, height,
-                                     Bitmap.Config.ALPHA_8,
-                                     false);
+       if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
+         bitmap = Bitmap.createBitmap (width, height,
+                                       Bitmap.Config.ALPHA_8,
+                                       false);
+       else
+         bitmap = Bitmap.createBitmap (width, height,
+                                       Bitmap.Config.ALPHA_8);
        break;
 
       case 24:
-       bitmap = Bitmap.createBitmap (width, height,
-                                     Bitmap.Config.ARGB_8888,
-                                     false);
+
+       /* Emacs doesn't just use the first kind of `createBitmap'
+          because the latter allows specifying that the pixmap is
+          always opaque, which really increases efficiency.  */
+       if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O)
+         bitmap = Bitmap.createBitmap (width, height,
+                                       Bitmap.Config.ARGB_8888);
+       else
+         bitmap = Bitmap.createBitmap (width, height,
+                                       Bitmap.Config.ARGB_8888,
+                                       false);
        break;
       }
 
+    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB_MR1)
+      /* On these old versions of Android, Bitmap.recycle frees bitmap
+        contents immediately.  */
+      needCollect = false;
+    else if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT)
+      needCollect = (bitmap.getByteCount ()
+                    >= 1024 * 512);
+    else
+      needCollect = (bitmap.getAllocationByteCount ()
+                    >= 1024 * 512);
+
     bitmap.eraseColor (0xff000000);
 
     this.width = width;
@@ -104,11 +135,32 @@ public class EmacsPixmap extends EmacsHandleObject
 
   @Override
   public Canvas
-  lockCanvas ()
+  lockCanvas (EmacsGC gc)
   {
+    int i;
+
     if (canvas == null)
-      canvas = new Canvas (bitmap);
+      {
+       canvas = new Canvas (bitmap);
+       canvas.save ();
+      }
 
+    /* Now see if clipping has to be redone.  */
+    if (gc.clipRectID == gcClipRectID)
+      return canvas;
+
+    /* It does have to be redone.  Reapply gc.real_clip_rects.  */
+    canvas.restore ();
+    canvas.save ();
+
+    if (gc.real_clip_rects != null)
+      {
+       for (i = 0; i < gc.real_clip_rects.length; ++i)
+         canvas.clipRect (gc.real_clip_rects[i]);
+      }
+
+    /* Save the clip rect ID again.  */
+    gcClipRectID = gc.clipRectID;
     return canvas;
   }
 
@@ -130,15 +182,6 @@ public class EmacsPixmap extends EmacsHandleObject
   public void
   destroyHandle ()
   {
-    boolean needCollect;
-
-    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT)
-      needCollect = (bitmap.getByteCount ()
-                    >= 1024 * 512);
-    else
-      needCollect = (bitmap.getAllocationByteCount ()
-                    >= 1024 * 512);
-
     bitmap.recycle ();
     bitmap = null;
 
diff --git a/java/org/gnu/emacs/EmacsSdk7FontDriver.java 
b/java/org/gnu/emacs/EmacsSdk7FontDriver.java
index c0f24c7433..a964cadb74 100644
--- a/java/org/gnu/emacs/EmacsSdk7FontDriver.java
+++ b/java/org/gnu/emacs/EmacsSdk7FontDriver.java
@@ -510,20 +510,12 @@ public class EmacsSdk7FontDriver extends EmacsFontDriver
     backgroundRect.right = x + backgroundWidth;
     backgroundRect.bottom = y + sdk7FontObject.descent;
 
-    canvas = drawable.lockCanvas ();
+    canvas = drawable.lockCanvas (gc);
 
     if (canvas == null)
       return 0;
 
-    canvas.save ();
     paint = gc.gcPaint;
-
-    if (gc.real_clip_rects != null)
-      {
-       for (i = 0; i < gc.real_clip_rects.length; ++i)
-         canvas.clipRect (gc.real_clip_rects[i]);
-      }
-
     paint.setStyle (Paint.Style.FILL);
 
     if (withBackground)
@@ -538,7 +530,6 @@ public class EmacsSdk7FontDriver extends EmacsFontDriver
     paint.setAntiAlias (true);
     canvas.drawText (charsArray, 0, chars.length, x, y, paint);
 
-    canvas.restore ();
     bounds = new Rect ();
     paint.getTextBounds (charsArray, 0, chars.length, bounds);
     bounds.offset (x, y);
diff --git a/java/org/gnu/emacs/EmacsService.java 
b/java/org/gnu/emacs/EmacsService.java
index ca38f93dc9..bcf8d9ff6e 100644
--- a/java/org/gnu/emacs/EmacsService.java
+++ b/java/org/gnu/emacs/EmacsService.java
@@ -175,7 +175,12 @@ public class EmacsService extends Service
            {
              view.thing = new EmacsView (window);
              view.thing.setVisibility (visibility);
-             view.thing.setFocusedByDefault (isFocusedByDefault);
+
+             /* The following function is only present on Android 26
+                or later.  */
+             if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
+               view.thing.setFocusedByDefault (isFocusedByDefault);
+
              notify ();
            }
        }
diff --git a/java/org/gnu/emacs/EmacsView.java 
b/java/org/gnu/emacs/EmacsView.java
index 6137fd74a7..82f44acaeb 100644
--- a/java/org/gnu/emacs/EmacsView.java
+++ b/java/org/gnu/emacs/EmacsView.java
@@ -83,6 +83,9 @@ public class EmacsView extends ViewGroup
   /* The last measured width and height.  */
   private int measuredWidth, measuredHeight;
 
+  /* The serial of the last clip rectangle change.  */
+  private long lastClipSerial;
+
   public
   EmacsView (EmacsWindow window)
   {
@@ -105,10 +108,6 @@ public class EmacsView extends ViewGroup
        on Android? */
     setChildrenDrawingOrderEnabled (true);
 
-    /* Get rid of the foreground and background tint.  */
-    setBackgroundTintList (null);
-    setForegroundTintList (null);
-
     /* Get rid of the default focus highlight.  */
     if (Build.VERSION.SDK_INT > Build.VERSION_CODES.O)
       setDefaultFocusHighlightEnabled (false);
@@ -145,6 +144,11 @@ public class EmacsView extends ViewGroup
 
     /* And canvases.  */
     canvas = new Canvas (bitmap);
+    canvas.save ();
+
+    /* Since the clip rectangles have been cleared, clear the clip
+       rectangle ID.  */
+    lastClipSerial = 0;
 
     /* Copy over the contents of the old bitmap.  */
     if (oldBitmap != null)
@@ -177,11 +181,31 @@ public class EmacsView extends ViewGroup
   }
 
   public synchronized Canvas
-  getCanvas ()
+  getCanvas (EmacsGC gc)
   {
+    int i;
+
     if (bitmapDirty || bitmap == null)
       handleDirtyBitmap ();
 
+    if (canvas == null)
+      return null;
+
+    /* Update clip rectangles if necessary.  */
+    if (gc.clipRectID != lastClipSerial)
+      {
+       canvas.restore ();
+       canvas.save ();
+
+       if (gc.real_clip_rects != null)
+         {
+           for (i = 0; i < gc.real_clip_rects.length; ++i)
+             canvas.clipRect (gc.real_clip_rects[i]);
+         }
+
+       lastClipSerial = gc.clipRectID;
+      }
+
     return canvas;
   }
 
diff --git a/java/org/gnu/emacs/EmacsWindow.java 
b/java/org/gnu/emacs/EmacsWindow.java
index f5b50f11f1..c5b1522086 100644
--- a/java/org/gnu/emacs/EmacsWindow.java
+++ b/java/org/gnu/emacs/EmacsWindow.java
@@ -164,7 +164,7 @@ public class EmacsWindow extends EmacsHandleObject
   {
     /* scratchGC is used as the argument to a FillRectangles req.  */
     scratchGC.foreground = pixel;
-    scratchGC.markDirty ();
+    scratchGC.markDirty (false);
   }
 
   public Rect
@@ -466,9 +466,9 @@ public class EmacsWindow extends EmacsHandleObject
 
   @Override
   public Canvas
-  lockCanvas ()
+  lockCanvas (EmacsGC gc)
   {
-    return view.getCanvas ();
+    return view.getCanvas (gc);
   }
 
   @Override
@@ -512,37 +512,75 @@ public class EmacsWindow extends EmacsHandleObject
   public void
   onKeyDown (int keyCode, KeyEvent event)
   {
-    int state;
+    int state, state_1;
 
-    state = event.getModifiers ();
-    state &= ~(KeyEvent.META_ALT_MASK | KeyEvent.META_CTRL_MASK);
+    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2)
+      state = event.getModifiers ();
+    else
+      {
+       /* Replace this with getMetaState and manual
+          normalization.  */
+       state = event.getMetaState ();
+
+       /* Normalize the state by setting the generic modifier bit if
+          either a left or right modifier is pressed.  */
+
+       if ((state & KeyEvent.META_ALT_LEFT_ON) != 0
+           || (state & KeyEvent.META_ALT_RIGHT_ON) != 0)
+         state |= KeyEvent.META_ALT_MASK;
+
+       if ((state & KeyEvent.META_CTRL_LEFT_ON) != 0
+           || (state & KeyEvent.META_CTRL_RIGHT_ON) != 0)
+         state |= KeyEvent.META_CTRL_MASK;
+      }
+
+    /* Ignore meta-state understood by Emacs for now, or Ctrl+C will
+       not be recognized as an ASCII key press event.  */
+    state_1
+      = state & ~(KeyEvent.META_ALT_MASK | KeyEvent.META_CTRL_MASK);
 
     EmacsNative.sendKeyPress (this.handle,
                              event.getEventTime (),
-                             event.getModifiers (),
-                             keyCode,
-                             /* Ignore meta-state understood by Emacs
-                                for now, or Ctrl+C will not be
-                                recognized as an ASCII key press
-                                event.  */
-                             event.getUnicodeChar (state));
-    lastModifiers = event.getModifiers ();
+                             state, keyCode,
+                             event.getUnicodeChar (state_1));
+    lastModifiers = state;
   }
 
   public void
   onKeyUp (int keyCode, KeyEvent event)
   {
-    int state;
+    int state, state_1;
+
+    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2)
+      state = event.getModifiers ();
+    else
+      {
+       /* Replace this with getMetaState and manual
+          normalization.  */
+       state = event.getMetaState ();
+
+       /* Normalize the state by setting the generic modifier bit if
+          either a left or right modifier is pressed.  */
+
+       if ((state & KeyEvent.META_ALT_LEFT_ON) != 0
+           || (state & KeyEvent.META_ALT_RIGHT_ON) != 0)
+         state |= KeyEvent.META_ALT_MASK;
+
+       if ((state & KeyEvent.META_CTRL_LEFT_ON) != 0
+           || (state & KeyEvent.META_CTRL_RIGHT_ON) != 0)
+         state |= KeyEvent.META_CTRL_MASK;
+      }
 
-    state = event.getModifiers ();
-    state &= ~(KeyEvent.META_ALT_MASK | KeyEvent.META_CTRL_MASK);
+    /* Ignore meta-state understood by Emacs for now, or Ctrl+C will
+       not be recognized as an ASCII key press event.  */
+    state_1
+      = state & ~(KeyEvent.META_ALT_MASK | KeyEvent.META_CTRL_MASK);
 
     EmacsNative.sendKeyRelease (this.handle,
                                event.getEventTime (),
-                               event.getModifiers (),
-                               keyCode,
-                               event.getUnicodeChar (state));
-    lastModifiers = event.getModifiers ();
+                               state, keyCode,
+                               event.getUnicodeChar (state_1));
+    lastModifiers = state;
   }
 
   public void
diff --git a/lib-src/Makefile.in b/lib-src/Makefile.in
index 7d49155752..ddf141e277 100644
--- a/lib-src/Makefile.in
+++ b/lib-src/Makefile.in
@@ -148,6 +148,9 @@ HAVE_BE_APP=@HAVE_BE_APP@
 HAIKU_LIBS=@HAIKU_LIBS@
 HAIKU_CFLAGS=@HAIKU_CFLAGS@
 
+## Android build-time support
+ANDROID=@ANDROID@
+
 # emacsclientw.exe for MinGW, empty otherwise
 CLIENTW = @CLIENTW@
 
@@ -164,8 +167,13 @@ UTILITIES = hexl${EXEEXT}                           \
 ifeq ($(HAVE_BE_APP),yes)
 DONT_INSTALL= make-docfile${EXEEXT} make-fingerprint${EXEEXT} be-resources
 else
+ifeq ($(HAVE_ANDROID),yes)
+DONT_INSTALL = make-docfile${EXEEXT} make-fingerprint${EXEEXT} \
+       asset-directory-tool
+else
 DONT_INSTALL= make-docfile${EXEEXT} make-fingerprint${EXEEXT}
 endif
+endif
 
 # Like UTILITIES, but they're not system-dependent, and should not be
 #  deleted by the distclean target.
@@ -414,6 +422,9 @@ etags${EXEEXT}: ${etags_deps}
 ctags${EXEEXT}: ${srcdir}/ctags.c ${etags_deps}
        $(AM_V_CCLD)$(CC) ${ALL_CFLAGS} -o $@ $< $(etags_libs)
 
+asset-directory-tool${EXEEXT}: ${srcdir}/asset-directory-tool.c $(config_h)
+       $(AM_V_CCLD)$(CC) ${ALL_CFLAGS} $< $(LOADLIBES) -o $@
+
 ebrowse${EXEEXT}: ${srcdir}/ebrowse.c ${srcdir}/../lib/min-max.h $(NTLIB) \
                    $(config_h)
        $(AM_V_CCLD)$(CC) ${ALL_CFLAGS} -o $@ $< $(NTLIB) $(LOADLIBES)
diff --git a/lib-src/asset-directory-tool.c b/lib-src/asset-directory-tool.c
new file mode 100644
index 0000000000..e53398eceb
--- /dev/null
+++ b/lib-src/asset-directory-tool.c
@@ -0,0 +1,280 @@
+/* Android asset directory tool.
+
+Copyright (C) 2023 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/>.  */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <endian.h>
+#include <stdlib.h>
+#include <dirent.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <sys/stat.h>
+
+/* This program takes a directory as input, and generates a
+   ``directory-tree'' file suitable for inclusion in an Android
+   application package.
+
+   Such a file records the layout of the `assets' directory in the
+   package.  Emacs records this information itself and uses it in the
+   Android emulation of readdir, because the system asset manager APIs
+   are routinely buggy, and are often unable to locate directories or
+   files.
+
+   The file is packed, with no data alignment guarantees made.  The
+   file starts with the bytes "EMACS", following which is the name of
+   the first file or directory, a NULL byte and an unsigned int
+   indicating the offset from the start of the file to the start of
+   the next sibling.  Following that is a list of subdirectories or
+   files in the same format.  The long is stored LSB first.  */
+
+
+
+struct directory_tree
+{
+  /* The offset to the next sibling.  */
+  size_t offset;
+
+  /* The name of this directory or file.  */
+  char *name;
+
+  /* Subdirectories and files inside this directory.  */
+  struct directory_tree *children, *next;
+};
+
+/* Exit with EXIT_FAILURE, after printing a description of a failing
+   function WHAT along with the details of the error.  */
+
+static _Noreturn void
+croak (const char *what)
+{
+  perror (what);
+  exit (EXIT_FAILURE);
+}
+
+/* Like malloc, but aborts on failure.  */
+
+static void *
+xmalloc (size_t size)
+{
+  void *ptr;
+
+  ptr = malloc (size);
+
+  if (!ptr)
+    croak ("malloc");
+
+  return ptr;
+}
+
+/* Recursively build a struct directory_tree structure for each
+   subdirectory or file in DIR, in preparation for writing it out to
+   disk.  PARENT should be the directory tree associated with the
+   parent directory, or else PARENT->offset must be initialized to
+   5.  */
+
+static void
+main_1 (DIR *dir, struct directory_tree *parent)
+{
+  struct dirent *dirent;
+  int dir_fd, fd;
+  struct stat statb;
+  struct directory_tree *this, **last;
+  size_t length;
+  DIR *otherdir;
+
+  dir_fd = dirfd (dir);
+  last = &parent->children;
+
+  while ((dirent = readdir (dir)))
+    {
+      /* Determine what kind of file DIRENT is.  */
+
+      if (fstatat (dir_fd, dirent->d_name, &statb,
+                  AT_SYMLINK_NOFOLLOW) == -1)
+       croak ("fstatat");
+
+      /* Ignore . and ...  */
+
+      if (!strcmp (dirent->d_name, ".")
+         || !strcmp (dirent->d_name, ".."))
+       continue;
+
+      length = strlen (dirent->d_name);
+
+      if (statb.st_mode & S_IFDIR)
+       {
+         /* This is a directory.  Write its name followed by a
+            trailing slash, then a NULL byte, and the offset to the
+            next sibling.  */
+         this = xmalloc (sizeof *this);
+         this->children = NULL;
+         this->next = NULL;
+         *last = this;
+         last = &this->next;
+         this->name = xmalloc (length + 2);
+         strcpy (this->name, dirent->d_name);
+
+         /* Now record the offset to the end of this directory.  This
+            is length + 1, for the file name, and 5 more bytes for
+            the trailing NULL and long.  */
+         this->offset = parent->offset + length + 6;
+
+         /* Terminate that with a slash and trailing NULL byte.  */
+         this->name[length] = '/';
+         this->name[length + 1] = '\0';
+
+         /* Open and build that directory recursively.  */
+
+         fd = openat (dir_fd, dirent->d_name, O_DIRECTORY,
+                      O_RDONLY);
+         if (fd < 0)
+           croak ("openat");
+         otherdir = fdopendir (fd);
+         if (!otherdir)
+           croak ("fdopendir");
+
+         main_1 (otherdir, this);
+
+         /* Close this directory.  */
+         closedir (otherdir);
+
+         /* Finally, set parent->offset to this->offset as well.  */
+         parent->offset = this->offset;
+       }
+      else if (statb.st_mode & S_IFREG)
+       {
+         /* This is a regular file.  */
+         this = xmalloc (sizeof *this);
+         this->children = NULL;
+         this->next = NULL;
+         *last = this;
+         last = &this->next;
+         this->name = xmalloc (length + 1);
+         strcpy (this->name, dirent->d_name);
+
+         /* This is one byte shorter because there is no trailing
+            slash.  */
+         this->offset = parent->offset + length + 5;
+         parent->offset = this->offset;
+       }
+    }
+}
+
+/* Write the struct directory_tree TREE and all of is children to the
+   file descriptor FD.  OFFSET is the offset of TREE and may be
+   modified; it is only used for checking purposes.  */
+
+static void
+main_2 (int fd, struct directory_tree *tree, size_t *offset)
+{
+  ssize_t size;
+  struct directory_tree *child;
+  unsigned int output;
+
+  /* Write tree->name with the trailing NULL byte.  */
+  size = strlen (tree->name) + 1;
+  if (write (fd, tree->name, size) < size)
+    croak ("write");
+
+  /* Write the offset.  */
+  output = htole32 (tree->offset);
+  if (write (fd, &output, 4) < 1)
+    croak ("write");
+  size += 4;
+
+  /* Now update offset.  */
+  *offset += size;
+
+  /* Write out each child.  */
+  for (child = tree->children; child; child = child->next)
+    main_2 (fd, child, offset);
+
+  /* Verify the offset is correct.  */
+  if (tree->offset != *offset)
+    {
+      fprintf (stderr,
+              "asset-directory-tool: invalid offset: expected %tu, "
+              "got %tu.\n"
+              "Please report this bug to bug-gnu-emacs@gnu.org, along\n"
+              "with an archive containing the contents of the java/inst"
+              "all_temp directory.\n",
+              tree->offset, *offset);
+      abort ();
+    }
+}
+
+int
+main (int argc, char **argv)
+{
+  int fd;
+  DIR *indir;
+  struct directory_tree tree;
+  size_t offset;
+
+  if (argc != 3)
+    {
+      fprintf (stderr, "usage: %s directory output-file\n",
+              argv[0]);
+      return EXIT_FAILURE;
+    }
+
+  fd = open (argv[2], O_CREAT | O_TRUNC | O_RDWR,
+            S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
+
+  if (fd < 0)
+    {
+      perror ("open");
+      return EXIT_FAILURE;
+    }
+
+  indir = opendir (argv[1]);
+
+  if (!indir)
+    {
+      perror ("opendir");
+      return EXIT_FAILURE;
+    }
+
+  /* Write the first 5 byte header to FD.  */
+
+  if (write (fd, "EMACS", 5) < 5)
+    {
+      perror ("write");
+      return EXIT_FAILURE;
+    }
+
+  /* Now iterate through children of INDIR, building the directory
+     tree.  */
+  tree.offset = 5;
+  tree.children = NULL;
+
+  main_1 (indir, &tree);
+  closedir (indir);
+
+  /* Finally, write the directory tree to the output file.  */
+  offset = 5;
+  for (; tree.children; tree.children = tree.children->next)
+    main_2 (fd, tree.children, &offset);
+
+  return 0;
+}
diff --git a/lib/faccessat.c b/lib/faccessat.c
index 807e266968..ac8977cfd6 100644
--- a/lib/faccessat.c
+++ b/lib/faccessat.c
@@ -43,11 +43,7 @@ orig_faccessat (int fd, char const *name, int mode, int flag)
 /* Write "unistd.h" here, not <unistd.h>, otherwise OSF/1 5.1 DTK cc
    eliminates this include because of the preliminary #include <unistd.h>
    above.  */
-#ifdef __ANROID__
-#include <unistd.h>
-#else
 #include "unistd.h"
-#endif
 
 #ifndef HAVE_ACCESS
 /* Mingw lacks access, but it also lacks real vs. effective ids, so
diff --git a/lib/fpending.c b/lib/fpending.c
index afa840b851..e57155e586 100644
--- a/lib/fpending.c
+++ b/lib/fpending.c
@@ -41,7 +41,7 @@ __fpending (FILE *fp)
   return fp->_IO_write_ptr - fp->_IO_write_base;
 #elif defined __sferror || defined __DragonFly__ || defined __ANDROID__
   /* FreeBSD, NetBSD, OpenBSD, DragonFly, Mac OS X, Cygwin < 1.7.34, Minix 3, 
Android */
-  return fp->_p - fp->_bf._base;
+  return fp_->_p - fp_->_bf._base;
 #elif defined __EMX__                /* emx+gcc */
   return fp->_ptr - fp->_buffer;
 #elif defined __minix                /* Minix */
diff --git a/lib/getdelim.c b/lib/getdelim.c
new file mode 100644
index 0000000000..79ec3dd12a
--- /dev/null
+++ b/lib/getdelim.c
@@ -0,0 +1,147 @@
+/* getdelim.c --- Implementation of replacement getdelim function.
+   Copyright (C) 1994, 1996-1998, 2001, 2003, 2005-2023 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/>.  */
+
+/* Ported from glibc by Simon Josefsson. */
+
+/* Don't use __attribute__ __nonnull__ in this compilation unit.  Otherwise gcc
+   optimizes away the lineptr == NULL || n == NULL || fp == NULL tests below.  
*/
+#define _GL_ARG_NONNULL(params)
+
+#include <config.h>
+
+#include <stdio.h>
+
+#include <limits.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#ifndef SSIZE_MAX
+# define SSIZE_MAX ((ssize_t) (SIZE_MAX / 2))
+#endif
+
+#if USE_UNLOCKED_IO
+# include "unlocked-io.h"
+# define getc_maybe_unlocked(fp)        getc(fp)
+#elif !HAVE_FLOCKFILE || !HAVE_FUNLOCKFILE || !HAVE_DECL_GETC_UNLOCKED
+# undef flockfile
+# undef funlockfile
+# define flockfile(x) ((void) 0)
+# define funlockfile(x) ((void) 0)
+# define getc_maybe_unlocked(fp)        getc(fp)
+#else
+# define getc_maybe_unlocked(fp)        getc_unlocked(fp)
+#endif
+
+static void
+alloc_failed (void)
+{
+#if defined _WIN32 && ! defined __CYGWIN__
+  /* Avoid errno problem without using the realloc module; see:
+     https://lists.gnu.org/r/bug-gnulib/2016-08/msg00025.html  */
+  errno = ENOMEM;
+#endif
+}
+
+/* Read up to (and including) a DELIMITER from FP into *LINEPTR (and
+   NUL-terminate it).  *LINEPTR is a pointer returned from malloc (or
+   NULL), pointing to *N characters of space.  It is realloc'ed as
+   necessary.  Returns the number of characters read (not including
+   the null terminator), or -1 on error or EOF.  */
+
+ssize_t
+getdelim (char **lineptr, size_t *n, int delimiter, FILE *fp)
+{
+  ssize_t result;
+  size_t cur_len = 0;
+
+  if (lineptr == NULL || n == NULL || fp == NULL)
+    {
+      errno = EINVAL;
+      return -1;
+    }
+
+  flockfile (fp);
+
+  if (*lineptr == NULL || *n == 0)
+    {
+      char *new_lineptr;
+      *n = 120;
+      new_lineptr = (char *) realloc (*lineptr, *n);
+      if (new_lineptr == NULL)
+        {
+          alloc_failed ();
+          result = -1;
+          goto unlock_return;
+        }
+      *lineptr = new_lineptr;
+    }
+
+  for (;;)
+    {
+      int i;
+
+      i = getc_maybe_unlocked (fp);
+      if (i == EOF)
+        {
+          result = -1;
+          break;
+        }
+
+      /* Make enough space for len+1 (for final NUL) bytes.  */
+      if (cur_len + 1 >= *n)
+        {
+          size_t needed_max =
+            SSIZE_MAX < SIZE_MAX ? (size_t) SSIZE_MAX + 1 : SIZE_MAX;
+          size_t needed = 2 * *n + 1;   /* Be generous. */
+          char *new_lineptr;
+
+          if (needed_max < needed)
+            needed = needed_max;
+          if (cur_len + 1 >= needed)
+            {
+              result = -1;
+              errno = EOVERFLOW;
+              goto unlock_return;
+            }
+
+          new_lineptr = (char *) realloc (*lineptr, needed);
+          if (new_lineptr == NULL)
+            {
+              alloc_failed ();
+              result = -1;
+              goto unlock_return;
+            }
+
+          *lineptr = new_lineptr;
+          *n = needed;
+        }
+
+      (*lineptr)[cur_len] = i;
+      cur_len++;
+
+      if (i == delimiter)
+        break;
+    }
+  (*lineptr)[cur_len] = '\0';
+  result = cur_len ? cur_len : result;
+
+ unlock_return:
+  funlockfile (fp); /* doesn't set errno */
+
+  return result;
+}
diff --git a/lib/getline.c b/lib/getline.c
new file mode 100644
index 0000000000..85f16ab8ba
--- /dev/null
+++ b/lib/getline.c
@@ -0,0 +1,27 @@
+/* getline.c --- Implementation of replacement getline function.
+   Copyright (C) 2005-2007, 2009-2023 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/>.  */
+
+/* Written by Simon Josefsson. */
+
+#include <config.h>
+
+#include <stdio.h>
+
+ssize_t
+getline (char **lineptr, size_t *n, FILE *stream)
+{
+  return getdelim (lineptr, n, '\n', stream);
+}
diff --git a/lib/gnulib.mk.in b/lib/gnulib.mk.in
index 2097850c81..66ba4a4adf 100644
--- a/lib/gnulib.mk.in
+++ b/lib/gnulib.mk.in
@@ -109,6 +109,7 @@
 #  fsusage \
 #  fsync \
 #  futimens \
+#  getline \
 #  getloadavg \
 #  getopt-gnu \
 #  getrandom \
@@ -172,11 +173,19 @@
 
 MOSTLYCLEANFILES += core *.stackdump
 # Start of GNU Make output.
+AAPT = @AAPT@
 ALLOCA = @ALLOCA@
 ALLOCA_H = @ALLOCA_H@
 ALSA_CFLAGS = @ALSA_CFLAGS@
 ALSA_LIBS = @ALSA_LIBS@
 AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+ANDROID = @ANDROID@
+ANDROID_ABI = @ANDROID_ABI@
+ANDROID_CFLAGS = @ANDROID_CFLAGS@
+ANDROID_JAR = @ANDROID_JAR@
+ANDROID_LIBS = @ANDROID_LIBS@
+ANDROID_MIN_SDK = @ANDROID_MIN_SDK@
+ANDROID_OBJ = @ANDROID_OBJ@
 APPLE_UNIVERSAL_BUILD = @APPLE_UNIVERSAL_BUILD@
 AR = @AR@
 ARFLAGS = @ARFLAGS@
@@ -216,6 +225,7 @@ CYGWIN_OBJ = @CYGWIN_OBJ@
 C_SWITCH_MACHINE = @C_SWITCH_MACHINE@
 C_SWITCH_SYSTEM = @C_SWITCH_SYSTEM@
 C_SWITCH_X_SITE = @C_SWITCH_X_SITE@
+D8 = @D8@
 DBUS_CFLAGS = @DBUS_CFLAGS@
 DBUS_LIBS = @DBUS_LIBS@
 DBUS_OBJ = @DBUS_OBJ@
@@ -278,8 +288,10 @@ GL_COND_OBJ_FSTATAT_CONDITION = 
@GL_COND_OBJ_FSTATAT_CONDITION@
 GL_COND_OBJ_FSUSAGE_CONDITION = @GL_COND_OBJ_FSUSAGE_CONDITION@
 GL_COND_OBJ_FSYNC_CONDITION = @GL_COND_OBJ_FSYNC_CONDITION@
 GL_COND_OBJ_FUTIMENS_CONDITION = @GL_COND_OBJ_FUTIMENS_CONDITION@
+GL_COND_OBJ_GETDELIM_CONDITION = @GL_COND_OBJ_GETDELIM_CONDITION@
 GL_COND_OBJ_GETDTABLESIZE_CONDITION = @GL_COND_OBJ_GETDTABLESIZE_CONDITION@
 GL_COND_OBJ_GETGROUPS_CONDITION = @GL_COND_OBJ_GETGROUPS_CONDITION@
+GL_COND_OBJ_GETLINE_CONDITION = @GL_COND_OBJ_GETLINE_CONDITION@
 GL_COND_OBJ_GETLOADAVG_CONDITION = @GL_COND_OBJ_GETLOADAVG_CONDITION@
 GL_COND_OBJ_GETOPT_CONDITION = @GL_COND_OBJ_GETOPT_CONDITION@
 GL_COND_OBJ_GETRANDOM_CONDITION = @GL_COND_OBJ_GETRANDOM_CONDITION@
@@ -883,6 +895,8 @@ INSTALL_PROGRAM = @INSTALL_PROGRAM@
 INSTALL_SCRIPT = @INSTALL_SCRIPT@
 INT32_MAX_LT_INTMAX_MAX = @INT32_MAX_LT_INTMAX_MAX@
 INT64_MAX_EQ_LONG_MAX = @INT64_MAX_EQ_LONG_MAX@
+JARSIGNER = @JARSIGNER@
+JAVAC = @JAVAC@
 JSON_CFLAGS = @JSON_CFLAGS@
 JSON_LIBS = @JSON_LIBS@
 JSON_OBJ = @JSON_OBJ@
@@ -1211,6 +1225,7 @@ REPLACE_WCTOMB = @REPLACE_WCTOMB@
 REPLACE_WRITE = @REPLACE_WRITE@
 RSVG_CFLAGS = @RSVG_CFLAGS@
 RSVG_LIBS = @RSVG_LIBS@
+SDK_BULD_TOOLS = @SDK_BULD_TOOLS@
 SEPCHAR = @SEPCHAR@
 SETFATTR = @SETFATTR@
 SETTINGS_CFLAGS = @SETTINGS_CFLAGS@
@@ -1268,6 +1283,7 @@ XARGS_LIMIT = @XARGS_LIMIT@
 XCB_LIBS = @XCB_LIBS@
 XCOMPOSITE_CFLAGS = @XCOMPOSITE_CFLAGS@
 XCOMPOSITE_LIBS = @XCOMPOSITE_LIBS@
+XCONFIGURE = @XCONFIGURE@
 XCRUN = @XCRUN@
 XDBE_CFLAGS = @XDBE_CFLAGS@
 XDBE_LIBS = @XDBE_LIBS@
@@ -1292,6 +1308,7 @@ XSYNC_CFLAGS = @XSYNC_CFLAGS@
 XSYNC_LIBS = @XSYNC_LIBS@
 XWIDGETS_OBJ = @XWIDGETS_OBJ@
 X_TOOLKIT_TYPE = @X_TOOLKIT_TYPE@
+ZIPALIGN = @ZIPALIGN@
 ac_ct_CC = @ac_ct_CC@
 ac_ct_CXX = @ac_ct_CXX@
 ac_ct_OBJC = @ac_ct_OBJC@
@@ -1337,6 +1354,7 @@ 
gl_GNULIB_ENABLED_e80bf6f757095d2e5fc94dafb8f8fc8b_CONDITION = @gl_GNULIB_ENABLE
 gl_GNULIB_ENABLED_ef455225c00f5049c808c2eda3e76866_CONDITION = 
@gl_GNULIB_ENABLED_ef455225c00f5049c808c2eda3e76866_CONDITION@
 gl_GNULIB_ENABLED_euidaccess_CONDITION = 
@gl_GNULIB_ENABLED_euidaccess_CONDITION@
 gl_GNULIB_ENABLED_fd38c7e463b54744b77b98aeafb4fa7c_CONDITION = 
@gl_GNULIB_ENABLED_fd38c7e463b54744b77b98aeafb4fa7c_CONDITION@
+gl_GNULIB_ENABLED_getdelim_CONDITION = @gl_GNULIB_ENABLED_getdelim_CONDITION@
 gl_GNULIB_ENABLED_getdtablesize_CONDITION = 
@gl_GNULIB_ENABLED_getdtablesize_CONDITION@
 gl_GNULIB_ENABLED_getgroups_CONDITION = @gl_GNULIB_ENABLED_getgroups_CONDITION@
 gl_GNULIB_ENABLED_lchmod_CONDITION = @gl_GNULIB_ENABLED_lchmod_CONDITION@
@@ -2093,6 +2111,18 @@ gl_V_at = $(AM_V_GEN)
 endif
 ## end   gnulib module gen-header
 
+## begin gnulib module getdelim
+ifeq (,$(OMIT_GNULIB_MODULE_getdelim))
+
+ifneq (,$(gl_GNULIB_ENABLED_getdelim_CONDITION))
+ifneq (,$(GL_COND_OBJ_GETDELIM_CONDITION))
+libgnu_a_SOURCES += getdelim.c
+endif
+
+endif
+endif
+## end   gnulib module getdelim
+
 ## begin gnulib module getdtablesize
 ifeq (,$(OMIT_GNULIB_MODULE_getdtablesize))
 
@@ -2117,6 +2147,16 @@ endif
 endif
 ## end   gnulib module getgroups
 
+## begin gnulib module getline
+ifeq (,$(OMIT_GNULIB_MODULE_getline))
+
+ifneq (,$(GL_COND_OBJ_GETLINE_CONDITION))
+libgnu_a_SOURCES += getline.c
+endif
+
+endif
+## end   gnulib module getline
+
 ## begin gnulib module getloadavg
 ifeq (,$(OMIT_GNULIB_MODULE_getloadavg))
 
diff --git a/lib/stdalign.in.h b/lib/stdalign.in.h
index 17357810c7..6523546f16 100644
--- a/lib/stdalign.in.h
+++ b/lib/stdalign.in.h
@@ -17,117 +17,18 @@
 
 /* Written by Paul Eggert and Bruno Haible.  */
 
+/* Define two obsolescent C11 macros, assuming alignas and alignof are
+   either keywords or alignasof-defined macros.  */
+
 #ifndef _GL_STDALIGN_H
 #define _GL_STDALIGN_H
 
-/* ISO C11 <stdalign.h> for platforms that lack it.
-
-   References:
-   ISO C11 (latest free draft
-   <http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf>)
-   sections 6.5.3.4, 6.7.5, 7.15.
-   C++11 (latest free draft
-   <http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2011/n3242.pdf>)
-   section 18.10. */
-
-/* alignof (TYPE), also known as _Alignof (TYPE), yields the alignment
-   requirement of a structure member (i.e., slot or field) that is of
-   type TYPE, as an integer constant expression.
-
-   This differs from GCC's and clang's __alignof__ operator, which can
-   yield a better-performing alignment for an object of that type.  For
-   example, on x86 with GCC and on Linux/x86 with clang,
-   __alignof__ (double) and __alignof__ (long long) are 8, whereas
-   alignof (double) and alignof (long long) are 4 unless the option
-   '-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.  */
-
-/* FreeBSD 9.1 <sys/cdefs.h>, included by <stddef.h> and lots of other
-   standard headers, defines conflicting implementations of _Alignas
-   and _Alignof that are no better than ours; override them.  */
-#undef _Alignas
-#undef _Alignof
-
-/* GCC releases before GCC 4.9 had a bug in _Alignof.  See GCC bug 52023
-   <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=52023>.
-   clang versions < 8.0.0 have the same bug.  */
-#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
-
-/* alignas (A), also known as _Alignas (A), aligns a variable or type
-   to the alignment A, where A is an integer constant expression.  For
-   example:
-
-      int alignas (8) foo;
-      struct s { int a; int alignas (8) bar; };
-
-   aligns the address of FOO and the offset of BAR to be multiples of 8.
-
-   A should be a power of two that is at least the type's alignment
-   and at most the implementation's alignment limit.  This limit is
-   2**28 on typical GNUish hosts, and 2**13 on MSVC.  To be portable
-   to MSVC through at least version 10.0, A should be an integer
-   constant, as MSVC does not support expressions such as 1 << 3.
-   To be portable to Sun C 5.11, do not align auto variables to
-   anything stricter than their default alignment.
-
-   The following C11 requirements are not supported here:
-
-     - If A is zero, alignas has no effect.
-     - alignas can be used multiple times; the strictest one wins.
-     - alignas (TYPE) is equivalent to alignas (alignof (TYPE)).
-
-   */
-
-#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 __STDC_VERSION__ && 202311 <= __STDC_VERSION__) \
      || (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
+#define __alignof_is_defined 1
 
 #endif /* _GL_STDALIGN_H */
diff --git a/lib/stdio-impl.h b/lib/stdio-impl.h
index 81e7f83837..46608bed19 100644
--- a/lib/stdio-impl.h
+++ b/lib/stdio-impl.h
@@ -70,6 +70,12 @@
 #   define _gl_flags_file_t int
 #  else
 #   define _gl_flags_file_t short
+#  endif
+#  ifdef __LP64__
+#   define _gl_file_offset_t int64_t
+#  else
+    /* see 
https://android.googlesource.com/platform/bionic/+/master/docs/32-bit-abi.md */
+#   define _gl_file_offset_t __kernel_off_t
 #  endif
   /* Up to this commit from 2015-10-12
      
<https://android.googlesource.com/platform/bionic.git/+/f0141dfab10a4b332769d52fa76631a64741297a>
@@ -96,7 +102,7 @@
                          unsigned char _nbuf[1]; \
                          struct { unsigned char *_base; size_t _size; } _lb; \
                          int _blksize; \
-                         fpos_t _offset; \
+                         _gl_file_offset_t _offset; \
                          /* More fields, not relevant here.  */ \
                        } *) fp)
 # else
diff --git a/lisp/button.el b/lisp/button.el
index f043073ea8..65abb81ec4 100644
--- a/lisp/button.el
+++ b/lisp/button.el
@@ -72,7 +72,12 @@ Mode-specific keymaps may want to use this as their parent 
keymap."
   ;; mode-line or header-line, the `mode-line' or `header-line' prefix
   ;; shouldn't be necessary!
   "<mode-line> <mouse-2>" #'push-button
-  "<header-line> <mouse-2>" #'push-button)
+  "<header-line> <mouse-2>" #'push-button
+  ;; `push-button' will automatically dispatch to
+  ;; `touch-screen-track-tap'.
+  "<mode-line> <touchscreen-down>" #'push-button
+  "<header-line> <touchscreen-down>" #'push-button
+  "<touchscreen-down>" #'push-button)
 
 (define-minor-mode button-mode
   "A minor mode for navigating to buttons with the TAB key."
@@ -454,18 +459,22 @@ instead of starting at the next button."
 
 (defun push-button (&optional pos use-mouse-action)
   "Perform the action specified by a button at location POS.
-POS may be either a buffer position or a mouse-event.  If
-USE-MOUSE-ACTION is non-nil, invoke the button's `mouse-action'
-property instead of its `action' property; if the button has no
-`mouse-action', the value of `action' is used instead.
+POS may be either a buffer position, a mouse-event, or a
+`touchscreen-down' event.  If USE-MOUSE-ACTION is non-nil, invoke
+the button's `mouse-action' property instead of its `action'
+property; if the button has no `mouse-action', the value of
+`action' is used instead.
+
+If POS is a `touchscreen-down' event, wait for the corresponding
+`touchscreen-up' event before calling `push-button'.
 
 The action in both cases may be either a function to call or a
 marker to display and is invoked using `button-activate' (which
 see).
 
 POS defaults to point, except when `push-button' is invoked
-interactively as the result of a mouse-event, in which case, the
-mouse event is used.
+interactively as the result of a mouse-event or touchscreen
+event, in which case, the position in the event event is used.
 
 If there's no button at POS, do nothing and return nil, otherwise
 return t.
@@ -483,7 +492,12 @@ pushing a button, use the `button-describe' command."
            (if str-button
                ;; mode-line, header-line, or display string event.
                (button-activate str t)
-             (push-button (posn-point posn) t)))))
+              (if (eq (car pos) 'touchscreen-down)
+                  ;; If touch-screen-track tap returns nil, then the
+                  ;; tap was cancelled.
+                  (when (touch-screen-track-tap pos)
+                    (push-button (posn-point posn) t))
+                (push-button (posn-point posn) t))))))
     ;; POS is just normal position
     (let ((button (button-at (or pos (point)))))
       (when button
diff --git a/lisp/frame.el b/lisp/frame.el
index d35df71a6c..f21c0c369b 100644
--- a/lisp/frame.el
+++ b/lisp/frame.el
@@ -2149,8 +2149,12 @@ frame's display)."
   "Return non-nil if popup menus are supported on DISPLAY.
 DISPLAY can be a display name, a frame, or nil (meaning the selected
 frame's display).
-Support for popup menus requires that the mouse be available."
-  (display-mouse-p display))
+Support for popup menus requires that a suitable pointing device
+be available."
+  ;; Android menus work fine with touch screens as well, and one must
+  ;; be present.
+  (or (eq (framep-on-display display) 'android)
+      (display-mouse-p display)))
 
 (defun display-graphic-p (&optional display)
   "Return non-nil if DISPLAY is a graphic display.
diff --git a/lisp/subr.el b/lisp/subr.el
index f909b63aab..345816dbd2 100644
--- a/lisp/subr.el
+++ b/lisp/subr.el
@@ -1636,7 +1636,13 @@ nil or (STRING . POSITION)'.
 `posn-timestamp': The time the event occurred, in milliseconds.
 
 For more information, see Info node `(elisp)Click Events'."
-  (or (and (consp event) (nth 1 event))
+  (or (and (consp event)
+           ;; Ignore touchscreen events.  They store the posn in a
+           ;; different format, and can have multiple posns.
+           (not (memq (car event) '(touchscreen-begin
+                                    touchscreen-update
+                                    touchscreen-end)))
+           (nth 1 event))
       (event--posn-at-point)))
 
 (defun event-end (event)
@@ -1644,7 +1650,11 @@ For more information, see Info node `(elisp)Click 
Events'."
 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))
+  (or (and (consp event)
+           (not (memq (car event) '(touchscreen-begin
+                                    touchscreen-update
+                                    touchscreen-end)))
+           (nth (if (consp (nth 2 event)) 2 1) event))
       (event--posn-at-point)))
 
 (defsubst event-click-count (event)
diff --git a/lisp/touch-screen.el b/lisp/touch-screen.el
index 192a09b3a2..bcc4f5e9be 100644
--- a/lisp/touch-screen.el
+++ b/lisp/touch-screen.el
@@ -105,10 +105,10 @@ known position of the tool."
       (setcar (nthcdr 3 touch-screen-current-tool) 'held)
       ;; Go to the initial position of the touchpoint and activate the
       ;; mark.
-      (with-selected-window (cadr touch-screen-current-tool)
-        (set-mark (posn-point (nth 4 touch-screen-current-tool)))
-        (goto-char (mark))
-        (activate-mark)))))
+      (select-window (cadr touch-screen-current-tool))
+      (set-mark (posn-point (nth 4 touch-screen-current-tool)))
+      (goto-char (mark))
+      (activate-mark))))
 
 (defun touch-screen-handle-point-update (point)
   "Notice that the touch point POINT has changed position.
@@ -132,9 +132,7 @@ happens, cancel `touch-screen-current-timer', and set the 
field
 to `drag'.  Then, activate the mark and start dragging.
 
 If the fourth element of `touch-screen-current-tool' is `drag',
-then move point to the position of POINT.
-
-Set `touch-screen-current-tool' to nil should any error occur."
+then move point to the position of POINT."
   (let ((window (nth 1 touch-screen-current-tool))
         (what (nth 3 touch-screen-current-tool)))
     (cond ((null what)
@@ -252,19 +250,42 @@ POINT should be the point currently tracked as
 
 If the fourth argument of `touch-screen-current-tool' is nil,
 move point to the position of POINT, selecting the window under
-POINT as well; if there is a button at POINT, then activate the
-button there.  Otherwise, deactivate the mark.  Then, display the
-on-screen keyboard."
+POINT as well, and deactivate the mark; if there is a button or
+link at POINT, call the command bound to `mouse-2' there.
+Otherwise, call the command bound to `mouse-1'."
   (let ((what (nth 3 touch-screen-current-tool)))
     (cond ((null what)
            (when (windowp (posn-window (cdr point)))
              ;; Select the window that was tapped.
              (select-window (posn-window (cdr point)))
-             (let ((button (button-at (posn-point (cdr point)))))
-               (when button
-                 (button-activate button t))
+             ;; Now simulate a mouse click there.  If there is a link
+             ;; or a button, use mouse-2 to push it.
+             (let ((event (list (if (or (mouse-on-link-p (cdr point))
+                                        (button-at (posn-point (cdr point))))
+                                    'mouse-2
+                                  'mouse-1)
+                                (cdr point)))
+                   ;; Look for an extra keymap to look in.
+                   (keymap (and (posn-object (cdr point))
+                                (stringp
+                                 (posn-object (cdr point)))
+                                (get-text-property
+                                 0 'keymap
+                                 (posn-object (cdr point)))))
+                   command)
+               (save-excursion
+                 (when (posn-point (cdr point))
+                   (goto-char (posn-point (cdr point))))
+                 (if keymap
+                     (setq keymap (cons keymap (current-active-maps t)))
+                   (setq keymap (current-active-maps t)))
+                 (setq command (lookup-key keymap (vector (car event)))))
+               (deactivate-mark)
+               ;; This is necessary for following links.
                (goto-char (posn-point (cdr point)))
-               (deactivate-mark)))))))
+               (when command
+                 (call-interactively command nil
+                                     (vector event)))))))))
 
 (defun touch-screen-handle-touch (event)
   "Handle a single touch EVENT, and perform associated actions.
@@ -317,6 +338,146 @@ touchscreen-end event."
 (define-key global-map [touchscreen-update] #'touch-screen-handle-touch)
 (define-key global-map [touchscreen-end] #'touch-screen-handle-touch)
 
+
+;; Exports.  These functions are intended for use externally.
+
+(defun touch-screen-track-tap (event &optional update data)
+  "Track a single tap starting from EVENT.
+EVENT should be a `touchscreen-begin' event.
+
+Read touch screen events until a `touchscreen-end' event is
+received with the same ID as in EVENT.  If UPDATE is non-nil and
+a `touchscreen-update' event is received in the mean time and
+contains a touch point with the same ID as in EVENT, call UPDATE
+with that event and DATA.
+
+Return nil immediately if any other kind of event is received;
+otherwise, return t once the `touchscreen-end' event arrives."
+  (catch 'finish
+    (while t
+      (let ((new-event (read-event)))
+        (cond
+         ((eq (car-safe new-event) 'touchscreen-update)
+          (when (and update (assq (caadr event) (cadr new-event)))
+            (funcall update new-event data)))
+         ((eq (car-safe new-event) 'touchscreen-end)
+          (throw 'finish
+                 ;; Now determine whether or not the `touchscreen-end'
+                 ;; event has the same ID as EVENT.  If it doesn't,
+                 ;; then this is another touch, so return nil.
+                 (eq (caadr event) (caadr new-event))))
+         (t (throw 'finish nil)))))))
+
+(defun touch-screen-track-drag (event update &optional data)
+  "Track a single drag starting from EVENT.
+EVENT should be a `touchscreen-end' event.
+
+Read touch screen events until a `touchscreen-end' event is
+received with the same ID as in EVENT.  For each
+`touchscreen-update' event received in the mean time containing a
+touch point with the same ID as in EVENT, call UPDATE with the
+touch point in event and DATA.
+
+Return nil immediately if any other kind of event is received;
+otherwise, return t once the `touchscreen-end' event arrives."
+  (catch 'finish
+    (while t
+      (let ((new-event (read-event)))
+        (cond
+         ((eq (car-safe new-event) 'touchscreen-update)
+          (let ((tool (assq (caadr event) (nth 1 new-event))))
+            (when (and update tool)
+              (funcall update new-event data))))
+         ((eq (car-safe new-event) 'touchscreen-end)
+          (throw 'finish
+                 ;; Now determine whether or not the `touchscreen-end'
+                 ;; event has the same ID as EVENT.  If it doesn't,
+                 ;; then this is another touch, so return nil.
+                 (eq (caadr event) (caadr new-event))))
+         (t (throw 'finish nil)))))))
+
+
+
+;; Modeline dragging.
+
+(defun touch-screen-drag-mode-line-1 (event)
+  "Internal helper for `touch-screen-drag-mode-line'.
+This is called when that function determines it need not execute
+any keymaps on the mode line at that particular spot."
+  ;; Find the window that should be dragged and the starting position.
+  (let* ((window (posn-window (cdadr event)))
+         (relative-xy (touch-screen-relative-xy
+                       (cdadr event) window))
+         (last-position (cdr relative-xy)))
+    (when (window-resizable window 0)
+      (touch-screen-track-drag
+       event (lambda (new-event &optional _data)
+               ;; Find the position of the touchpoint in NEW-EVENT.
+               (let* ((touchpoint (assq (caadr event) (cadr new-event)))
+                      (new-relative-xy
+                       (touch-screen-relative-xy (cdr touchpoint)
+                                                 window))
+                      (position (cdr new-relative-xy))
+                      growth)
+                 ;; Now set the new height of the window.
+                 ;; If new-relative-y is above relative-xy, then
+                 ;; make the window that much shorter.  Otherwise,
+                 ;; make it bigger.
+                 (unless (or (zerop (setq growth (- position last-position)))
+                             (and (> growth 0)
+                                  (< position (+ (window-pixel-top window)
+                                                 (window-pixel-height 
window))))
+                             (and (< growth 0)
+                                  (> position (+ (window-pixel-top window)
+                                                 (window-pixel-height 
window)))))
+                   (adjust-window-trailing-edge window growth nil t))
+                 (setq last-position position)))))))
+
+(defun touch-screen-drag-mode-line (event)
+  "Begin dragging the mode line in response to a touch EVENT.
+If EVENT lies on top of text with a mouse command bound, run
+that command instead.
+
+Change the height of the window based on where the touch point
+in EVENT moves."
+  (interactive "e")
+  ;; If there is an object at EVENT, then look either a keymap bound
+  ;; to [down-mouse-1] or a command bound to [mouse-1].  Then, if a
+  ;; keymap was found, pop it up as a menu.  Otherwise, wait for a tap
+  ;; to complete and run the command found.
+  (let* ((object (posn-object (cdadr event)))
+         (object-keymap (and (consp object)
+                             (stringp (car object))
+                             (or (get-text-property (cdr object)
+                                                    'keymap
+                                                    (car object))
+                                 (get-text-property (cdr object)
+                                                    'local-map
+                                                    (car object)))))
+         (keymap (lookup-key object-keymap [mode-line down-mouse-1]))
+         (command (or (lookup-key object-keymap [mode-line mouse-1])
+                      keymap)))
+    (if (or (keymapp keymap) command)
+        (if (keymapp keymap)
+            (when (touch-screen-track-tap event)
+              (when-let* ((command (x-popup-menu event keymap))
+                          (tem (lookup-key keymap
+                                           (if (consp command)
+                                               (apply #'vector command)
+                                             (vector command))
+                                           t)))
+                (call-interactively tem)))
+          (when (and (commandp command)
+                     (touch-screen-track-tap event))
+            (call-interactively command nil
+                                (vector (list 'mouse-1 (cdadr event))))))
+      (touch-screen-drag-mode-line-1 event))))
+
+(global-set-key [mode-line touchscreen-begin]
+                #'touch-screen-drag-mode-line)
+(global-set-key [bottom-divider touchscreen-begin]
+                #'touch-screen-drag-mode-line)
+
 (provide 'touch-screen)
 
 ;;; touch-screen ends here
diff --git a/lisp/wid-edit.el b/lisp/wid-edit.el
index 60bd2baa6f..4c52d82798 100644
--- a/lisp/wid-edit.el
+++ b/lisp/wid-edit.el
@@ -65,8 +65,11 @@
 ;;; Compatibility.
 
 (defun widget-event-point (event)
-  "Character position of the end of event if that exists, or nil."
-  (posn-point (event-end event)))
+  "Character position of the end of event if that exists, or nil.
+EVENT can either be a mouse event or a touch screen event."
+  (if (eq (car-safe event) 'touchscreen-begin)
+      (posn-point (cdadr event))
+    (posn-point (event-end event))))
 
 (defun widget-button-release-event-p (event)
   "Non-nil if EVENT is a mouse-button-release event object."
@@ -1017,6 +1020,7 @@ button end points."
     (define-key map [backtab] 'widget-backward)
     (define-key map [down-mouse-2] 'widget-button-click)
     (define-key map [down-mouse-1] 'widget-button-click)
+    (define-key map [touchscreen-begin] 'widget-button-click)
     ;; The following definition needs to avoid using escape sequences that
     ;; might get converted to ^M when building loaddefs.el
     (define-key map [(control ?m)] 'widget-button-press)
@@ -1072,8 +1076,18 @@ Note that such modes will need to require wid-edit.")
   "If non-nil, `widget-button-click' moves point to a button after invoking it.
 If nil, point returns to its original position after invoking a button.")
 
+(defun widget-event-start (event)
+  "Return the start of EVENT.
+If EVENT is not a touchscreen event, simply return its
+`event-start'.  Otherwise, it is a touchscreen event, so return
+the posn of its touchpoint."
+  (if (eq (car event) 'touchscreen-begin)
+      (cdadr event)
+    (event-start event)))
+
 (defun widget-button--check-and-call-button (event button)
   "Call BUTTON if BUTTON is a widget and EVENT is correct for it.
+EVENT can either be a mouse event or a touchscreen-begin event.
 If nothing was called, return non-nil."
   (let* ((oevent event)
          (mouse-1 (memq (event-basic-type event) '(mouse-1 down-mouse-1)))
@@ -1084,49 +1098,58 @@ If nothing was called, return non-nil."
       ;; in a save-excursion so that the click on the button
       ;; doesn't change point.
       (save-selected-window
-        (select-window (posn-window (event-start event)))
+        (select-window (posn-window (widget-event-start event)))
         (save-excursion
-         (goto-char (posn-point (event-start event)))
+         (goto-char (posn-point (widget-event-start event)))
          (let* ((overlay (widget-get button :button-overlay))
                 (pressed-face (or (widget-get button :pressed-face)
                                   widget-button-pressed-face))
                 (face (overlay-get overlay 'face))
                 (mouse-face (overlay-get overlay 'mouse-face)))
            (unwind-protect
-               ;; Read events, including mouse-movement
-               ;; events, waiting for a release event.  If we
-               ;; began with a mouse-1 event and receive a
-               ;; movement event, that means the user wants
-               ;; to perform drag-selection, so cancel the
-               ;; button press and do the default mouse-1
-               ;; action.  For mouse-2, just highlight/
-               ;; unhighlight the button the mouse was
-               ;; initially on when we move over it.
+               ;; Read events, including mouse-movement events,
+               ;; waiting for a release event.  If we began with a
+               ;; mouse-1 event and receive a movement event, that
+               ;; means the user wants to perform drag-selection, so
+               ;; cancel the button press and do the default mouse-1
+               ;; action.  For mouse-2, just highlight/ unhighlight
+               ;; the button the mouse was initially on when we move
+               ;; over it.
+                ;;
+                ;; If this function was called in response to a
+                ;; touchscreen event, then wait for a corresponding
+                ;; touchscreen-end event instead.
                (save-excursion
                  (when face            ; avoid changing around image
                    (overlay-put overlay 'face pressed-face)
                    (overlay-put overlay 'mouse-face pressed-face))
-                 (unless (widget-apply button :mouse-down-action event)
-                   (let ((track-mouse t))
-                     (while (not (widget-button-release-event-p event))
-                        (setq event (read--potential-mouse-event))
-                       (when (and mouse-1 (mouse-movement-p event))
-                         (push event unread-command-events)
-                         (setq event oevent)
-                         (throw 'button-press-cancelled t))
-                       (unless (or (integerp event)
-                                   (memq (car event)
-                                          '(switch-frame select-window))
-                                   (eq (car event) 'scroll-bar-movement))
-                         (setq pos (widget-event-point event))
-                         (if (and pos
-                                  (eq (get-char-property pos 'button)
-                                      button))
-                             (when face
-                               (overlay-put overlay 'face pressed-face)
-                               (overlay-put overlay 'mouse-face pressed-face))
-                           (overlay-put overlay 'face face)
-                           (overlay-put overlay 'mouse-face mouse-face))))))
+                  (if (eq (car event) 'touchscreen-begin)
+                      ;; This a touchscreen event and must be handled
+                      ;; specially through `touch-screen-track-tap'.
+                      (progn
+                        (unless (touch-screen-track-tap event)
+                          (throw 'button-press-cancelled t)))
+                    (unless (widget-apply button :mouse-down-action event)
+                      (let ((track-mouse t))
+                        (while (not (widget-button-release-event-p event))
+                          (setq event (read--potential-mouse-event))
+                          (when (and mouse-1 (mouse-movement-p event))
+                            (push event unread-command-events)
+                            (setq event oevent)
+                            (throw 'button-press-cancelled t))
+                          (unless (or (integerp event)
+                                      (memq (car event)
+                                            '(switch-frame select-window))
+                                      (eq (car event) 'scroll-bar-movement))
+                            (setq pos (widget-event-point event))
+                            (if (and pos
+                                     (eq (get-char-property pos 'button)
+                                         button))
+                                (when face
+                                  (overlay-put overlay 'face pressed-face)
+                                  (overlay-put overlay 'mouse-face 
pressed-face))
+                              (overlay-put overlay 'face face)
+                              (overlay-put overlay 'mouse-face 
mouse-face)))))))
 
                  ;; When mouse is released over the button, run
                  ;; its action function.
@@ -1148,32 +1171,35 @@ If nothing was called, return non-nil."
   (if (widget-event-point event)
       (let* ((mouse-1 (memq (event-basic-type event) '(mouse-1 down-mouse-1)))
             (pos (widget-event-point event))
-            (start (event-start event))
+            (start (widget-event-start event))
              (button (get-char-property
                      pos 'button (and (windowp (posn-window start))
                                       (window-buffer (posn-window start))))))
 
        (when (or (null button)
                   (widget-button--check-and-call-button event button))
-         (let ((up t)
+         (let ((up (not (eq (car event) 'touchscreen-begin)))
                 command)
            ;; Mouse click not on a widget button.  Find the global
            ;; command to run, and check whether it is bound to an
            ;; up event.
-           (if mouse-1
-               (cond ((setq command    ;down event
-                            (lookup-key widget-global-map [down-mouse-1]))
-                      (setq up nil))
-                     ((setq command    ;up event
-                            (lookup-key widget-global-map [mouse-1]))))
-             (cond ((setq command      ;down event
-                          (lookup-key widget-global-map [down-mouse-2]))
-                    (setq up nil))
-                   ((setq command      ;up event
-                          (lookup-key widget-global-map [mouse-2])))))
+            (cond
+             ((eq (car event) 'touchscreen-begin)
+              (setq command (lookup-key widget-global-map
+                                        [touchscreen-begin])))
+             (mouse-1 (cond ((setq command     ;down event
+                                   (lookup-key widget-global-map 
[down-mouse-1]))
+                             (setq up nil))
+                            ((setq command     ;up event
+                                   (lookup-key widget-global-map [mouse-1])))))
+             (t (cond ((setq command   ;down event
+                             (lookup-key widget-global-map [down-mouse-2]))
+                       (setq up nil))
+                      ((setq command   ;up event
+                             (lookup-key widget-global-map [mouse-2]))))))
            (when up
              ;; Don't execute up events twice.
-             (while (not (widget-button-release-event-p event))
+             (while (not (and (widget-button-release-event-p event)))
                (setq event (read--potential-mouse-event))))
            (when command
              (call-interactively command)))))
diff --git a/m4/getdelim.m4 b/m4/getdelim.m4
new file mode 100644
index 0000000000..9aaed202ab
--- /dev/null
+++ b/m4/getdelim.m4
@@ -0,0 +1,111 @@
+# getdelim.m4 serial 16
+
+dnl Copyright (C) 2005-2007, 2009-2023 Free Software Foundation, Inc.
+dnl
+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_PREREQ([2.59])
+
+AC_DEFUN([gl_FUNC_GETDELIM],
+[
+  AC_REQUIRE([gl_STDIO_H_DEFAULTS])
+  AC_REQUIRE([AC_CANONICAL_HOST])
+
+  dnl Persuade glibc <stdio.h> to declare getdelim().
+  AC_REQUIRE([AC_USE_SYSTEM_EXTENSIONS])
+
+  AC_CHECK_DECLS_ONCE([getdelim])
+
+  AC_CHECK_FUNCS_ONCE([getdelim])
+  if test $ac_cv_func_getdelim = yes; then
+    HAVE_GETDELIM=1
+    dnl Found it in some library.  Verify that it works.
+    AC_CACHE_CHECK([for working getdelim function],
+      [gl_cv_func_working_getdelim],
+      [case "$host_os" in
+         darwin*)
+           dnl On macOS 10.13, valgrind detected an out-of-bounds read during
+           dnl the GNU sed test suite:
+           dnl   Invalid read of size 16
+           dnl      at 0x100EE6A05: _platform_memchr$VARIANT$Base (in 
/usr/lib/system/libsystem_platform.dylib)
+           dnl      by 0x100B7B0BD: getdelim (in 
/usr/lib/system/libsystem_c.dylib)
+           dnl      by 0x10000B0BE: ck_getdelim (utils.c:254)
+           gl_cv_func_working_getdelim=no ;;
+         *)
+           echo fooNbarN | tr -d '\012' | tr N '\012' > conftest.data
+           AC_RUN_IFELSE([AC_LANG_SOURCE([[
+#    include <stdio.h>
+#    include <stdlib.h>
+#    include <string.h>
+    int main ()
+    {
+      FILE *in = fopen ("./conftest.data", "r");
+      if (!in)
+        return 1;
+      {
+        /* Test result for a NULL buffer and a zero size.
+           Based on a test program from Karl Heuer.  */
+        char *line = NULL;
+        size_t siz = 0;
+        int len = getdelim (&line, &siz, '\n', in);
+        if (!(len == 4 && line && strcmp (line, "foo\n") == 0))
+          { free (line); fclose (in); return 2; }
+        free (line);
+      }
+      {
+        /* Test result for a NULL buffer and a non-zero size.
+           This crashes on FreeBSD 8.0.  */
+        char *line = NULL;
+        size_t siz = (size_t)(~0) / 4;
+        if (getdelim (&line, &siz, '\n', in) == -1)
+          { fclose (in); return 3; }
+        free (line);
+      }
+      fclose (in);
+      return 0;
+    }
+    ]])],
+             [gl_cv_func_working_getdelim=yes],
+             [gl_cv_func_working_getdelim=no],
+             [dnl We're cross compiling.
+              dnl Guess it works on glibc2 systems and musl systems.
+              AC_EGREP_CPP([Lucky GNU user],
+                [
+#include <features.h>
+#ifdef __GNU_LIBRARY__
+ #if (__GLIBC__ >= 2) && !defined __UCLIBC__
+  Lucky GNU user
+ #endif
+#endif
+                ],
+                [gl_cv_func_working_getdelim="guessing yes"],
+                [case "$host_os" in
+                   *-musl*) gl_cv_func_working_getdelim="guessing yes" ;;
+                   *)       
gl_cv_func_working_getdelim="$gl_cross_guess_normal" ;;
+                 esac
+                ])
+             ])
+           ;;
+       esac
+      ])
+    case "$gl_cv_func_working_getdelim" in
+      *yes) ;;
+      *) REPLACE_GETDELIM=1 ;;
+    esac
+  else
+    HAVE_GETDELIM=0
+  fi
+
+  if test $ac_cv_have_decl_getdelim = no; then
+    HAVE_DECL_GETDELIM=0
+  fi
+])
+
+# Prerequisites of lib/getdelim.c.
+AC_DEFUN([gl_PREREQ_GETDELIM],
+[
+  AC_CHECK_FUNCS([flockfile funlockfile])
+  AC_CHECK_DECLS([getc_unlocked])
+])
diff --git a/m4/getline.m4 b/m4/getline.m4
new file mode 100644
index 0000000000..03569f06b2
--- /dev/null
+++ b/m4/getline.m4
@@ -0,0 +1,109 @@
+# getline.m4 serial 30
+
+dnl Copyright (C) 1998-2003, 2005-2007, 2009-2023 Free Software Foundation,
+dnl Inc.
+dnl
+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_PREREQ([2.59])
+
+dnl See if there's a working, system-supplied version of the getline function.
+dnl We can't just do AC_REPLACE_FUNCS([getline]) because some systems
+dnl have a function by that name in -linet that doesn't have anything
+dnl to do with the function we need.
+AC_DEFUN([gl_FUNC_GETLINE],
+[
+  AC_REQUIRE([gl_STDIO_H_DEFAULTS])
+  AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles
+
+  dnl Persuade glibc <stdio.h> to declare getline().
+  AC_REQUIRE([AC_USE_SYSTEM_EXTENSIONS])
+
+  AC_CHECK_DECLS_ONCE([getline])
+
+  gl_getline_needs_run_time_check=no
+  AC_CHECK_FUNC([getline],
+                [dnl Found it in some library.  Verify that it works.
+                 gl_getline_needs_run_time_check=yes],
+                [am_cv_func_working_getline=no])
+  if test $gl_getline_needs_run_time_check = yes; then
+    AC_CACHE_CHECK([for working getline function],
+      [am_cv_func_working_getline],
+      [echo fooNbarN | tr -d '\012' | tr N '\012' > conftest.data
+       AC_RUN_IFELSE([AC_LANG_SOURCE([[
+#    include <stdio.h>
+#    include <stdlib.h>
+#    include <string.h>
+    int main ()
+    {
+      FILE *in = fopen ("./conftest.data", "r");
+      if (!in)
+        return 1;
+      {
+        /* Test result for a NULL buffer and a zero size.
+           Based on a test program from Karl Heuer.  */
+        char *line = NULL;
+        size_t siz = 0;
+        int len = getline (&line, &siz, in);
+        if (!(len == 4 && line && strcmp (line, "foo\n") == 0))
+          { free (line); fclose (in); return 2; }
+        free (line);
+      }
+      {
+        /* Test result for a NULL buffer and a non-zero size.
+           This crashes on FreeBSD 8.0.  */
+        char *line = NULL;
+        size_t siz = (size_t)(~0) / 4;
+        if (getline (&line, &siz, in) == -1)
+          { fclose (in); return 3; }
+        free (line);
+      }
+      fclose (in);
+      return 0;
+    }
+    ]])],
+         [am_cv_func_working_getline=yes],
+         [am_cv_func_working_getline=no],
+         [dnl We're cross compiling.
+          dnl Guess it works on glibc2 systems and musl systems.
+          AC_EGREP_CPP([Lucky GNU user],
+            [
+#include <features.h>
+#ifdef __GNU_LIBRARY__
+ #if (__GLIBC__ >= 2) && !defined __UCLIBC__
+  Lucky GNU user
+ #endif
+#endif
+            ],
+            [am_cv_func_working_getline="guessing yes"],
+            [case "$host_os" in
+               *-musl*) am_cv_func_working_getline="guessing yes" ;;
+               *)       am_cv_func_working_getline="$gl_cross_guess_normal" ;;
+             esac
+            ])
+         ])
+      ])
+  fi
+
+  if test $ac_cv_have_decl_getline = no; then
+    HAVE_DECL_GETLINE=0
+  fi
+
+  case "$am_cv_func_working_getline" in
+    *yes) ;;
+    *)
+      dnl Set REPLACE_GETLINE always: Even if we have not found the broken
+      dnl getline function among $LIBS, it may exist in libinet and the
+      dnl executable may be linked with -linet.
+      REPLACE_GETLINE=1
+      ;;
+  esac
+])
+
+# Prerequisites of lib/getline.c.
+AC_DEFUN([gl_PREREQ_GETLINE],
+[
+  :
+])
diff --git a/m4/gnulib-comp.m4 b/m4/gnulib-comp.m4
index 10c74fa239..501fe20f4e 100644
--- a/m4/gnulib-comp.m4
+++ b/m4/gnulib-comp.m4
@@ -44,6 +44,7 @@ AC_DEFUN([gl_EARLY],
 
   # Code from module absolute-header:
   # Code from module acl-permissions:
+  # Code from module alignasof:
   # Code from module alloca-opt:
   # Code from module allocator:
   # Code from module assert-h:
@@ -103,8 +104,10 @@ AC_DEFUN([gl_EARLY],
   # Code from module fsync:
   # Code from module futimens:
   # Code from module gen-header:
+  # Code from module getdelim:
   # Code from module getdtablesize:
   # Code from module getgroups:
+  # Code from module getline:
   # Code from module getloadavg:
   # Code from module getopt-gnu:
   # Code from module getopt-posix:
@@ -231,6 +234,7 @@ AC_DEFUN([gl_INIT],
   gl_source_base='lib'
   gl_source_base_prefix=
   gl_FUNC_ACL
+  gl_ALIGNASOF
   gl_FUNC_ALLOCA
   gl_CONDITIONAL_HEADER([alloca.h])
   AC_PROG_MKDIR_P
@@ -342,6 +346,12 @@ AC_DEFUN([gl_INIT],
   gl_CONDITIONAL([GL_COND_OBJ_FUTIMENS],
                  [test $HAVE_FUTIMENS = 0 || test $REPLACE_FUTIMENS = 1])
   gl_SYS_STAT_MODULE_INDICATOR([futimens])
+  gl_FUNC_GETLINE
+  gl_CONDITIONAL([GL_COND_OBJ_GETLINE], [test $REPLACE_GETLINE = 1])
+  AM_COND_IF([GL_COND_OBJ_GETLINE], [
+    gl_PREREQ_GETLINE
+  ])
+  gl_STDIO_MODULE_INDICATOR([getline])
   AC_REQUIRE([AC_CANONICAL_HOST])
   gl_GETLOADAVG
   gl_CONDITIONAL([GL_COND_OBJ_GETLOADAVG], [test $HAVE_GETLOADAVG = 0])
@@ -637,6 +647,7 @@ AC_DEFUN([gl_INIT],
   gl_gnulib_enabled_dirfd=false
   gl_gnulib_enabled_925677f0343de64b89a9f0c790b4104c=false
   gl_gnulib_enabled_euidaccess=false
+  gl_gnulib_enabled_getdelim=false
   gl_gnulib_enabled_getdtablesize=false
   gl_gnulib_enabled_getgroups=false
   gl_gnulib_enabled_be453cec5eecf5731a274f2de7f2db36=false
@@ -708,6 +719,19 @@ AC_DEFUN([gl_INIT],
       func_gl_gnulib_m4code_6099e9737f757db36c47fa9d9f02e88c
     fi
   }
+  func_gl_gnulib_m4code_getdelim ()
+  {
+    if ! $gl_gnulib_enabled_getdelim; then
+      gl_FUNC_GETDELIM
+      gl_CONDITIONAL([GL_COND_OBJ_GETDELIM],
+                     [test $HAVE_GETDELIM = 0 || test $REPLACE_GETDELIM = 1])
+      AM_COND_IF([GL_COND_OBJ_GETDELIM], [
+        gl_PREREQ_GETDELIM
+      ])
+      gl_STDIO_MODULE_INDICATOR([getdelim])
+      gl_gnulib_enabled_getdelim=true
+    fi
+  }
   func_gl_gnulib_m4code_getdtablesize ()
   {
     if ! $gl_gnulib_enabled_getdtablesize; then
@@ -973,6 +997,9 @@ AC_DEFUN([gl_INIT],
   if test $HAVE_FUTIMENS = 0 || test $REPLACE_FUTIMENS = 1; then
     func_gl_gnulib_m4code_utimens
   fi
+  if test $REPLACE_GETLINE = 1; then
+    func_gl_gnulib_m4code_getdelim
+  fi
   if case $host_os in mingw*) false;; *) test $HAVE_GETLOADAVG = 0;; esac; then
     func_gl_gnulib_m4code_open
   fi
@@ -1012,6 +1039,7 @@ AC_DEFUN([gl_INIT],
   AM_CONDITIONAL([gl_GNULIB_ENABLED_dirfd], [$gl_gnulib_enabled_dirfd])
   AM_CONDITIONAL([gl_GNULIB_ENABLED_925677f0343de64b89a9f0c790b4104c], 
[$gl_gnulib_enabled_925677f0343de64b89a9f0c790b4104c])
   AM_CONDITIONAL([gl_GNULIB_ENABLED_euidaccess], 
[$gl_gnulib_enabled_euidaccess])
+  AM_CONDITIONAL([gl_GNULIB_ENABLED_getdelim], [$gl_gnulib_enabled_getdelim])
   AM_CONDITIONAL([gl_GNULIB_ENABLED_getdtablesize], 
[$gl_gnulib_enabled_getdtablesize])
   AM_CONDITIONAL([gl_GNULIB_ENABLED_getgroups], [$gl_gnulib_enabled_getgroups])
   AM_CONDITIONAL([gl_GNULIB_ENABLED_be453cec5eecf5731a274f2de7f2db36], 
[$gl_gnulib_enabled_be453cec5eecf5731a274f2de7f2db36])
@@ -1278,8 +1306,10 @@ AC_DEFUN([gl_FILE_LIST], [
   lib/ftoastr.h
   lib/futimens.c
   lib/get-permissions.c
+  lib/getdelim.c
   lib/getdtablesize.c
   lib/getgroups.c
+  lib/getline.c
   lib/getloadavg.c
   lib/getopt-cdefs.in.h
   lib/getopt-core.h
@@ -1456,8 +1486,10 @@ AC_DEFUN([gl_FILE_LIST], [
   m4/fsusage.m4
   m4/fsync.m4
   m4/futimens.m4
+  m4/getdelim.m4
   m4/getdtablesize.m4
   m4/getgroups.m4
+  m4/getline.m4
   m4/getloadavg.m4
   m4/getopt.m4
   m4/getrandom.m4
diff --git a/m4/stdalign.m4 b/m4/stdalign.m4
index b1438eeace..0bb9281f5e 100644
--- a/m4/stdalign.m4
+++ b/m4/stdalign.m4
@@ -5,9 +5,11 @@ 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 Written by Paul Eggert and Bruno Haible.
+
 # Prepare for substituting <stdalign.h> if it is not supported.
 
-AC_DEFUN([gl_STDALIGN_H],
+AC_DEFUN([gl_ALIGNASOF],
 [
   AC_CACHE_CHECK([for alignas and alignof],
     [gl_cv_header_working_stdalign_h],
@@ -58,16 +60,11 @@ AC_DEFUN([gl_STDALIGN_H],
       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],
@@ -75,11 +72,33 @@ AC_DEFUN([gl_STDALIGN_H],
 # 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
+/* ISO C23 alignas and alignof for platforms that lack it.
+
+   References:
+   ISO C23 (latest free draft
+   <http://www.open-std.org/jtc1/sc22/wg14/www/docs/n3047.pdf>)
+   sections 6.5.3.4, 6.7.5, 7.15.
+   C++11 (latest free draft
+   <http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2011/n3242.pdf>)
+   section 18.10. */
+
+/* alignof (TYPE), also known as _Alignof (TYPE), yields the alignment
+   requirement of a structure member (i.e., slot or field) that is of
+   type TYPE, as an integer constant expression.
+
+   This differs from GCC's and clang's __alignof__ operator, which can
+   yield a better-performing alignment for an object of that type.  For
+   example, on x86 with GCC and on Linux/x86 with clang,
+   __alignof__ (double) and __alignof__ (long long) are 8, whereas
+   alignof (double) and alignof (long long) are 4 unless the option
+   '-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.  */
+
+/* GCC releases before GCC 4.9 had a bug in _Alignof.  See GCC bug 52023
+   <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=52023>.
+   clang versions < 8.0.0 have the same bug.  */
 #   if (!defined __STDC_VERSION__ || __STDC_VERSION__ < 201112 \
         || (defined __GNUC__ && __GNUC__ < 4 + (__GNUC_MINOR__ < 9) \
             && !defined __clang__) \
@@ -100,35 +119,65 @@ AC_DEFUN([gl_STDALIGN_H],
 #   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>
+
+/* alignas (A), also known as _Alignas (A), aligns a variable or type
+   to the alignment A, where A is an integer constant expression.  For
+   example:
+
+      int alignas (8) foo;
+      struct s { int a; int alignas (8) bar; };
+
+   aligns the address of FOO and the offset of BAR to be multiples of 8.
+
+   A should be a power of two that is at least the type's alignment
+   and at most the implementation's alignment limit.  This limit is
+   2**28 on typical GNUish hosts, and 2**13 on MSVC.  To be portable
+   to MSVC through at least version 10.0, A should be an integer
+   constant, as MSVC does not support expressions such as 1 << 3.
+   To be portable to Sun C 5.11, do not align auto variables to
+   anything stricter than their default alignment.
+
+   The following C23 requirements are not supported here:
+
+     - If A is zero, alignas has no effect.
+     - alignas can be used multiple times; the strictest one wins.
+     - alignas (TYPE) is equivalent to alignas (alignof (TYPE)).
+
+   */
+#  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 /* _GL_STDALIGN_H */
+#  endif
+#  if ((defined _Alignas \
+        && !(defined __cplusplus \
+             && (201103 <= __cplusplus || defined _MSC_VER))) \
+       || (defined __STDC_VERSION__ && 201112 <= __STDC_VERSION__))
+#   define alignas _Alignas
+#  endif
+#  if _GL_STDALIGN_NEEDS_STDDEF
+#   include <stddef.h>
+#  endif
 # endif
 #endif])
 ])
+
+AC_DEFUN([gl_STDALIGN_H],
+[
+  AC_REQUIRE([gl_ALIGNASOF])
+  GL_GENERATE_STDALIGN_H=false
+  AS_IF([test "$gl_cv_header_working_stdalign_h" = no],
+    [GL_GENERATE_STDALIGN_H=true])
+
+  AC_CHECK_HEADERS_ONCE([stdalign.h])
+])
diff --git a/m4/unistd_h.m4 b/m4/unistd_h.m4
index f4384027e3..dd799ae27d 100644
--- a/m4/unistd_h.m4
+++ b/m4/unistd_h.m4
@@ -50,6 +50,7 @@ AC_DEFUN_ONCE([gl_UNISTD_H],
     group_member isatty lchown link linkat lseek pipe pipe2 pread pwrite
     readlink readlinkat rmdir sethostname sleep symlink symlinkat
     truncate ttyname_r unlink unlinkat usleep])
+  gl_CHECK_FUNCS_ANDROID([ftruncate], [[#include <unistd.h>]])
 
   AC_REQUIRE([AC_C_RESTRICT])
 
diff --git a/m4/utimens.m4 b/m4/utimens.m4
index c5d9b69e6f..2b87f0149b 100644
--- a/m4/utimens.m4
+++ b/m4/utimens.m4
@@ -11,7 +11,8 @@ AC_DEFUN([gl_UTIMENS],
   AC_REQUIRE([gl_FUNC_UTIMES])
   AC_REQUIRE([gl_CHECK_TYPE_STRUCT_TIMESPEC])
   AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles
-  AC_CHECK_FUNCS_ONCE([futimens utimensat lutimes])
+  AC_CHECK_FUNCS_ONCE([futimens lutimes])
+  gl_CHECK_FUNCS_ANDROID([utimensat], [[#include <sys/stat.h>]])
   gl_CHECK_FUNCS_ANDROID([futimes], [[#include <sys/time.h>]])
   gl_CHECK_FUNCS_ANDROID([futimesat], [[#include <sys/time.h>]])
 
diff --git a/m4/utimensat.m4 b/m4/utimensat.m4
index dd210fc989..f92fc3af7d 100644
--- a/m4/utimensat.m4
+++ b/m4/utimensat.m4
@@ -13,7 +13,9 @@ AC_DEFUN([gl_FUNC_UTIMENSAT],
   AC_REQUIRE([gl_SYS_STAT_H_DEFAULTS])
   AC_REQUIRE([gl_USE_SYSTEM_EXTENSIONS])
   AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles
-  AC_CHECK_FUNCS_ONCE([utimensat])
+  # This is necessary for cross-compiles, because otherwise utimensat
+  # will appear to work.
+  gl_CHECK_FUNCS_ANDROID([utimensat], [[#include <sys/stat.h>]])
   if test $ac_cv_func_utimensat = no; then
     HAVE_UTIMENSAT=0
   else
diff --git a/m4/xattr.m4 b/m4/xattr.m4
index 6141515652..0e179cc0d1 100644
--- a/m4/xattr.m4
+++ b/m4/xattr.m4
@@ -1,5 +1,5 @@
 # xattr.m4 - check for Extended Attributes (Linux)
-# serial 5
+# serial 6
 
 # Copyright (C) 2003-2023 Free Software Foundation, Inc.
 # This file is free software; the Free Software Foundation
@@ -17,23 +17,33 @@ AC_DEFUN([gl_FUNC_XATTR],
   AC_SUBST([LIB_XATTR])
 
   if test "$use_xattr" = yes; then
-    AC_CHECK_HEADERS([attr/error_context.h attr/libattr.h])
-    use_xattr=no
-    if test "$ac_cv_header_attr_libattr_h" = yes \
-        && test "$ac_cv_header_attr_error_context_h" = yes; then
-      xattr_saved_LIBS=$LIBS
-      AC_SEARCH_LIBS([attr_copy_file], [attr],
-                     [test "$ac_cv_search_attr_copy_file" = "none required" ||
-                        LIB_XATTR="$ac_cv_search_attr_copy_file"])
-      AC_CHECK_FUNCS([attr_copy_file])
-      LIBS=$xattr_saved_LIBS
-      if test "$ac_cv_func_attr_copy_file" = yes; then
-        use_xattr=yes
-      fi
-    fi
-    if test $use_xattr = no; then
+    AC_CACHE_CHECK([for xattr library with ATTR_ACTION_PERMISSIONS],
+      [gl_cv_xattr_lib],
+      [gl_cv_xattr_lib=no
+       AC_LANG_CONFTEST(
+         [AC_LANG_PROGRAM(
+            [[#include <attr/error_context.h>
+              #include <attr/libattr.h>
+              static int
+              is_attr_permissions (const char *name, struct error_context *ctx)
+              {
+                return attr_copy_action (name, ctx) == ATTR_ACTION_PERMISSIONS;
+              }
+            ]],
+            [[return attr_copy_fd ("/", 0, "/", 0, is_attr_permissions, 0);
+            ]])])
+       AC_LINK_IFELSE([],
+         [gl_cv_xattr_lib='none required'],
+         [xattr_saved_LIBS=$LIBS
+          LIBS="-lattr $LIBS"
+          AC_LINK_IFELSE([], [gl_cv_xattr_lib=-lattr])
+          LIBS=$xattr_saved_LIBS])])
+    if test "$gl_cv_xattr_lib" = no; then
       AC_MSG_WARN([libattr development library was not found or not usable.])
       AC_MSG_WARN([AC_PACKAGE_NAME will be built without xattr support.])
+      use_xattr=no
+    elif test "$gl_cv_xattr_lib" != 'none required'; then
+      LIB_XATTR=$gl_cv_xattr_lib
     fi
   fi
   if test "$use_xattr" = yes; then
diff --git a/src/alloc.c b/src/alloc.c
index 86e019b931..ed55ae3271 100644
--- a/src/alloc.c
+++ b/src/alloc.c
@@ -6172,16 +6172,44 @@ mark_pinned_objects (void)
     mark_object (pobj->object);
 }
 
+#if defined HAVE_ANDROID && !defined (__clang__)
+
+/* The Android gcc is broken and needs the following version of
+   make_lisp_symbol.  Otherwise a mysterious ICE pops up.  */
+
+#define make_lisp_symbol android_make_lisp_symbol
+
+static Lisp_Object
+android_make_lisp_symbol (struct Lisp_Symbol *sym)
+{
+  intptr_t symoffset;
+  Lisp_Object a;
+
+  symoffset = (intptr_t) sym;
+  INT_SUBTRACT_WRAPV (symoffset, (intptr_t) &lispsym,
+                     &symoffset);
+
+  a = TAG_PTR (Lisp_Symbol, symoffset);
+  return a;
+}
+
+#endif
+
 static void
 mark_pinned_symbols (void)
 {
   struct symbol_block *sblk;
-  int lim = (symbol_block_pinned == symbol_block
-            ? symbol_block_index : SYMBOL_BLOCK_SIZE);
+  int lim;
+  struct Lisp_Symbol *sym, *end;
+
+  if (symbol_block_pinned == symbol_block)
+    lim = symbol_block_index;
+  else
+    lim = SYMBOL_BLOCK_SIZE;
 
   for (sblk = symbol_block_pinned; sblk; sblk = sblk->next)
     {
-      struct Lisp_Symbol *sym = sblk->symbols, *end = sym + lim;
+      sym = sblk->symbols, end = sym + lim;
       for (; sym < end; ++sym)
        if (sym->u.s.pinned)
          mark_object (make_lisp_symbol (sym));
diff --git a/src/android.c b/src/android.c
index cfb79045c0..eb9c404f1a 100644
--- a/src/android.c
+++ b/src/android.c
@@ -54,6 +54,9 @@ bool android_init_gui;
 #include <android/log.h>
 
 #include <linux/ashmem.h>
+#include <linux/unistd.h>
+
+#include <sys/syscall.h>
 
 #define ANDROID_THROW(env, class, msg)                                 \
   ((*(env))->ThrowNew ((env), (*(env))->FindClass ((env), class), msg))
@@ -114,6 +117,12 @@ struct android_emacs_drawable
   jmethodID damage_rect;
 };
 
+struct android_emacs_window
+{
+  jclass class;
+  jmethodID swap_buffers;
+};
+
 /* The asset manager being used.  */
 static AAssetManager *asset_manager;
 
@@ -181,6 +190,9 @@ static struct android_graphics_point point_class;
 /* Various methods associated with the EmacsDrawable class.  */
 static struct android_emacs_drawable drawable_class;
 
+/* Various methods associated with the EmacsWindow class.  */
+static struct android_emacs_window window_class;
+
 
 
 /* Event handling functions.  Events are stored on a (circular) queue
@@ -247,10 +259,21 @@ android_run_select_thread (void *data)
 
   sigfillset (&signals);
 
+#if __ANDROID_API__ < 16
+  /* sigprocmask must be used instead of pthread_sigmask due to a bug
+     in Android versions earlier than 16.  It only affects the calling
+     thread on Android anyhow.  */
+
+  if (sigprocmask (SIG_BLOCK, &signals, NULL))
+    __android_log_print (ANDROID_LOG_FATAL, __func__,
+                        "sigprocmask: %s",
+                        strerror (errno));
+#else
   if (pthread_sigmask (SIG_BLOCK, &signals, NULL))
     __android_log_print (ANDROID_LOG_FATAL, __func__,
                         "pthread_sigmask: %s",
                         strerror (errno));
+#endif
 
   sigdelset (&signals, SIGUSR1);
   sigemptyset (&waitset);
@@ -292,6 +315,8 @@ android_run_select_thread (void *data)
         still be locked, so this must come before.  */
       sem_post (&android_pselect_sem);
     }
+
+  return NULL;
 }
 
 static void
@@ -538,6 +563,200 @@ android_run_debug_thread (void *data)
 
 
 
+/* Asset directory handling functions.  ``directory-tree'' is a file in
+   the root of the assets directory describing its contents.
+
+   See lib-src/asset-directory-tool for more details.  */
+
+/* The Android directory tree.  */
+static const char *directory_tree;
+
+/* The size of the directory tree.  */
+static size_t directory_tree_size;
+
+/* Read an unaligned (32-bit) long from the address POINTER.  */
+
+static unsigned int
+android_extract_long (char *pointer)
+{
+  unsigned int number;
+
+  memcpy (&number, pointer, sizeof number);
+  return number;
+}
+
+/* Scan to the file FILE in the asset directory tree.  Return a
+   pointer to the end of that file (immediately before any children)
+   in the directory tree, or NULL if that file does not exist.
+
+   If returning non-NULL, also return the offset to the end of the
+   last subdirectory or file in *LIMIT_RETURN.  LIMIT_RETURN may be
+   NULL.
+
+   FILE must have less than 11 levels of nesting.  If it ends with a
+   trailing slash, then NULL will be returned if it is not actually a
+   directory.  */
+
+static const char *
+android_scan_directory_tree (char *file, size_t *limit_return)
+{
+  char *token, *saveptr, *copy, *copy1, *start, *max, *limit;
+  size_t token_length, ntokens, i;
+  char *tokens[10];
+
+  USE_SAFE_ALLOCA;
+
+  /* Skip past the 5 byte header.  */
+  start = (char *) directory_tree + 5;
+
+  /* Figure out the current limit.  */
+  limit = (char *) directory_tree + directory_tree_size;
+
+  /* Now, split `file' into tokens, with the delimiter being the file
+     name separator.  Look for the file and seek past it.  */
+
+  ntokens = 0;
+  saveptr = NULL;
+  copy = copy1 = xstrdup (file);
+  memset (tokens, 0, sizeof tokens);
+
+  while ((token = strtok_r (copy, "/", &saveptr)))
+    {
+      copy = NULL;
+
+      /* Make sure ntokens is within bounds.  */
+      if (ntokens == ARRAYELTS (tokens))
+       {
+         xfree (copy1);
+         goto fail;
+       }
+
+      tokens[ntokens] = SAFE_ALLOCA (strlen (token) + 1);
+      memcpy (tokens[ntokens], token, strlen (token) + 1);
+      ntokens++;
+    }
+
+  /* Free the copy created for strtok_r.  */
+  xfree (copy1);
+
+  /* If there are no tokens, just return the start of the directory
+     tree.  */
+  if (!ntokens)
+    {
+      SAFE_FREE ();
+
+      /* Subtract the initial header bytes.  */
+      if (limit_return)
+       *limit_return = directory_tree_size - 5;
+
+      return start;
+    }
+
+  /* Loop through tokens, indexing the directory tree each time.  */
+
+  for (i = 0; i < ntokens; ++i)
+    {
+      token = tokens[i];
+
+      /* Figure out how many bytes to compare.  */
+      token_length = strlen (token);
+
+    again:
+
+      /* If this would be past the directory, return NULL.  */
+      if (start + token_length > limit)
+       goto fail;
+
+      /* Now compare the file name.  */
+      if (!memcmp (start, token, token_length))
+       {
+         /* They probably match.  Find the NULL byte.  It must be
+            either one byte past start + token_length, with the last
+            byte a trailing slash (indicating that it is a
+            directory), or just start + token_length.  Return 4 bytes
+            past the next NULL byte.  */
+
+         max = memchr (start, 0, limit - start);
+
+         if (max != start + token_length
+             && !(max == start + token_length + 1
+                  && *(max - 1) == '/'))
+           goto false_positive;
+
+         /* Return it if it exists and is in range, and this is the
+            last token.  Otherwise, set it as start and the limit as
+            start + the offset and continue the loop.  */
+
+         if (max && max + 5 <= limit)
+           {
+             if (i < ntokens - 1)
+               {
+                 start = max + 5;
+                 limit = ((char *) directory_tree
+                          + android_extract_long (max + 1));
+
+                 /* Make sure limit is still in range.  */
+                 if (limit > directory_tree + directory_tree_size
+                     || start > directory_tree + directory_tree_size)
+                   goto fail;
+
+                 continue;
+               }
+
+             /* Now see if max is not a directory and file is.  If
+                file is a directory, then return NULL.  */
+             if (*(max - 1) != '/' && file[strlen (file) - 1] == '/')
+               max = NULL;
+             else
+               {
+                 /* Figure out the limit.  */
+                 if (limit_return)
+                   *limit_return = android_extract_long (max + 1);
+
+                 /* Go to the end of this file.  */
+                 max += 5;
+               }
+
+             SAFE_FREE ();
+             return max;
+           }
+
+         /* Return NULL otherwise.  */
+         __android_log_print (ANDROID_LOG_WARN, __func__,
+                              "could not scan to end of directory tree"
+                              ": %s", file);
+         goto fail;
+       }
+
+    false_positive:
+
+      /* No match was found.  Set start to the next sibling and try
+        again.  */
+
+      start = memchr (start, 0, limit - start);
+
+      if (!start || start + 5 > limit)
+       goto fail;
+
+      start = ((char *) directory_tree
+              + android_extract_long (start + 1));
+
+      /* Make sure start is still in bounds.  */
+
+      if (start > limit)
+       goto fail;
+
+      /* Continue the loop.  */
+      goto again;
+    }
+
+ fail:
+  SAFE_FREE ();
+  return NULL;
+}
+
+
+
 /* Intercept USER_FULL_NAME and return something that makes sense if
    pw->pw_gecos is NULL.  */
 
@@ -624,10 +843,6 @@ android_fstatat (int dirfd, const char *restrict pathname,
 bool
 android_file_access_p (const char *name, int amode)
 {
-  AAsset *asset;
-  AAssetDir *directory;
-  int length;
-
   if (!asset_manager)
     return false;
 
@@ -637,50 +852,11 @@ android_file_access_p (const char *name, int amode)
        /* /assets always exists.  */
        return true;
 
-      /* Check if the asset exists by opening it.  Suboptimal! */
-      asset = AAssetManager_open (asset_manager, name,
-                                 AASSET_MODE_UNKNOWN);
-
-      if (!asset)
-       {
-         /* See if it's a directory as well.  To open a directory
-            with the asset manager, the trailing slash (if specified)
-            must be removed.  */
-         directory = AAssetManager_openDir (asset_manager, name);
-
-         if (directory)
-           {
-             /* Make sure the directory actually has files in it.  */
-
-             if (!AAssetDir_getNextFileName (directory))
-               {
-                 AAssetDir_close (directory);
-                 errno = ENOENT;
-                 return false;
-               }
-
-             AAssetDir_close (directory);
-             return true;
-           }
-
-         errno = ENOENT;
-         return false;
-       }
-
-      AAsset_close (asset);
-
-      /* If NAME is a directory name, but it was a regular file, set
-        errno to ENOTDIR and return false.  This is to behave like
-        faccessat.  */
-
-      length = strlen (name);
-      if (name[length - 1] == '/')
-       {
-         errno = ENOTDIR;
-         return false;
-       }
-
-      return true;
+      /* Check if the file exists by looking in the ``directory tree''
+        asset generated during the build process.  This is used
+        instead of the AAsset functions, because the latter are
+        buggy and treat directories inconsistently.  */
+      return android_scan_directory_tree ((char *) name, NULL) != NULL;
     }
 
   return false;
@@ -908,8 +1084,13 @@ android_get_home_directory (void)
 
 /* JNI functions called by Java.  */
 
+#ifdef __clang__
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wmissing-prototypes"
+#else
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wmissing-prototypes"
+#endif
 
 JNIEXPORT void JNICALL
 NATIVE_NAME (setEmacsParams) (JNIEnv *env, jobject object,
@@ -923,6 +1104,7 @@ NATIVE_NAME (setEmacsParams) (JNIEnv *env, jobject object,
   int pipefd[2];
   pthread_t thread;
   const char *java_string;
+  AAsset *asset;
 
   /* This may be called from multiple threads.  setEmacsParams should
      only ever be called once.  */
@@ -943,6 +1125,33 @@ NATIVE_NAME (setEmacsParams) (JNIEnv *env, jobject object,
   /* Set the asset manager.  */
   asset_manager = AAssetManager_fromJava (env, local_asset_manager);
 
+  /* Initialize the directory tree.  */
+  asset = AAssetManager_open (asset_manager, "directory-tree",
+                             AASSET_MODE_BUFFER);
+
+  if (!asset)
+    {
+      __android_log_print (ANDROID_LOG_FATAL, __func__,
+                          "Failed to open directory tree");
+      emacs_abort ();
+    }
+
+  directory_tree = AAsset_getBuffer (asset);
+
+  if (!directory_tree)
+    emacs_abort ();
+
+  /* Now figure out how big the directory tree is, and compare the
+     first few bytes.  */
+  directory_tree_size = AAsset_getLength (asset);
+  if (directory_tree_size < 5
+      || memcmp (directory_tree, "EMACS", 5))
+    {
+      __android_log_print (ANDROID_LOG_FATAL, __func__,
+                          "Directory tree has bad magic");
+      emacs_abort ();
+    }
+
   /* Hold a VM reference to the asset manager to prevent the native
      object from being deleted.  */
   (*env)->NewGlobalRef (env, local_asset_manager);
@@ -1199,6 +1408,36 @@ android_init_emacs_drawable (void)
 #undef FIND_METHOD
 }
 
+static void
+android_init_emacs_window (void)
+{
+  jclass old;
+
+  window_class.class
+    = (*android_java_env)->FindClass (android_java_env,
+                                     "org/gnu/emacs/EmacsWindow");
+  eassert (window_class.class);
+
+  old = window_class.class;
+  window_class.class
+    = (jclass) (*android_java_env)->NewGlobalRef (android_java_env,
+                                                 (jobject) old);
+  ANDROID_DELETE_LOCAL_REF (old);
+
+  if (!window_class.class)
+    emacs_abort ();
+
+#define FIND_METHOD(c_name, name, signature)                   \
+  window_class.c_name                                          \
+    = (*android_java_env)->GetMethodID (android_java_env,      \
+                                       window_class.class,     \
+                                       name, signature);       \
+  assert (window_class.c_name);
+
+  FIND_METHOD (swap_buffers, "swapBuffers", "()V");
+#undef FIND_METHOD
+}
+
 extern JNIEXPORT void JNICALL
 NATIVE_NAME (initEmacs) (JNIEnv *env, jobject object, jarray argv)
 {
@@ -1232,6 +1471,7 @@ NATIVE_NAME (initEmacs) (JNIEnv *env, jobject object, 
jarray argv)
   android_init_emacs_pixmap ();
   android_init_graphics_point ();
   android_init_emacs_drawable ();
+  android_init_emacs_window ();
 
   /* Set HOME to the app data directory.  */
   setenv ("HOME", android_files_dir, 1);
@@ -1545,7 +1785,11 @@ NATIVE_NAME (sendContextMenu) (JNIEnv *env, jobject 
object,
   android_write_event (&event);
 }
 
+#ifdef __clang__
 #pragma clang diagnostic pop
+#else
+#pragma GCC diagnostic pop
+#endif
 
 
 
@@ -1557,8 +1801,6 @@ NATIVE_NAME (sendContextMenu) (JNIEnv *env, jobject 
object,
    This means that every local reference must be explicitly destroyed
    with DeleteLocalRef.  A helper macro is provided to do this.  */
 
-#define MAX_HANDLE 65535
-
 struct android_handle_entry
 {
   /* The type.  */
@@ -1883,7 +2125,7 @@ android_init_emacs_gc_class (void)
   emacs_gc_mark_dirty
     = (*android_java_env)->GetMethodID (android_java_env,
                                        emacs_gc_class,
-                                       "markDirty", "()V");
+                                       "markDirty", "(Z)V");
   assert (emacs_gc_mark_dirty);
 
   old = emacs_gc_class;
@@ -2011,6 +2253,9 @@ android_change_gc (struct android_gc *gc,
                   struct android_gc_values *values)
 {
   jobject what, gcontext;
+  jboolean clip_changed;
+
+  clip_changed = false;
 
   android_init_emacs_gc_class ();
   gcontext = android_resolve_handle (gc->gcontext,
@@ -2041,16 +2286,22 @@ android_change_gc (struct android_gc *gc,
                                      values->function);
 
   if (mask & ANDROID_GC_CLIP_X_ORIGIN)
-    (*android_java_env)->SetIntField (android_java_env,
-                                     gcontext,
-                                     emacs_gc_clip_x_origin,
-                                     values->clip_x_origin);
+    {
+      (*android_java_env)->SetIntField (android_java_env,
+                                       gcontext,
+                                       emacs_gc_clip_x_origin,
+                                       values->clip_x_origin);
+      clip_changed = true;
+    }
 
   if (mask & ANDROID_GC_CLIP_Y_ORIGIN)
-    (*android_java_env)->SetIntField (android_java_env,
-                                     gcontext,
-                                     emacs_gc_clip_y_origin,
-                                     values->clip_y_origin);
+    {
+      (*android_java_env)->SetIntField (android_java_env,
+                                       gcontext,
+                                       emacs_gc_clip_y_origin,
+                                       values->clip_y_origin);
+      clip_changed = true;
+    }
 
   if (mask & ANDROID_GC_CLIP_MASK)
     {
@@ -2070,6 +2321,7 @@ android_change_gc (struct android_gc *gc,
       xfree (gc->clip_rects);
       gc->clip_rects = NULL;
       gc->num_clip_rects = -1;
+      clip_changed = true;
     }
 
   if (mask & ANDROID_GC_STIPPLE)
@@ -2103,7 +2355,8 @@ android_change_gc (struct android_gc *gc,
   if (mask)
     (*android_java_env)->CallVoidMethod (android_java_env,
                                         gcontext,
-                                        emacs_gc_mark_dirty);
+                                        emacs_gc_mark_dirty,
+                                        (jboolean) clip_changed);
 }
 
 void
@@ -2174,7 +2427,8 @@ android_set_clip_rectangles (struct android_gc *gc, int 
clip_x_origin,
 
   (*android_java_env)->CallVoidMethod (android_java_env,
                                       gcontext,
-                                      emacs_gc_mark_dirty);
+                                      emacs_gc_mark_dirty,
+                                      (jboolean) true);
 
   /* Cache the clip rectangles on the C side for
      sfntfont-android.c.  */
@@ -2327,18 +2581,15 @@ android_swap_buffers (struct android_swap_info 
*swap_info,
                      int num_windows)
 {
   jobject window;
-  jmethodID swap_buffers;
   int i;
 
-  swap_buffers = android_lookup_method ("org/gnu/emacs/EmacsWindow",
-                                       "swapBuffers", "()V");
-
   for (i = 0; i < num_windows; ++i)
     {
       window = android_resolve_handle (swap_info[i].swap_window,
                                       ANDROID_HANDLE_WINDOW);
       (*android_java_env)->CallVoidMethod (android_java_env,
-                                          window, swap_buffers);
+                                          window,
+                                          window_class.swap_buffers);
     }
 }
 
@@ -3555,11 +3806,17 @@ android_sync (void)
 
 
 
+#if __ANDROID_API__ >= 17
+
 #undef faccessat
 
 /* Replace the system faccessat with one which understands AT_EACCESS.
    Android's faccessat simply fails upon using AT_EACCESS, so repalce
-   it with zero here.  This isn't caught during configuration.  */
+   it with zero here.  This isn't caught during configuration.
+
+   This replacement is only done when building for Android 17 or
+   later, because earlier versions use the gnulib replacement that
+   lacks these issues.  */
 
 int
 faccessat (int dirfd, const char *pathname, int mode, int flags)
@@ -3572,6 +3829,8 @@ faccessat (int dirfd, const char *pathname, int mode, int 
flags)
   return real_faccessat (dirfd, pathname, mode, flags & ~AT_EACCESS);
 }
 
+#endif /* __ANDROID_API__ < 16 */
+
 
 
 /* Directory listing emulation.  */
@@ -3581,8 +3840,11 @@ struct android_dir
   /* The real DIR *, if it exists.  */
   DIR *dir;
 
-  /* Otherwise, the AAssetDir.  */
-  void *asset_dir;
+  /* Otherwise, the pointer to the directory in directory_tree.  */
+  char *asset_dir;
+
+  /* And the end of the files in asset_dir.  */
+  char *asset_limit;
 };
 
 /* Like opendir.  However, return an asset directory if NAME points to
@@ -3592,8 +3854,9 @@ struct android_dir *
 android_opendir (const char *name)
 {
   struct android_dir *dir;
-  AAssetDir *asset_dir;
+  char *asset_dir;
   const char *asset_name;
+  size_t limit;
 
   asset_name = android_get_asset_name (name);
 
@@ -3601,8 +3864,9 @@ android_opendir (const char *name)
      directory.  */
   if (asset_manager && asset_name)
     {
-      asset_dir = AAssetManager_openDir (asset_manager,
-                                        asset_name);
+      asset_dir
+       = (char *) android_scan_directory_tree ((char *) asset_name,
+                                               &limit);
 
       if (!asset_dir)
        {
@@ -3613,6 +3877,20 @@ android_opendir (const char *name)
       dir = xmalloc (sizeof *dir);
       dir->dir = NULL;
       dir->asset_dir = asset_dir;
+      dir->asset_limit = (char *) directory_tree + limit;
+
+      /* Make sure dir->asset_limit is within bounds.  It is a limit,
+        and as such can be exactly one byte past directory_tree.  */
+      if (dir->asset_limit > directory_tree + directory_tree_size)
+       {
+         xfree (dir);
+         __android_log_print (ANDROID_LOG_VERBOSE, __func__,
+                              "Invalid dir tree, limit %zu, size %zu\n",
+                              limit, directory_tree_size);
+         dir = NULL;
+         errno = EACCES;
+       }
+
       return dir;
     }
 
@@ -3636,23 +3914,55 @@ struct dirent *
 android_readdir (struct android_dir *dir)
 {
   static struct dirent dirent;
-  const char *filename;
+  const char *last;
 
   if (dir->asset_dir)
     {
-      filename = AAssetDir_getNextFileName (dir->asset_dir);
-      errno = 0;
+      /* There are no more files to read.  */
+      if (dir->asset_dir >= dir->asset_limit)
+       return NULL;
+
+      /* Otherwise, scan forward looking for the next NULL byte.  */
+      last = memchr (dir->asset_dir, 0,
+                    dir->asset_limit - dir->asset_dir);
+
+      /* No more NULL bytes remain.  */
+      if (!last)
+       return NULL;
+
+      /* Forward last past the NULL byte.  */
+      last++;
 
-      if (!filename)
+      /* Make sure it is still within the directory tree.  */
+      if (last >= directory_tree + directory_tree_size)
        return NULL;
 
+      /* Now, fill in the dirent with the name.  */
       memset (&dirent, 0, sizeof dirent);
       dirent.d_ino = 0;
       dirent.d_off = 0;
       dirent.d_reclen = sizeof dirent;
       dirent.d_type = DT_UNKNOWN;
-      strncpy (dirent.d_name, filename,
-              sizeof dirent.d_name - 1);
+
+      /* Note that dir->asset_dir is actually a NULL terminated
+        string.  */
+      memcpy (dirent.d_name, dir->asset_dir,
+             MIN (sizeof dirent.d_name,
+                  last - dir->asset_dir));
+      dirent.d_name[sizeof dirent.d_name - 1] = '\0';
+
+      /* Strip off the trailing slash, if any.  */
+      if (dirent.d_name[MIN (sizeof dirent.d_name,
+                            last - dir->asset_dir)
+                       - 2] == '/')
+       dirent.d_name[MIN (sizeof dirent.d_name,
+                          last - dir->asset_dir)
+                     - 2] = '\0';
+
+      /* Finally, forward dir->asset_dir to the file past last.  */
+      dir->asset_dir = ((char *) directory_tree
+                       + android_extract_long ((char *) last));
+
       return &dirent;
     }
 
@@ -3666,8 +3976,9 @@ android_closedir (struct android_dir *dir)
 {
   if (dir->dir)
     closedir (dir->dir);
-  else
-    AAssetDir_close (dir->asset_dir);
+
+  /* There is no need to close anything else, as the directory tree
+     lies in statically allocated memory.  */
 
   xfree (dir);
 }
@@ -3795,40 +4106,40 @@ android_four_corners_bilinear (unsigned int tl, 
unsigned int tr,
                               unsigned int bl, unsigned int br,
                               int distx, int disty)
 {
-    int distxy, distxiy, distixy, distixiy;
-    uint32_t f, r;
-
-    distxy = distx * disty;
-    distxiy = (distx << 8) - distxy;
-    distixy = (disty << 8) - distxy;
-    distixiy = (256 * 256 - (disty << 8)
-               - (distx << 8) + distxy);
-
-    /* Red */
-    r = ((tl & 0x000000ff) * distixiy + (tr & 0x000000ff) * distxiy
-        + (bl & 0x000000ff) * distixy  + (br & 0x000000ff) * distxy);
-
-    /* Green */
-    f = ((tl & 0x0000ff00) * distixiy + (tr & 0x0000ff00) * distxiy
-        + (bl & 0x0000ff00) * distixy  + (br & 0x0000ff00) * distxy);
-    r |= f & 0xff000000;
-
-    /* Now do the upper two components.  */
-    tl >>= 16;
-    tr >>= 16;
-    bl >>= 16;
-    br >>= 16;
-    r >>= 16;
-
-    /* Blue */
-    f = ((tl & 0x000000ff) * distixiy + (tr & 0x000000ff) * distxiy
-        + (bl & 0x000000ff) * distixy  + (br & 0x000000ff) * distxy);
-    r |= f & 0x00ff0000;
-
-    /* Alpha */
-    f = ((tl & 0x0000ff00) * distixiy + (tr & 0x0000ff00) * distxiy
-        + (bl & 0x0000ff00) * distixy  + (br & 0x0000ff00) * distxy);
-    r |= f & 0xff000000;
+  int distxy, distxiy, distixy, distixiy;
+  uint32_t f, r;
+
+  distxy = distx * disty;
+  distxiy = (distx << 8) - distxy;
+  distixy = (disty << 8) - distxy;
+  distixiy = (256 * 256 - (disty << 8)
+             - (distx << 8) + distxy);
+
+  /* Red */
+  r = ((tl & 0x000000ff) * distixiy + (tr & 0x000000ff) * distxiy
+       + (bl & 0x000000ff) * distixy  + (br & 0x000000ff) * distxy);
+
+  /* Green */
+  f = ((tl & 0x0000ff00) * distixiy + (tr & 0x0000ff00) * distxiy
+       + (bl & 0x0000ff00) * distixy  + (br & 0x0000ff00) * distxy);
+  r |= f & 0xff000000;
+
+  /* Now do the upper two components.  */
+  tl >>= 16;
+  tr >>= 16;
+  bl >>= 16;
+  br >>= 16;
+  r >>= 16;
+
+  /* Blue */
+  f = ((tl & 0x000000ff) * distixiy + (tr & 0x000000ff) * distxiy
+       + (bl & 0x000000ff) * distixy  + (br & 0x000000ff) * distxy);
+  r |= f & 0x00ff0000;
+
+  /* Alpha */
+  f = ((tl & 0x0000ff00) * distixiy + (tr & 0x0000ff00) * distxiy
+       + (bl & 0x0000ff00) * distixy  + (br & 0x0000ff00) * distxy);
+  r |= f & 0xff000000;
 
   return r;
 }
@@ -4010,6 +4321,36 @@ android_project_image_nearest (struct android_image 
*image,
     }
 }
 
+
+
+/* System call wrappers for stuff missing in bionic.  */
+
+#ifndef HAVE_FTRUNCATE
+
+/* ftruncate wrapper for Android, for systems without ftruncate in the
+   C library.
+
+   Such systems are always 32 bit systems, since Android 21 and later
+   all support ftruncate.  In addition, ARM and MIPS require registers
+   used to store long long parameters to be aligned to an even
+   register pair.  */
+
+int
+android_ftruncate (int fd, off_t length)
+{
+  int rc;
+
+#if defined __arm__ || defined __mips__
+  return syscall (SYS_ftruncate64, fd, 0,
+                 (unsigned int) (length & 0xffffffff),
+                 (unsigned int) (length >> 32));
+#else
+  return syscall (SYS_ftruncate64, fd, length);
+#endif
+}
+
+#endif
+
 #else /* ANDROID_STUBIFY */
 
 /* X emulation functions for Android.  */
diff --git a/src/android.h b/src/android.h
index 036e6d266f..240bc90d83 100644
--- a/src/android.h
+++ b/src/android.h
@@ -102,6 +102,14 @@ extern struct android_dir *android_opendir (const char *);
 extern struct dirent *android_readdir (struct android_dir *);
 extern void android_closedir (struct android_dir *);
 
+#ifndef HAVE_FTRUNCATE
+extern int android_ftruncate (int, off_t);
+
+/* Replace calls to ftruncate with android_ftruncate when ftruncate is
+   not defined.  */
+#define ftruncate android_ftruncate
+#endif
+
 
 
 #endif
diff --git a/src/androidterm.c b/src/androidterm.c
index f19cee5b11..3c16b542d9 100644
--- a/src/androidterm.c
+++ b/src/androidterm.c
@@ -683,7 +683,7 @@ handle_one_android_event (struct android_display_info 
*dpyinfo,
              XSETFRAME (inev.ie.frame_or_window, f);
            }
          else
-           /* A new frame must be created.  */;
+           ((void) 0) /* A new frame must be created.  */;
        }
 
     case ANDROID_ENTER_NOTIFY:
@@ -988,6 +988,9 @@ handle_one_android_event (struct android_display_info 
*dpyinfo,
 
              if (!NILP (Vmouse_highlight))
                {
+                 /* Clear the pointer invisible flag to always make
+                    note_mouse_highlight do its thing.  */
+                 any->pointer_invisible = false;
                  note_mouse_highlight (any, x, y);
 
                  /* Always allow future mouse motion to
diff --git a/src/emacs.c b/src/emacs.c
index f4973c7061..994a4d1db9 100644
--- a/src/emacs.c
+++ b/src/emacs.c
@@ -423,7 +423,15 @@ using_utf8 (void)
      the result is known in advance anyway...  */
 #if defined HAVE_WCHAR_H && !defined WINDOWSNT
   wchar_t wc;
+#ifndef HAVE_ANDROID
   mbstate_t mbs = { 0 };
+#else
+  mbstate_t mbs;
+
+  /* Not sure how mbstate works on Android, but this seems to be
+     required.  */
+  memset (&mbs, 0, sizeof mbs);
+#endif
   return mbrtowc (&wc, "\xc4\x80", 2, &mbs) == 2 && wc == 0x100;
 #else
   return false;
diff --git a/src/fileio.c b/src/fileio.c
index 6fa524b3bb..b5a79312d8 100644
--- a/src/fileio.c
+++ b/src/fileio.c
@@ -6325,6 +6325,11 @@ effect except for flushing STREAM's data.  */)
 
 #ifndef DOS_NT
 
+#if defined STAT_STATFS2_BSIZE || defined STAT_STATFS2_FRSIZE  \
+  || defined STAT_STATFS2_FSIZE || defined STAT_STATFS3_OSF1   \
+  || defined STAT_STATFS4 || defined STAT_STATVFS              \
+  || defined STAT_STATVFS64
+
 /* Yield a Lisp number equal to BLOCKSIZE * BLOCKS, with the result
    negated if NEGATE.  */
 static Lisp_Object
@@ -6339,6 +6344,8 @@ blocks_to_bytes (uintmax_t blocksize, uintmax_t blocks, 
bool negate)
   return CALLN (Ftimes, bs, make_uint (blocks));
 }
 
+#endif
+
 DEFUN ("file-system-info", Ffile_system_info, Sfile_system_info, 1, 1, 0,
        doc: /* Return storage information about the file system FILENAME is on.
 Value is a list of numbers (TOTAL FREE AVAIL), where TOTAL is the total
@@ -6360,6 +6367,11 @@ If the underlying system call fails, value is nil.  */)
       error ("Invalid handler in `file-name-handler-alist'");
     }
 
+  /* Try to detect whether or not fsusage.o is actually built.  */
+#if defined STAT_STATFS2_BSIZE || defined STAT_STATFS2_FRSIZE  \
+  || defined STAT_STATFS2_FSIZE || defined STAT_STATFS3_OSF1   \
+  || defined STAT_STATFS4 || defined STAT_STATVFS              \
+  || defined STAT_STATVFS64
   struct fs_usage u;
   if (get_fs_usage (SSDATA (ENCODE_FILE (filename)), NULL, &u) != 0)
     return errno == ENOSYS ? Qnil : file_attribute_errno (filename, errno);
@@ -6367,6 +6379,9 @@ If the underlying system call fails, value is nil.  */)
                blocks_to_bytes (u.fsu_blocksize, u.fsu_bfree, false),
                blocks_to_bytes (u.fsu_blocksize, u.fsu_bavail,
                                 u.fsu_bavail_top_bit_set));
+#else
+  return Qnil;
+#endif
 }
 
 #endif /* !DOS_NT */
diff --git a/src/filelock.c b/src/filelock.c
index 51e1ffca9d..45eac5a19a 100644
--- a/src/filelock.c
+++ b/src/filelock.c
@@ -65,6 +65,12 @@ along with GNU Emacs.  If not, see 
<https://www.gnu.org/licenses/>.  */
 #define BOOT_TIME_FILE "/var/run/random-seed"
 #endif
 
+/* Boot time is not available on Android.  */
+
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+#undef BOOT_TIME
+#endif
+
 #if !defined WTMP_FILE && !defined WINDOWSNT && defined BOOT_TIME
 #define WTMP_FILE "/var/log/wtmp"
 #endif
@@ -120,12 +126,6 @@ along with GNU Emacs.  If not, see 
<https://www.gnu.org/licenses/>.  */
    * Non-forced locks on non-MS-Windows systems that support neither
      hard nor symbolic links.  */
 
-/* Boot time is not available on Android.  */
-
-#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
-#undef BOOT_TIME
-#endif
-
 
 /* Return the time of the last system boot.  */
 
diff --git a/src/frame.c b/src/frame.c
index 286c9a2cb7..e98256fe9e 100644
--- a/src/frame.c
+++ b/src/frame.c
@@ -5666,6 +5666,8 @@ On Nextstep, this just calls `ns-parse-geometry'.  */)
   int x UNINIT, y UNINIT;
   unsigned int width, height;
 
+  width = height = 0;
+
   CHECK_STRING (string);
 
 #ifdef HAVE_NS
diff --git a/src/keyboard.c b/src/keyboard.c
index 834049b496..11fa1e62c8 100644
--- a/src/keyboard.c
+++ b/src/keyboard.c
@@ -62,6 +62,10 @@ along with GNU Emacs.  If not, see 
<https://www.gnu.org/licenses/>.  */
 
 #include "syssignal.h"
 
+#if defined HAVE_STACK_OVERFLOW_HANDLING && !defined WINDOWSNT
+#include <setjmp.h>
+#endif
+
 #include <sys/types.h>
 #include <unistd.h>
 #include <fcntl.h>
@@ -4955,7 +4959,7 @@ const char *const lispy_function_keys[] =
     [278] = "copy",
     [279] = "paste",
     [28]  = "clear",
-    [4]          = "back",
+    [4]          = "XF86Back",
     [61]  = "tab",
     [66]  = "return",
     [67]  = "backspace",
diff --git a/src/menu.c b/src/menu.c
index e1f899858d..e02ee88011 100644
--- a/src/menu.c
+++ b/src/menu.c
@@ -1152,7 +1152,7 @@ x_popup_menu_1 (Lisp_Object position, Lisp_Object menu)
        else
          {
            menuflags |= MENU_FOR_CLICK;
-           tem = Fcar (XCDR (position));    /* EVENT_START (position) */
+           tem = EVENT_START (position);    /* EVENT_START (position) */
            window = Fcar (tem);             /* POSN_WINDOW (tem) */
            tem2 = Fcar (Fcdr (tem));        /* POSN_POSN (tem) */
            /* The MENU_KBD_NAVIGATION field is set when the menu
@@ -1466,9 +1466,10 @@ cached information about equivalent key sequences.
 If the user gets rid of the menu without making a valid choice, for
 instance by clicking the mouse away from a valid choice or by typing
 keyboard input, then this normally results in a quit and
-`x-popup-menu' does not return.  But if POSITION is a mouse button
-event (indicating that the user invoked the menu with the mouse) then
-no quit occurs and `x-popup-menu' returns nil.  */)
+`x-popup-menu' does not return.  But if POSITION is a mouse button or
+touch screen event (indicating that the user invoked the menu with the
+a pointing device) then no quit occurs and `x-popup-menu' returns
+nil.  */)
   (Lisp_Object position, Lisp_Object menu)
 {
   init_raw_keybuf_count ();
diff --git a/src/sfnt.c b/src/sfnt.c
index 6d58798c59..7300915a50 100644
--- a/src/sfnt.c
+++ b/src/sfnt.c
@@ -4624,8 +4624,14 @@ main (int argc, char **argv)
     }
 
   if (meta)
-    fprintf (stderr, "meta table with count: %"PRIu32"\n",
-            meta->num_data_maps);
+    {
+      fprintf (stderr, "meta table with count: %"PRIu32"\n",
+              meta->num_data_maps);
+
+      for (i = 0; i < meta->num_data_maps; ++i)
+       fprintf (stderr, "  meta tag: %"PRIx32"\n",
+                meta->data_maps[i].tag);
+    }
 
   loca_long = NULL;
   loca_short = NULL;
diff --git a/src/sfntfont-android.c b/src/sfntfont-android.c
index 1b01a4d9be..37fd81953f 100644
--- a/src/sfntfont-android.c
+++ b/src/sfntfont-android.c
@@ -310,7 +310,7 @@ sfntfont_android_put_glyphs (struct glyph_string *s, int 
from,
 
   /* Allocate enough to hold text_rectangle.height, aligned to 8
      bytes.  Then fill it with the background.  */
-  stride = (text_rectangle.width * sizeof *buffer) + 7 & ~7;
+  stride = ((text_rectangle.width * sizeof *buffer) + 7) & ~7;
   GET_SCANLINE_BUFFER (buffer, text_rectangle.height, stride);
   memset (buffer, 0, text_rectangle.height * stride);
 
@@ -546,6 +546,7 @@ init_sfntfont_android (void)
 {
   /* Make sure to pick the right Sans Serif font depending on what
      version of Android the device is running.  */
+#if HAVE_DECL_ANDROID_GET_DEVICE_API_LEVEL
   if (android_get_device_api_level () >= 15)
     Vsfnt_default_family_alist
       = list3 (Fcons (build_string ("Monospace"),
@@ -557,6 +558,7 @@ init_sfntfont_android (void)
               Fcons (build_string ("Sans Serif"),
                      build_string ("Roboto")));
   else
+#endif
     Vsfnt_default_family_alist
       = list3 (Fcons (build_string ("Monospace"),
                      build_string ("Droid Sans Mono")),
diff --git a/src/sfntfont.c b/src/sfntfont.c
index e2d18517fc..87f93473ff 100644
--- a/src/sfntfont.c
+++ b/src/sfntfont.c
@@ -21,6 +21,7 @@ Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 
USA.  */
 #include <config.h>
 
 #include <fcntl.h>
+#include <ctype.h>
 
 #include "lisp.h"
 
@@ -61,6 +62,12 @@ struct sfnt_font_desc
   /* Designer (foundry) of the font.  */
   Lisp_Object designer;
 
+  /* Style tokens that could not be parsed.  */
+  Lisp_Object adstyle;
+
+  /* List of design languages.  */
+  Lisp_Object languages;
+
   /* Numeric width, weight, slant and spacing.  */
   int width, weight, slant, spacing;
 
@@ -344,7 +351,9 @@ static struct sfnt_style_desc sfnt_width_descriptions[] =
   };
 
 /* Figure out DESC->width, DESC->weight, DESC->slant and DESC->spacing
-   based on the style name passed as STYLE_NAME.  */
+   based on the style name passed as STYLE_NAME.
+
+   Also append any unknown tokens to DESC->adstyle.  */
 
 static void
 sfnt_parse_style (Lisp_Object style_name, struct sfnt_font_desc *desc)
@@ -419,16 +428,85 @@ sfnt_parse_style (Lisp_Object style_name, struct 
sfnt_font_desc *desc)
            }
        }
 
-    next:
+      /* This token is extraneous or was not recognized.  Capitalize
+        the first letter and set it as the adstyle.  */
 
-      /* Break early if everything has been found.  */
-      if (desc->slant != 100 && desc->width != 100 && desc->weight != 80)
-       break;
+      if (strlen (single))
+       {
+         if (islower (single[0]))
+           single[0] = toupper (single[0]);
+
+         if (NILP (desc->adstyle))
+           desc->adstyle = build_string (single);
+         else
+           desc->adstyle = CALLN (Fconcat, desc->adstyle,
+                                  build_string (" "),
+                                  build_string (single));
+       }
 
+    next:
       continue;
     }
 }
 
+/* Parse the list of design languages in META, a font metadata table,
+   and place the results in DESC->languages.  Do nothing if there is
+   no such metadata.  */
+
+static void
+sfnt_parse_languages (struct sfnt_meta_table *meta,
+                     struct sfnt_font_desc *desc)
+{
+  char *data, *metadata, *tag;
+  struct sfnt_meta_data_map map;
+  char *saveptr;
+
+  /* Look up the ``design languages'' metadata.  This is a comma (and
+     possibly space) separated list of scripts that the font was
+     designed for.  Here is an example of one such tag:
+
+       zh-Hans,Jpan,Kore
+
+     for a font that covers Simplified Chinese, along with Japanese
+     and Korean text.  */
+
+  saveptr = NULL;
+  data = sfnt_find_metadata (meta, SFNT_META_DATA_TAG_DLNG,
+                            &map);
+
+  if (!data)
+    return;
+
+  USE_SAFE_ALLOCA;
+
+  /* Now copy metadata and add a trailing NULL byte.  */
+
+  if (map.data_length >= SIZE_MAX)
+    memory_full (SIZE_MAX);
+
+  metadata = SAFE_ALLOCA ((size_t) map.data_length + 1);
+  memcpy (metadata, data, map.data_length);
+  metadata[map.data_length] = '\0';
+
+  /* Loop through each script-language tag.  Note that there may be
+     extra leading spaces.  */
+  while ((tag = strtok_r (metadata, ",", &saveptr)))
+    {
+      metadata = NULL;
+
+      if (strstr (tag, "Hans") || strstr (tag, "Hant"))
+       desc->languages = Fcons (Qzh, desc->languages);
+
+      if (strstr (tag, "Japn"))
+       desc->languages = Fcons (Qja, desc->languages);
+
+      if (strstr (tag, "Kore"))
+       desc->languages = Fcons (Qko, desc->languages);
+    }
+
+  SAFE_FREE ();
+}
+
 /* Enumerate the offset subtable SUBTABLES in the file FD, whose file
    name is FILE.  OFFSET should be the offset of the subtable within
    the font file, and is recorded for future use.  Value is 1 upon
@@ -481,6 +559,10 @@ sfnt_enum_font_1 (int fd, const char *file,
   /* Parse the style.  */
   sfnt_parse_style (style, desc);
 
+  /* If the meta table exists, parse the list of design languages.  */
+  if (meta)
+    sfnt_parse_languages (meta, desc);
+
   /* Figure out the spacing.  Some fancy test like what Fontconfig
      does is probably in order but not really necessary.  */
   if (!NILP (Fstring_search (Fdowncase (family),
@@ -990,11 +1072,10 @@ sfntfont_list_1 (struct sfnt_font_desc *desc, 
Lisp_Object spec)
                                          desc->family)))
     return false;
 
-  /* Check that no adstyle has been specified.  That's a relic from
-     the Postscript era.  */
+  /* Check that the adstyle specified matches.  */
 
   tem = AREF (spec, FONT_ADSTYLE_INDEX);
-  if (!NILP (tem))
+  if (!NILP (tem) && NILP (Fequal (tem, desc->adstyle)))
     return false;
 
   /* Check the style.  */
@@ -1069,6 +1150,11 @@ sfntfont_list_1 (struct sfnt_font_desc *desc, 
Lisp_Object spec)
        }
     }
 
+  /* Now check that the language is supported.  */
+  tem = assq_no_quit (QClang, extra);
+  if (!NILP (tem) && NILP (Fmemq (tem, desc->languages)))
+    goto fail;
+
   /* Set desc->subtable if cmap was specified.  */
   if (cmap)
     desc->subtable = subtable;
@@ -2076,9 +2162,9 @@ sfntfont_text_extents (struct font *font, const unsigned 
int *code,
 
          if (pcm.descent > metrics->descent)
            metrics->descent = pcm.descent;
-       }
 
-      total_width += pcm.width;
+         total_width += pcm.width;
+       }
     }
 
   metrics->width = total_width;
@@ -2239,6 +2325,9 @@ syms_of_sfntfont (void)
   DEFSYM (Qapple_roman, "apple-roman");
   DEFSYM (Qjisx0208_1983_0, "jisx0208.1983-0");
   DEFSYM (Qksc5601_1987_0, "ksc5601.1987-0");
+  DEFSYM (Qzh, "zh");
+  DEFSYM (Qja, "ja");
+  DEFSYM (Qko, "ko");
 
   /* Char-table purpose.  */
   DEFSYM (Qfont_lookup_cache, "font-lookup-cache");
@@ -2269,6 +2358,8 @@ mark_sfntfont (void)
     {
       mark_object (desc->family);
       mark_object (desc->style);
+      mark_object (desc->adstyle);
+      mark_object (desc->languages);
       mark_object (desc->char_cache);
       mark_object (desc->designer);
     }
diff --git a/xcompile/lib/faccessat.c b/xcompile/lib/faccessat.c
index 807e266968..ac8977cfd6 100644
--- a/xcompile/lib/faccessat.c
+++ b/xcompile/lib/faccessat.c
@@ -43,11 +43,7 @@ orig_faccessat (int fd, char const *name, int mode, int flag)
 /* Write "unistd.h" here, not <unistd.h>, otherwise OSF/1 5.1 DTK cc
    eliminates this include because of the preliminary #include <unistd.h>
    above.  */
-#ifdef __ANROID__
-#include <unistd.h>
-#else
 #include "unistd.h"
-#endif
 
 #ifndef HAVE_ACCESS
 /* Mingw lacks access, but it also lacks real vs. effective ids, so
diff --git a/xcompile/lib/getdelim.c b/xcompile/lib/getdelim.c
new file mode 100644
index 0000000000..79ec3dd12a
--- /dev/null
+++ b/xcompile/lib/getdelim.c
@@ -0,0 +1,147 @@
+/* getdelim.c --- Implementation of replacement getdelim function.
+   Copyright (C) 1994, 1996-1998, 2001, 2003, 2005-2023 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/>.  */
+
+/* Ported from glibc by Simon Josefsson. */
+
+/* Don't use __attribute__ __nonnull__ in this compilation unit.  Otherwise gcc
+   optimizes away the lineptr == NULL || n == NULL || fp == NULL tests below.  
*/
+#define _GL_ARG_NONNULL(params)
+
+#include <config.h>
+
+#include <stdio.h>
+
+#include <limits.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#ifndef SSIZE_MAX
+# define SSIZE_MAX ((ssize_t) (SIZE_MAX / 2))
+#endif
+
+#if USE_UNLOCKED_IO
+# include "unlocked-io.h"
+# define getc_maybe_unlocked(fp)        getc(fp)
+#elif !HAVE_FLOCKFILE || !HAVE_FUNLOCKFILE || !HAVE_DECL_GETC_UNLOCKED
+# undef flockfile
+# undef funlockfile
+# define flockfile(x) ((void) 0)
+# define funlockfile(x) ((void) 0)
+# define getc_maybe_unlocked(fp)        getc(fp)
+#else
+# define getc_maybe_unlocked(fp)        getc_unlocked(fp)
+#endif
+
+static void
+alloc_failed (void)
+{
+#if defined _WIN32 && ! defined __CYGWIN__
+  /* Avoid errno problem without using the realloc module; see:
+     https://lists.gnu.org/r/bug-gnulib/2016-08/msg00025.html  */
+  errno = ENOMEM;
+#endif
+}
+
+/* Read up to (and including) a DELIMITER from FP into *LINEPTR (and
+   NUL-terminate it).  *LINEPTR is a pointer returned from malloc (or
+   NULL), pointing to *N characters of space.  It is realloc'ed as
+   necessary.  Returns the number of characters read (not including
+   the null terminator), or -1 on error or EOF.  */
+
+ssize_t
+getdelim (char **lineptr, size_t *n, int delimiter, FILE *fp)
+{
+  ssize_t result;
+  size_t cur_len = 0;
+
+  if (lineptr == NULL || n == NULL || fp == NULL)
+    {
+      errno = EINVAL;
+      return -1;
+    }
+
+  flockfile (fp);
+
+  if (*lineptr == NULL || *n == 0)
+    {
+      char *new_lineptr;
+      *n = 120;
+      new_lineptr = (char *) realloc (*lineptr, *n);
+      if (new_lineptr == NULL)
+        {
+          alloc_failed ();
+          result = -1;
+          goto unlock_return;
+        }
+      *lineptr = new_lineptr;
+    }
+
+  for (;;)
+    {
+      int i;
+
+      i = getc_maybe_unlocked (fp);
+      if (i == EOF)
+        {
+          result = -1;
+          break;
+        }
+
+      /* Make enough space for len+1 (for final NUL) bytes.  */
+      if (cur_len + 1 >= *n)
+        {
+          size_t needed_max =
+            SSIZE_MAX < SIZE_MAX ? (size_t) SSIZE_MAX + 1 : SIZE_MAX;
+          size_t needed = 2 * *n + 1;   /* Be generous. */
+          char *new_lineptr;
+
+          if (needed_max < needed)
+            needed = needed_max;
+          if (cur_len + 1 >= needed)
+            {
+              result = -1;
+              errno = EOVERFLOW;
+              goto unlock_return;
+            }
+
+          new_lineptr = (char *) realloc (*lineptr, needed);
+          if (new_lineptr == NULL)
+            {
+              alloc_failed ();
+              result = -1;
+              goto unlock_return;
+            }
+
+          *lineptr = new_lineptr;
+          *n = needed;
+        }
+
+      (*lineptr)[cur_len] = i;
+      cur_len++;
+
+      if (i == delimiter)
+        break;
+    }
+  (*lineptr)[cur_len] = '\0';
+  result = cur_len ? cur_len : result;
+
+ unlock_return:
+  funlockfile (fp); /* doesn't set errno */
+
+  return result;
+}
diff --git a/xcompile/lib/getline.c b/xcompile/lib/getline.c
new file mode 100644
index 0000000000..85f16ab8ba
--- /dev/null
+++ b/xcompile/lib/getline.c
@@ -0,0 +1,27 @@
+/* getline.c --- Implementation of replacement getline function.
+   Copyright (C) 2005-2007, 2009-2023 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/>.  */
+
+/* Written by Simon Josefsson. */
+
+#include <config.h>
+
+#include <stdio.h>
+
+ssize_t
+getline (char **lineptr, size_t *n, FILE *stream)
+{
+  return getdelim (lineptr, n, '\n', stream);
+}
diff --git a/xcompile/lib/gnulib.mk.in b/xcompile/lib/gnulib.mk.in
index 2ebf187e86..66ba4a4adf 100644
--- a/xcompile/lib/gnulib.mk.in
+++ b/xcompile/lib/gnulib.mk.in
@@ -109,6 +109,7 @@
 #  fsusage \
 #  fsync \
 #  futimens \
+#  getline \
 #  getloadavg \
 #  getopt-gnu \
 #  getrandom \
@@ -172,11 +173,19 @@
 
 MOSTLYCLEANFILES += core *.stackdump
 # Start of GNU Make output.
+AAPT = @AAPT@
 ALLOCA = @ALLOCA@
 ALLOCA_H = @ALLOCA_H@
 ALSA_CFLAGS = @ALSA_CFLAGS@
 ALSA_LIBS = @ALSA_LIBS@
 AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+ANDROID = @ANDROID@
+ANDROID_ABI = @ANDROID_ABI@
+ANDROID_CFLAGS = @ANDROID_CFLAGS@
+ANDROID_JAR = @ANDROID_JAR@
+ANDROID_LIBS = @ANDROID_LIBS@
+ANDROID_MIN_SDK = @ANDROID_MIN_SDK@
+ANDROID_OBJ = @ANDROID_OBJ@
 APPLE_UNIVERSAL_BUILD = @APPLE_UNIVERSAL_BUILD@
 AR = @AR@
 ARFLAGS = @ARFLAGS@
@@ -216,6 +225,7 @@ CYGWIN_OBJ = @CYGWIN_OBJ@
 C_SWITCH_MACHINE = @C_SWITCH_MACHINE@
 C_SWITCH_SYSTEM = @C_SWITCH_SYSTEM@
 C_SWITCH_X_SITE = @C_SWITCH_X_SITE@
+D8 = @D8@
 DBUS_CFLAGS = @DBUS_CFLAGS@
 DBUS_LIBS = @DBUS_LIBS@
 DBUS_OBJ = @DBUS_OBJ@
@@ -278,8 +288,10 @@ GL_COND_OBJ_FSTATAT_CONDITION = 
@GL_COND_OBJ_FSTATAT_CONDITION@
 GL_COND_OBJ_FSUSAGE_CONDITION = @GL_COND_OBJ_FSUSAGE_CONDITION@
 GL_COND_OBJ_FSYNC_CONDITION = @GL_COND_OBJ_FSYNC_CONDITION@
 GL_COND_OBJ_FUTIMENS_CONDITION = @GL_COND_OBJ_FUTIMENS_CONDITION@
+GL_COND_OBJ_GETDELIM_CONDITION = @GL_COND_OBJ_GETDELIM_CONDITION@
 GL_COND_OBJ_GETDTABLESIZE_CONDITION = @GL_COND_OBJ_GETDTABLESIZE_CONDITION@
 GL_COND_OBJ_GETGROUPS_CONDITION = @GL_COND_OBJ_GETGROUPS_CONDITION@
+GL_COND_OBJ_GETLINE_CONDITION = @GL_COND_OBJ_GETLINE_CONDITION@
 GL_COND_OBJ_GETLOADAVG_CONDITION = @GL_COND_OBJ_GETLOADAVG_CONDITION@
 GL_COND_OBJ_GETOPT_CONDITION = @GL_COND_OBJ_GETOPT_CONDITION@
 GL_COND_OBJ_GETRANDOM_CONDITION = @GL_COND_OBJ_GETRANDOM_CONDITION@
@@ -883,6 +895,8 @@ INSTALL_PROGRAM = @INSTALL_PROGRAM@
 INSTALL_SCRIPT = @INSTALL_SCRIPT@
 INT32_MAX_LT_INTMAX_MAX = @INT32_MAX_LT_INTMAX_MAX@
 INT64_MAX_EQ_LONG_MAX = @INT64_MAX_EQ_LONG_MAX@
+JARSIGNER = @JARSIGNER@
+JAVAC = @JAVAC@
 JSON_CFLAGS = @JSON_CFLAGS@
 JSON_LIBS = @JSON_LIBS@
 JSON_OBJ = @JSON_OBJ@
@@ -949,6 +963,7 @@ LIB_PTHREAD = @LIB_PTHREAD@
 LIB_PTHREAD_SIGMASK = @LIB_PTHREAD_SIGMASK@
 LIB_TIMER_TIME = @LIB_TIMER_TIME@
 LIB_WSOCK32 = @LIB_WSOCK32@
+LIB_XATTR = @LIB_XATTR@
 LIMITS_H = @LIMITS_H@
 LN_S_FILEONLY = @LN_S_FILEONLY@
 LTLIBGMP = @LTLIBGMP@
@@ -1041,6 +1056,7 @@ PROFILING_CFLAGS = @PROFILING_CFLAGS@
 PTHREAD_H_DEFINES_STRUCT_TIMESPEC = @PTHREAD_H_DEFINES_STRUCT_TIMESPEC@
 PTHREAD_SIGMASK_LIB = @PTHREAD_SIGMASK_LIB@
 PTRDIFF_T_SUFFIX = @PTRDIFF_T_SUFFIX@
+QCOPY_ACL_LIB = @QCOPY_ACL_LIB@
 RALLOC_OBJ = @RALLOC_OBJ@
 RANLIB = @RANLIB@
 REPLACE_ACCESS = @REPLACE_ACCESS@
@@ -1209,6 +1225,7 @@ REPLACE_WCTOMB = @REPLACE_WCTOMB@
 REPLACE_WRITE = @REPLACE_WRITE@
 RSVG_CFLAGS = @RSVG_CFLAGS@
 RSVG_LIBS = @RSVG_LIBS@
+SDK_BULD_TOOLS = @SDK_BULD_TOOLS@
 SEPCHAR = @SEPCHAR@
 SETFATTR = @SETFATTR@
 SETTINGS_CFLAGS = @SETTINGS_CFLAGS@
@@ -1266,6 +1283,7 @@ XARGS_LIMIT = @XARGS_LIMIT@
 XCB_LIBS = @XCB_LIBS@
 XCOMPOSITE_CFLAGS = @XCOMPOSITE_CFLAGS@
 XCOMPOSITE_LIBS = @XCOMPOSITE_LIBS@
+XCONFIGURE = @XCONFIGURE@
 XCRUN = @XCRUN@
 XDBE_CFLAGS = @XDBE_CFLAGS@
 XDBE_LIBS = @XDBE_LIBS@
@@ -1290,6 +1308,7 @@ XSYNC_CFLAGS = @XSYNC_CFLAGS@
 XSYNC_LIBS = @XSYNC_LIBS@
 XWIDGETS_OBJ = @XWIDGETS_OBJ@
 X_TOOLKIT_TYPE = @X_TOOLKIT_TYPE@
+ZIPALIGN = @ZIPALIGN@
 ac_ct_CC = @ac_ct_CC@
 ac_ct_CXX = @ac_ct_CXX@
 ac_ct_OBJC = @ac_ct_OBJC@
@@ -1335,6 +1354,7 @@ 
gl_GNULIB_ENABLED_e80bf6f757095d2e5fc94dafb8f8fc8b_CONDITION = @gl_GNULIB_ENABLE
 gl_GNULIB_ENABLED_ef455225c00f5049c808c2eda3e76866_CONDITION = 
@gl_GNULIB_ENABLED_ef455225c00f5049c808c2eda3e76866_CONDITION@
 gl_GNULIB_ENABLED_euidaccess_CONDITION = 
@gl_GNULIB_ENABLED_euidaccess_CONDITION@
 gl_GNULIB_ENABLED_fd38c7e463b54744b77b98aeafb4fa7c_CONDITION = 
@gl_GNULIB_ENABLED_fd38c7e463b54744b77b98aeafb4fa7c_CONDITION@
+gl_GNULIB_ENABLED_getdelim_CONDITION = @gl_GNULIB_ENABLED_getdelim_CONDITION@
 gl_GNULIB_ENABLED_getdtablesize_CONDITION = 
@gl_GNULIB_ENABLED_getdtablesize_CONDITION@
 gl_GNULIB_ENABLED_getgroups_CONDITION = @gl_GNULIB_ENABLED_getgroups_CONDITION@
 gl_GNULIB_ENABLED_lchmod_CONDITION = @gl_GNULIB_ENABLED_lchmod_CONDITION@
@@ -2091,6 +2111,18 @@ gl_V_at = $(AM_V_GEN)
 endif
 ## end   gnulib module gen-header
 
+## begin gnulib module getdelim
+ifeq (,$(OMIT_GNULIB_MODULE_getdelim))
+
+ifneq (,$(gl_GNULIB_ENABLED_getdelim_CONDITION))
+ifneq (,$(GL_COND_OBJ_GETDELIM_CONDITION))
+libgnu_a_SOURCES += getdelim.c
+endif
+
+endif
+endif
+## end   gnulib module getdelim
+
 ## begin gnulib module getdtablesize
 ifeq (,$(OMIT_GNULIB_MODULE_getdtablesize))
 
@@ -2115,6 +2147,16 @@ endif
 endif
 ## end   gnulib module getgroups
 
+## begin gnulib module getline
+ifeq (,$(OMIT_GNULIB_MODULE_getline))
+
+ifneq (,$(GL_COND_OBJ_GETLINE_CONDITION))
+libgnu_a_SOURCES += getline.c
+endif
+
+endif
+## end   gnulib module getline
+
 ## begin gnulib module getloadavg
 ifeq (,$(OMIT_GNULIB_MODULE_getloadavg))
 
diff --git a/xcompile/lib/qcopy-acl.c b/xcompile/lib/qcopy-acl.c
index 883bcf7d58..0f4159b7fd 100644
--- a/xcompile/lib/qcopy-acl.c
+++ b/xcompile/lib/qcopy-acl.c
@@ -23,6 +23,20 @@
 
 #include "acl-internal.h"
 
+#if USE_XATTR
+
+# include <attr/libattr.h>
+
+/* Returns 1 if NAME is the name of an extended attribute that is related
+   to permissions, i.e. ACLs.  Returns 0 otherwise.  */
+
+static int
+is_attr_permissions (const char *name, struct error_context *ctx)
+{
+  return attr_copy_action (name, ctx) == ATTR_ACTION_PERMISSIONS;
+}
+
+#endif  /* USE_XATTR */
 
 /* Copy access control lists from one file to another. If SOURCE_DESC is
    a valid file descriptor, use file descriptor operations, else use
@@ -39,13 +53,33 @@ int
 qcopy_acl (const char *src_name, int source_desc, const char *dst_name,
            int dest_desc, mode_t mode)
 {
-  struct permission_context ctx;
   int ret;
 
+#ifdef USE_XATTR
+  /* in case no ACLs present and also to set higher mode bits
+     we chmod before setting ACLs as doing it after could overwrite them
+     (especially true for NFSv4, posix ACL has that ugly "mask" hack that
+     nobody understands) */
+  ret = chmod_or_fchmod (dst_name, dest_desc, mode);
+  /* Rather than fiddling with acls one by one, we just copy the whole ACL 
xattrs
+     (Posix or NFSv4). Of course, that won't address ACLs conversion
+     (i.e. posix <-> nfs4) but we can't do it anyway, so for now, we don't care
+     Functions attr_copy_* return 0 in case we copied something OR nothing
+     to copy */
+  if (ret == 0)
+    ret = source_desc <= 0 || dest_desc <= 0
+      ? attr_copy_file (src_name, dst_name, is_attr_permissions, NULL)
+      : attr_copy_fd (src_name, source_desc, dst_name, dest_desc,
+                      is_attr_permissions, NULL);
+#else
+  /* no XATTR, so we proceed the old dusty way */
+  struct permission_context ctx;
+
   ret = get_permissions (src_name, source_desc, mode, &ctx);
   if (ret != 0)
     return -2;
   ret = set_permissions (&ctx, dst_name, dest_desc);
   free_permission_context (&ctx);
+#endif
   return ret;
 }
diff --git a/xcompile/lib/verify.h b/xcompile/lib/verify.h
index 17d6e78c81..b63cb26432 100644
--- a/xcompile/lib/verify.h
+++ b/xcompile/lib/verify.h
@@ -258,7 +258,9 @@ template <int w>
 
 /* @assert.h omit start@  */
 
-#if 3 < __GNUC__ + (3 < __GNUC_MINOR__ + (4 <= __GNUC_PATCHLEVEL__))
+#if defined __clang_major__ && __clang_major__ < 5
+# define _GL_HAS_BUILTIN_TRAP 0
+#elif 3 < __GNUC__ + (3 < __GNUC_MINOR__ + (4 <= __GNUC_PATCHLEVEL__))
 # define _GL_HAS_BUILTIN_TRAP 1
 #elif defined __has_builtin
 # define _GL_HAS_BUILTIN_TRAP __has_builtin (__builtin_trap)
@@ -266,7 +268,9 @@ template <int w>
 # define _GL_HAS_BUILTIN_TRAP 0
 #endif
 
-#if 4 < __GNUC__ + (5 <= __GNUC_MINOR__)
+#if defined __clang_major__ && __clang_major__ < 5
+# define _GL_HAS_BUILTIN_UNREACHABLE 0
+#elif 4 < __GNUC__ + (5 <= __GNUC_MINOR__)
 # define _GL_HAS_BUILTIN_UNREACHABLE 1
 #elif defined __has_builtin
 # define _GL_HAS_BUILTIN_UNREACHABLE __has_builtin (__builtin_unreachable)
diff --git a/xcompile/malloc/dynarray-skeleton.c 
b/xcompile/malloc/dynarray-skeleton.c
new file mode 100644
index 0000000000..580c278b7c
--- /dev/null
+++ b/xcompile/malloc/dynarray-skeleton.c
@@ -0,0 +1,528 @@
+/* Type-safe arrays which grow dynamically.
+   Copyright (C) 2017-2023 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library 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.
+
+   The GNU C Library 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 the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+/* Pre-processor macros which act as parameters:
+
+   DYNARRAY_STRUCT
+      The struct tag of dynamic array to be defined.
+   DYNARRAY_ELEMENT
+      The type name of the element type.  Elements are copied
+      as if by memcpy, and can change address as the dynamic
+      array grows.
+   DYNARRAY_PREFIX
+      The prefix of the functions which are defined.
+
+   The following parameters are optional:
+
+   DYNARRAY_ELEMENT_FREE
+      DYNARRAY_ELEMENT_FREE (E) is evaluated to deallocate the
+      contents of elements. E is of type  DYNARRAY_ELEMENT *.
+   DYNARRAY_ELEMENT_INIT
+      DYNARRAY_ELEMENT_INIT (E) is evaluated to initialize a new
+      element.  E is of type  DYNARRAY_ELEMENT *.
+      If DYNARRAY_ELEMENT_FREE but not DYNARRAY_ELEMENT_INIT is
+      defined, new elements are automatically zero-initialized.
+      Otherwise, new elements have undefined contents.
+   DYNARRAY_INITIAL_SIZE
+      The size of the statically allocated array (default:
+      at least 2, more elements if they fit into 128 bytes).
+      Must be a preprocessor constant.  If DYNARRAY_INITIAL_SIZE is 0,
+      there is no statically allocated array at, and all non-empty
+      arrays are heap-allocated.
+   DYNARRAY_FINAL_TYPE
+      The name of the type which holds the final array.  If not
+      defined, is PREFIX##finalize not provided.  DYNARRAY_FINAL_TYPE
+      must be a struct type, with members of type DYNARRAY_ELEMENT and
+      size_t at the start (in this order).
+
+   These macros are undefined after this header file has been
+   included.
+
+   The following types are provided (their members are private to the
+   dynarray implementation):
+
+     struct DYNARRAY_STRUCT
+
+   The following functions are provided:
+
+     void DYNARRAY_PREFIX##init (struct DYNARRAY_STRUCT *);
+     void DYNARRAY_PREFIX##free (struct DYNARRAY_STRUCT *);
+     bool DYNARRAY_PREFIX##has_failed (const struct DYNARRAY_STRUCT *);
+     void DYNARRAY_PREFIX##mark_failed (struct DYNARRAY_STRUCT *);
+     size_t DYNARRAY_PREFIX##size (const struct DYNARRAY_STRUCT *);
+     DYNARRAY_ELEMENT *DYNARRAY_PREFIX##begin (const struct DYNARRAY_STRUCT *);
+     DYNARRAY_ELEMENT *DYNARRAY_PREFIX##end (const struct DYNARRAY_STRUCT *);
+     DYNARRAY_ELEMENT *DYNARRAY_PREFIX##at (struct DYNARRAY_STRUCT *, size_t);
+     void DYNARRAY_PREFIX##add (struct DYNARRAY_STRUCT *, DYNARRAY_ELEMENT);
+     DYNARRAY_ELEMENT *DYNARRAY_PREFIX##emplace (struct DYNARRAY_STRUCT *);
+     bool DYNARRAY_PREFIX##resize (struct DYNARRAY_STRUCT *, size_t);
+     void DYNARRAY_PREFIX##remove_last (struct DYNARRAY_STRUCT *);
+     void DYNARRAY_PREFIX##clear (struct DYNARRAY_STRUCT *);
+
+   The following functions are provided are provided if the
+   prerequisites are met:
+
+     bool DYNARRAY_PREFIX##finalize (struct DYNARRAY_STRUCT *,
+                                     DYNARRAY_FINAL_TYPE *);
+       (if DYNARRAY_FINAL_TYPE is defined)
+     DYNARRAY_ELEMENT *DYNARRAY_PREFIX##finalize (struct DYNARRAY_STRUCT *,
+                                                  size_t *);
+       (if DYNARRAY_FINAL_TYPE is not defined)
+*/
+
+#include <malloc/dynarray.h>
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifndef DYNARRAY_STRUCT
+# error "DYNARRAY_STRUCT must be defined"
+#endif
+
+#ifndef DYNARRAY_ELEMENT
+# error "DYNARRAY_ELEMENT must be defined"
+#endif
+
+#ifndef DYNARRAY_PREFIX
+# error "DYNARRAY_PREFIX must be defined"
+#endif
+
+#ifdef DYNARRAY_INITIAL_SIZE
+# if DYNARRAY_INITIAL_SIZE < 0
+#  error "DYNARRAY_INITIAL_SIZE must be non-negative"
+# endif
+# if DYNARRAY_INITIAL_SIZE > 0
+#  define DYNARRAY_HAVE_SCRATCH 1
+# else
+#  define DYNARRAY_HAVE_SCRATCH 0
+# endif
+#else
+/* Provide a reasonable default which limits the size of
+   DYNARRAY_STRUCT.  */
+# define DYNARRAY_INITIAL_SIZE \
+  (sizeof (DYNARRAY_ELEMENT) > 64 ? 2 : 128 / sizeof (DYNARRAY_ELEMENT))
+# define DYNARRAY_HAVE_SCRATCH 1
+#endif
+
+/* Public type definitions.  */
+
+/* All fields of this struct are private to the implementation.  */
+struct DYNARRAY_STRUCT
+{
+  union
+  {
+    struct dynarray_header dynarray_abstract;
+    struct
+    {
+      /* These fields must match struct dynarray_header.  */
+      size_t used;
+      size_t allocated;
+      DYNARRAY_ELEMENT *array;
+    } dynarray_header;
+  } u;
+
+#if DYNARRAY_HAVE_SCRATCH
+  /* Initial inline allocation.  */
+  DYNARRAY_ELEMENT scratch[DYNARRAY_INITIAL_SIZE];
+#endif
+};
+
+/* Internal use only: Helper macros.  */
+
+/* Ensure macro-expansion of DYNARRAY_PREFIX.  */
+#define DYNARRAY_CONCAT0(prefix, name) prefix##name
+#define DYNARRAY_CONCAT1(prefix, name) DYNARRAY_CONCAT0(prefix, name)
+#define DYNARRAY_NAME(name) DYNARRAY_CONCAT1(DYNARRAY_PREFIX, name)
+
+/* Use DYNARRAY_FREE instead of DYNARRAY_NAME (free),
+   so that Gnulib does not change 'free' to 'rpl_free'.  */
+#define DYNARRAY_FREE DYNARRAY_CONCAT1 (DYNARRAY_NAME (f), ree)
+
+/* Address of the scratch buffer if any.  */
+#if DYNARRAY_HAVE_SCRATCH
+# define DYNARRAY_SCRATCH(list) (list)->scratch
+#else
+# define DYNARRAY_SCRATCH(list) NULL
+#endif
+
+/* Internal use only: Helper functions.  */
+
+/* Internal function.  Call DYNARRAY_ELEMENT_FREE with the array
+   elements.  Name mangling needed due to the DYNARRAY_ELEMENT_FREE
+   macro expansion.  */
+static inline void
+DYNARRAY_NAME (free__elements__) (DYNARRAY_ELEMENT *__dynarray_array,
+                                  size_t __dynarray_used)
+{
+#ifdef DYNARRAY_ELEMENT_FREE
+  for (size_t __dynarray_i = 0; __dynarray_i < __dynarray_used; ++__dynarray_i)
+    DYNARRAY_ELEMENT_FREE (&__dynarray_array[__dynarray_i]);
+#endif /* DYNARRAY_ELEMENT_FREE */
+}
+
+/* Internal function.  Free the non-scratch array allocation.  */
+static inline void
+DYNARRAY_NAME (free__array__) (struct DYNARRAY_STRUCT *list)
+{
+#if DYNARRAY_HAVE_SCRATCH
+  if (list->u.dynarray_header.array != list->scratch)
+    free (list->u.dynarray_header.array);
+#else
+  free (list->u.dynarray_header.array);
+#endif
+}
+
+/* Public functions.  */
+
+/* Initialize a dynamic array object.  This must be called before any
+   use of the object.  */
+__attribute_nonnull__ ((1))
+static void
+DYNARRAY_NAME (init) (struct DYNARRAY_STRUCT *list)
+{
+  list->u.dynarray_header.used = 0;
+  list->u.dynarray_header.allocated = DYNARRAY_INITIAL_SIZE;
+  list->u.dynarray_header.array = DYNARRAY_SCRATCH (list);
+}
+
+/* Deallocate the dynamic array and its elements.  */
+__attribute_maybe_unused__ __attribute_nonnull__ ((1))
+static void
+DYNARRAY_FREE (struct DYNARRAY_STRUCT *list)
+{
+  DYNARRAY_NAME (free__elements__)
+    (list->u.dynarray_header.array, list->u.dynarray_header.used);
+  DYNARRAY_NAME (free__array__) (list);
+  DYNARRAY_NAME (init) (list);
+}
+
+/* Return true if the dynamic array is in an error state.  */
+__attribute_nonnull__ ((1))
+static inline bool
+DYNARRAY_NAME (has_failed) (const struct DYNARRAY_STRUCT *list)
+{
+  return list->u.dynarray_header.allocated == __dynarray_error_marker ();
+}
+
+/* Mark the dynamic array as failed.  All elements are deallocated as
+   a side effect.  */
+__attribute_nonnull__ ((1))
+static void
+DYNARRAY_NAME (mark_failed) (struct DYNARRAY_STRUCT *list)
+{
+  DYNARRAY_NAME (free__elements__)
+    (list->u.dynarray_header.array, list->u.dynarray_header.used);
+  DYNARRAY_NAME (free__array__) (list);
+  list->u.dynarray_header.array = DYNARRAY_SCRATCH (list);
+  list->u.dynarray_header.used = 0;
+  list->u.dynarray_header.allocated = __dynarray_error_marker ();
+}
+
+/* Return the number of elements which have been added to the dynamic
+   array.  */
+__attribute_nonnull__ ((1))
+static inline size_t
+DYNARRAY_NAME (size) (const struct DYNARRAY_STRUCT *list)
+{
+  return list->u.dynarray_header.used;
+}
+
+/* Return a pointer to the array element at INDEX.  Terminate the
+   process if INDEX is out of bounds.  */
+__attribute_nonnull__ ((1))
+static inline DYNARRAY_ELEMENT *
+DYNARRAY_NAME (at) (struct DYNARRAY_STRUCT *list, size_t index)
+{
+  if (__glibc_unlikely (index >= DYNARRAY_NAME (size) (list)))
+    __libc_dynarray_at_failure (DYNARRAY_NAME (size) (list), index);
+  return list->u.dynarray_header.array + index;
+}
+
+/* Return a pointer to the first array element, if any.  For a
+   zero-length array, the pointer can be NULL even though the dynamic
+   array has not entered the failure state.  */
+__attribute_nonnull__ ((1))
+static inline DYNARRAY_ELEMENT *
+DYNARRAY_NAME (begin) (struct DYNARRAY_STRUCT *list)
+{
+  return list->u.dynarray_header.array;
+}
+
+/* Return a pointer one element past the last array element.  For a
+   zero-length array, the pointer can be NULL even though the dynamic
+   array has not entered the failure state.  */
+__attribute_nonnull__ ((1))
+static inline DYNARRAY_ELEMENT *
+DYNARRAY_NAME (end) (struct DYNARRAY_STRUCT *list)
+{
+  return list->u.dynarray_header.array + list->u.dynarray_header.used;
+}
+
+/* Internal function.  Slow path for the add function below.  */
+static void
+DYNARRAY_NAME (add__) (struct DYNARRAY_STRUCT *list, DYNARRAY_ELEMENT item)
+{
+  if (__glibc_unlikely
+      (!__libc_dynarray_emplace_enlarge (&list->u.dynarray_abstract,
+                                         DYNARRAY_SCRATCH (list),
+                                         sizeof (DYNARRAY_ELEMENT))))
+    {
+      DYNARRAY_NAME (mark_failed) (list);
+      return;
+    }
+
+  /* Copy the new element and increase the array length.  */
+  list->u.dynarray_header.array[list->u.dynarray_header.used++] = item;
+}
+
+/* Add ITEM at the end of the array, enlarging it by one element.
+   Mark *LIST as failed if the dynamic array allocation size cannot be
+   increased.  */
+__attribute_nonnull__ ((1))
+static inline void
+DYNARRAY_NAME (add) (struct DYNARRAY_STRUCT *list, DYNARRAY_ELEMENT item)
+{
+  /* Do nothing in case of previous error.  */
+  if (DYNARRAY_NAME (has_failed) (list))
+    return;
+
+  /* Enlarge the array if necessary.  */
+  if (__glibc_unlikely (list->u.dynarray_header.used
+                        == list->u.dynarray_header.allocated))
+    {
+      DYNARRAY_NAME (add__) (list, item);
+      return;
+    }
+
+  /* Copy the new element and increase the array length.  */
+  list->u.dynarray_header.array[list->u.dynarray_header.used++] = item;
+}
+
+/* Internal function.  Building block for the emplace functions below.
+   Assumes space for one more element in *LIST.  */
+static inline DYNARRAY_ELEMENT *
+DYNARRAY_NAME (emplace__tail__) (struct DYNARRAY_STRUCT *list)
+{
+  DYNARRAY_ELEMENT *result
+    = &list->u.dynarray_header.array[list->u.dynarray_header.used];
+  ++list->u.dynarray_header.used;
+#if defined (DYNARRAY_ELEMENT_INIT)
+  DYNARRAY_ELEMENT_INIT (result);
+#elif defined (DYNARRAY_ELEMENT_FREE)
+  memset (result, 0, sizeof (*result));
+#endif
+  return result;
+}
+
+/* Internal function.  Slow path for the emplace function below.  */
+static DYNARRAY_ELEMENT *
+DYNARRAY_NAME (emplace__) (struct DYNARRAY_STRUCT *list)
+{
+  if (__glibc_unlikely
+      (!__libc_dynarray_emplace_enlarge (&list->u.dynarray_abstract,
+                                         DYNARRAY_SCRATCH (list),
+                                         sizeof (DYNARRAY_ELEMENT))))
+    {
+      DYNARRAY_NAME (mark_failed) (list);
+      return NULL;
+    }
+  return DYNARRAY_NAME (emplace__tail__) (list);
+}
+
+/* Allocate a place for a new element in *LIST and return a pointer to
+   it.  The pointer can be NULL if the dynamic array cannot be
+   enlarged due to a memory allocation failure.  */
+__attribute_maybe_unused__ __attribute_warn_unused_result__
+__attribute_nonnull__ ((1))
+static
+/* Avoid inlining with the larger initialization code.  */
+#if !(defined (DYNARRAY_ELEMENT_INIT) || defined (DYNARRAY_ELEMENT_FREE))
+inline
+#endif
+DYNARRAY_ELEMENT *
+DYNARRAY_NAME (emplace) (struct DYNARRAY_STRUCT *list)
+{
+  /* Do nothing in case of previous error.  */
+  if (DYNARRAY_NAME (has_failed) (list))
+    return NULL;
+
+  /* Enlarge the array if necessary.  */
+  if (__glibc_unlikely (list->u.dynarray_header.used
+                        == list->u.dynarray_header.allocated))
+    return (DYNARRAY_NAME (emplace__) (list));
+  return DYNARRAY_NAME (emplace__tail__) (list);
+}
+
+/* Change the size of *LIST to SIZE.  If SIZE is larger than the
+   existing size, new elements are added (which can be initialized).
+   Otherwise, the list is truncated, and elements are freed.  Return
+   false on memory allocation failure (and mark *LIST as failed).  */
+__attribute_maybe_unused__ __attribute_nonnull__ ((1))
+static bool
+DYNARRAY_NAME (resize) (struct DYNARRAY_STRUCT *list, size_t size)
+{
+  if (size > list->u.dynarray_header.used)
+    {
+      bool ok;
+#if defined (DYNARRAY_ELEMENT_INIT)
+      /* The new elements have to be initialized.  */
+      size_t old_size = list->u.dynarray_header.used;
+      ok = __libc_dynarray_resize (&list->u.dynarray_abstract,
+                                   size, DYNARRAY_SCRATCH (list),
+                                   sizeof (DYNARRAY_ELEMENT));
+      if (ok)
+        for (size_t i = old_size; i < size; ++i)
+          {
+            DYNARRAY_ELEMENT_INIT (&list->u.dynarray_header.array[i]);
+          }
+#elif defined (DYNARRAY_ELEMENT_FREE)
+      /* Zero initialization is needed so that the elements can be
+         safely freed.  */
+      ok = __libc_dynarray_resize_clear
+        (&list->u.dynarray_abstract, size,
+         DYNARRAY_SCRATCH (list), sizeof (DYNARRAY_ELEMENT));
+#else
+      ok =  __libc_dynarray_resize (&list->u.dynarray_abstract,
+                                    size, DYNARRAY_SCRATCH (list),
+                                    sizeof (DYNARRAY_ELEMENT));
+#endif
+      if (__glibc_unlikely (!ok))
+        DYNARRAY_NAME (mark_failed) (list);
+      return ok;
+    }
+  else
+    {
+      /* The list has shrunk in size.  Free the removed elements.  */
+      DYNARRAY_NAME (free__elements__)
+        (list->u.dynarray_header.array + size,
+         list->u.dynarray_header.used - size);
+      list->u.dynarray_header.used = size;
+      return true;
+    }
+}
+
+/* Remove the last element of LIST if it is present.  */
+__attribute_maybe_unused__ __attribute_nonnull__ ((1))
+static void
+DYNARRAY_NAME (remove_last) (struct DYNARRAY_STRUCT *list)
+{
+  /* used > 0 implies that the array is the non-failed state.  */
+  if (list->u.dynarray_header.used > 0)
+    {
+      size_t new_length = list->u.dynarray_header.used - 1;
+#ifdef DYNARRAY_ELEMENT_FREE
+      DYNARRAY_ELEMENT_FREE (&list->u.dynarray_header.array[new_length]);
+#endif
+      list->u.dynarray_header.used = new_length;
+    }
+}
+
+/* Remove all elements from the list.  The elements are freed, but the
+   list itself is not.  */
+__attribute_maybe_unused__ __attribute_nonnull__ ((1))
+static void
+DYNARRAY_NAME (clear) (struct DYNARRAY_STRUCT *list)
+{
+  /* free__elements__ does nothing if the list is in the failed
+     state.  */
+  DYNARRAY_NAME (free__elements__)
+    (list->u.dynarray_header.array, list->u.dynarray_header.used);
+  list->u.dynarray_header.used = 0;
+}
+
+#ifdef DYNARRAY_FINAL_TYPE
+/* Transfer the dynamic array to a permanent location at *RESULT.
+   Returns true on success on false on allocation failure.  In either
+   case, *LIST is re-initialized and can be reused.  A NULL pointer is
+   stored in *RESULT if LIST refers to an empty list.  On success, the
+   pointer in *RESULT is heap-allocated and must be deallocated using
+   free.  */
+__attribute_maybe_unused__ __attribute_warn_unused_result__
+__attribute_nonnull__ ((1, 2))
+static bool
+DYNARRAY_NAME (finalize) (struct DYNARRAY_STRUCT *list,
+                          DYNARRAY_FINAL_TYPE *result)
+{
+  struct dynarray_finalize_result res;
+  if (__libc_dynarray_finalize (&list->u.dynarray_abstract,
+                                DYNARRAY_SCRATCH (list),
+                                sizeof (DYNARRAY_ELEMENT), &res))
+    {
+      /* On success, the result owns all the data.  */
+      DYNARRAY_NAME (init) (list);
+      *result = (DYNARRAY_FINAL_TYPE) { res.array, res.length };
+      return true;
+    }
+  else
+    {
+      /* On error, we need to free all data.  */
+      DYNARRAY_FREE (list);
+      errno = ENOMEM;
+      return false;
+    }
+}
+#else /* !DYNARRAY_FINAL_TYPE */
+/* Transfer the dynamic array to a heap-allocated array and return a
+   pointer to it.  The pointer is NULL if memory allocation fails, or
+   if the array is empty, so this function should be used only for
+   arrays which are known not be empty (usually because they always
+   have a sentinel at the end).  If LENGTHP is not NULL, the array
+   length is written to *LENGTHP.  *LIST is re-initialized and can be
+   reused.  */
+__attribute_maybe_unused__ __attribute_warn_unused_result__
+__attribute_nonnull__ ((1))
+static DYNARRAY_ELEMENT *
+DYNARRAY_NAME (finalize) (struct DYNARRAY_STRUCT *list, size_t *lengthp)
+{
+  struct dynarray_finalize_result res;
+  if (__libc_dynarray_finalize (&list->u.dynarray_abstract,
+                                DYNARRAY_SCRATCH (list),
+                                sizeof (DYNARRAY_ELEMENT), &res))
+    {
+      /* On success, the result owns all the data.  */
+      DYNARRAY_NAME (init) (list);
+      if (lengthp != NULL)
+        *lengthp = res.length;
+      return res.array;
+    }
+  else
+    {
+      /* On error, we need to free all data.  */
+      DYNARRAY_FREE (list);
+      errno = ENOMEM;
+      return NULL;
+    }
+}
+#endif /* !DYNARRAY_FINAL_TYPE */
+
+/* Undo macro definitions.  */
+
+#undef DYNARRAY_CONCAT0
+#undef DYNARRAY_CONCAT1
+#undef DYNARRAY_NAME
+#undef DYNARRAY_SCRATCH
+#undef DYNARRAY_HAVE_SCRATCH
+
+#undef DYNARRAY_STRUCT
+#undef DYNARRAY_ELEMENT
+#undef DYNARRAY_PREFIX
+#undef DYNARRAY_ELEMENT_FREE
+#undef DYNARRAY_ELEMENT_INIT
+#undef DYNARRAY_INITIAL_SIZE
+#undef DYNARRAY_FINAL_TYPE
diff --git a/xcompile/malloc/dynarray.h b/xcompile/malloc/dynarray.h
new file mode 100644
index 0000000000..a9a3b0859c
--- /dev/null
+++ b/xcompile/malloc/dynarray.h
@@ -0,0 +1,177 @@
+/* Type-safe arrays which grow dynamically.  Shared definitions.
+   Copyright (C) 2017-2023 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library 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.
+
+   The GNU C Library 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 the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+/* To use the dynarray facility, you need to include
+   <malloc/dynarray-skeleton.c> and define the parameter macros
+   documented in that file.
+
+   A minimal example which provides a growing list of integers can be
+   defined like this:
+
+     struct int_array
+     {
+       // Pointer to result array followed by its length,
+       // as required by DYNARRAY_FINAL_TYPE.
+       int *array;
+       size_t length;
+     };
+
+     #define DYNARRAY_STRUCT dynarray_int
+     #define DYNARRAY_ELEMENT int
+     #define DYNARRAY_PREFIX dynarray_int_
+     #define DYNARRAY_FINAL_TYPE struct int_array
+     #include <malloc/dynarray-skeleton.c>
+
+   To create a three-element array with elements 1, 2, 3, use this
+   code:
+
+     struct dynarray_int dyn;
+     dynarray_int_init (&dyn);
+     for (int i = 1; i <= 3; ++i)
+       {
+         int *place = dynarray_int_emplace (&dyn);
+         assert (place != NULL);
+         *place = i;
+       }
+     struct int_array result;
+     bool ok = dynarray_int_finalize (&dyn, &result);
+     assert (ok);
+     assert (result.length == 3);
+     assert (result.array[0] == 1);
+     assert (result.array[1] == 2);
+     assert (result.array[2] == 3);
+     free (result.array);
+
+   If the elements contain resources which must be freed, define
+   DYNARRAY_ELEMENT_FREE appropriately, like this:
+
+     struct str_array
+     {
+       char **array;
+       size_t length;
+     };
+
+     #define DYNARRAY_STRUCT dynarray_str
+     #define DYNARRAY_ELEMENT char *
+     #define DYNARRAY_ELEMENT_FREE(ptr) free (*ptr)
+     #define DYNARRAY_PREFIX dynarray_str_
+     #define DYNARRAY_FINAL_TYPE struct str_array
+     #include <malloc/dynarray-skeleton.c>
+
+   Compared to scratch buffers, dynamic arrays have the following
+   features:
+
+   - They have an element type, and are not just an untyped buffer of
+     bytes.
+
+   - When growing, previously stored elements are preserved.  (It is
+     expected that scratch_buffer_grow_preserve and
+     scratch_buffer_set_array_size eventually go away because all
+     current users are moved to dynamic arrays.)
+
+   - Scratch buffers have a more aggressive growth policy because
+     growing them typically means a retry of an operation (across an
+     NSS service module boundary), which is expensive.
+
+   - For the same reason, scratch buffers have a much larger initial
+     stack allocation.  */
+
+#ifndef _DYNARRAY_H
+#define _DYNARRAY_H
+
+#include <stddef.h>
+#include <string.h>
+
+struct dynarray_header
+{
+  size_t used;
+  size_t allocated;
+  void *array;
+};
+
+/* Marker used in the allocated member to indicate that an error was
+   encountered.  */
+static inline size_t
+__dynarray_error_marker (void)
+{
+  return -1;
+}
+
+/* Internal function.  See the has_failed function in
+   dynarray-skeleton.c.  */
+static inline bool
+__dynarray_error (struct dynarray_header *list)
+{
+  return list->allocated == __dynarray_error_marker ();
+}
+
+/* Internal function.  Enlarge the dynamically allocated area of the
+   array to make room for one more element.  SCRATCH is a pointer to
+   the scratch area (which is not heap-allocated and must not be
+   freed).  ELEMENT_SIZE is the size, in bytes, of one element.
+   Return false on failure, true on success.  */
+bool __libc_dynarray_emplace_enlarge (struct dynarray_header *,
+                                      void *scratch, size_t element_size);
+
+/* Internal function.  Enlarge the dynamically allocated area of the
+   array to make room for at least SIZE elements (which must be larger
+   than the existing used part of the dynamic array).  SCRATCH is a
+   pointer to the scratch area (which is not heap-allocated and must
+   not be freed).  ELEMENT_SIZE is the size, in bytes, of one element.
+   Return false on failure, true on success.  */
+bool __libc_dynarray_resize (struct dynarray_header *, size_t size,
+                             void *scratch, size_t element_size);
+
+/* Internal function.  Like __libc_dynarray_resize, but clear the new
+   part of the dynamic array.  */
+bool __libc_dynarray_resize_clear (struct dynarray_header *, size_t size,
+                                   void *scratch, size_t element_size);
+
+/* Internal type.  */
+struct dynarray_finalize_result
+{
+  void *array;
+  size_t length;
+};
+
+/* Internal function.  Copy the dynamically-allocated area to an
+   explicitly-sized heap allocation.  SCRATCH is a pointer to the
+   embedded scratch space.  ELEMENT_SIZE is the size, in bytes, of the
+   element type.  On success, true is returned, and pointer and length
+   are written to *RESULT.  On failure, false is returned.  The caller
+   has to take care of some of the memory management; this function is
+   expected to be called from dynarray-skeleton.c.  */
+bool __libc_dynarray_finalize (struct dynarray_header *list, void *scratch,
+                               size_t element_size,
+                               struct dynarray_finalize_result *result);
+
+
+/* Internal function.  Terminate the process after an index error.
+   SIZE is the number of elements of the dynamic array.  INDEX is the
+   lookup index which triggered the failure.  */
+_Noreturn void __libc_dynarray_at_failure (size_t size, size_t index);
+
+#ifndef _ISOMAC
+libc_hidden_proto (__libc_dynarray_emplace_enlarge)
+libc_hidden_proto (__libc_dynarray_resize)
+libc_hidden_proto (__libc_dynarray_resize_clear)
+libc_hidden_proto (__libc_dynarray_finalize)
+libc_hidden_proto (__libc_dynarray_at_failure)
+#endif
+
+#endif /* _DYNARRAY_H */
diff --git a/xcompile/malloc/dynarray_at_failure.c 
b/xcompile/malloc/dynarray_at_failure.c
new file mode 100644
index 0000000000..ebc9310982
--- /dev/null
+++ b/xcompile/malloc/dynarray_at_failure.c
@@ -0,0 +1,40 @@
+/* Report an dynamic array index out of bounds condition.
+   Copyright (C) 2017-2023 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library 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.
+
+   The GNU C Library 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 the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#ifndef _LIBC
+# include <libc-config.h>
+# include <stdlib.h>
+#endif
+
+#include <dynarray.h>
+#include <stdio.h>
+
+void
+__libc_dynarray_at_failure (size_t size, size_t index)
+{
+#ifdef _LIBC
+  char buf[200];
+  __snprintf (buf, sizeof (buf), "Fatal glibc error: "
+              "array index %zu not less than array length %zu\n",
+              index, size);
+  __libc_fatal (buf);
+#else
+ abort ();
+#endif
+}
+libc_hidden_def (__libc_dynarray_at_failure)
diff --git a/xcompile/malloc/dynarray_emplace_enlarge.c 
b/xcompile/malloc/dynarray_emplace_enlarge.c
new file mode 100644
index 0000000000..7da539316c
--- /dev/null
+++ b/xcompile/malloc/dynarray_emplace_enlarge.c
@@ -0,0 +1,77 @@
+/* Increase the size of a dynamic array in preparation of an emplace operation.
+   Copyright (C) 2017-2023 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library 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.
+
+   The GNU C Library 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 the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#ifndef _LIBC
+# include <libc-config.h>
+#endif
+
+#include <dynarray.h>
+#include <errno.h>
+#include <intprops.h>
+#include <stdlib.h>
+#include <string.h>
+
+bool
+__libc_dynarray_emplace_enlarge (struct dynarray_header *list,
+                                 void *scratch, size_t element_size)
+{
+  size_t new_allocated;
+  if (list->allocated == 0)
+    {
+      /* No scratch buffer provided.  Choose a reasonable default
+         size.  */
+      if (element_size < 4)
+        new_allocated = 16;
+      else if (element_size < 8)
+        new_allocated = 8;
+      else
+        new_allocated = 4;
+    }
+  else
+    /* Increase the allocated size, using an exponential growth
+       policy.  */
+    {
+      new_allocated = list->allocated + list->allocated / 2 + 1;
+      if (new_allocated <= list->allocated)
+        {
+          /* Overflow.  */
+          __set_errno (ENOMEM);
+          return false;
+        }
+    }
+
+  size_t new_size;
+  if (INT_MULTIPLY_WRAPV (new_allocated, element_size, &new_size))
+    return false;
+  void *new_array;
+  if (list->array == scratch)
+    {
+      /* The previous array was not heap-allocated.  */
+      new_array = malloc (new_size);
+      if (new_array != NULL && list->array != NULL)
+        memcpy (new_array, list->array, list->used * element_size);
+    }
+  else
+    new_array = realloc (list->array, new_size);
+  if (new_array == NULL)
+    return false;
+  list->array = new_array;
+  list->allocated = new_allocated;
+  return true;
+}
+libc_hidden_def (__libc_dynarray_emplace_enlarge)
diff --git a/xcompile/malloc/dynarray_finalize.c 
b/xcompile/malloc/dynarray_finalize.c
new file mode 100644
index 0000000000..673595a5fa
--- /dev/null
+++ b/xcompile/malloc/dynarray_finalize.c
@@ -0,0 +1,66 @@
+/* Copy the dynamically-allocated area to an explicitly-sized heap allocation.
+   Copyright (C) 2017-2023 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library 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.
+
+   The GNU C Library 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 the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#ifndef _LIBC
+# include <libc-config.h>
+#endif
+
+#include <dynarray.h>
+#include <stdlib.h>
+#include <string.h>
+
+bool
+__libc_dynarray_finalize (struct dynarray_header *list,
+                          void *scratch, size_t element_size,
+                          struct dynarray_finalize_result *result)
+{
+  if (__dynarray_error (list))
+    /* The caller will reported the deferred error.  */
+    return false;
+
+  size_t used = list->used;
+
+  /* Empty list.  */
+  if (used == 0)
+    {
+      /* An empty list could still be backed by a heap-allocated
+         array.  Free it if necessary.  */
+      if (list->array != scratch)
+        free (list->array);
+      *result = (struct dynarray_finalize_result) { NULL, 0 };
+      return true;
+    }
+
+  size_t allocation_size = used * element_size;
+  void *heap_array = malloc (allocation_size);
+  if (heap_array != NULL)
+    {
+      /* The new array takes ownership of the strings.  */
+      if (list->array != NULL)
+        memcpy (heap_array, list->array, allocation_size);
+      if (list->array != scratch)
+        free (list->array);
+      *result = (struct dynarray_finalize_result)
+        { .array = heap_array, .length = used };
+      return true;
+    }
+  else
+    /* The caller will perform the freeing operation.  */
+    return false;
+}
+libc_hidden_def (__libc_dynarray_finalize)
diff --git a/xcompile/malloc/dynarray_resize.c 
b/xcompile/malloc/dynarray_resize.c
new file mode 100644
index 0000000000..7ecd4de63b
--- /dev/null
+++ b/xcompile/malloc/dynarray_resize.c
@@ -0,0 +1,68 @@
+/* Increase the size of a dynamic array.
+   Copyright (C) 2017-2023 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library 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.
+
+   The GNU C Library 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 the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#ifndef _LIBC
+# include <libc-config.h>
+#endif
+
+#include <dynarray.h>
+#include <errno.h>
+#include <intprops.h>
+#include <stdlib.h>
+#include <string.h>
+
+bool
+__libc_dynarray_resize (struct dynarray_header *list, size_t size,
+                        void *scratch, size_t element_size)
+{
+  /* The existing allocation provides sufficient room.  */
+  if (size <= list->allocated)
+    {
+      list->used = size;
+      return true;
+    }
+
+  /* Otherwise, use size as the new allocation size.  The caller is
+     expected to provide the final size of the array, so there is no
+     over-allocation here.  */
+
+  size_t new_size_bytes;
+  if (INT_MULTIPLY_WRAPV (size, element_size, &new_size_bytes))
+    {
+      /* Overflow.  */
+      __set_errno (ENOMEM);
+      return false;
+    }
+  void *new_array;
+  if (list->array == scratch)
+    {
+      /* The previous array was not heap-allocated.  */
+      new_array = malloc (new_size_bytes);
+      if (new_array != NULL && list->array != NULL)
+        memcpy (new_array, list->array, list->used * element_size);
+    }
+  else
+    new_array = realloc (list->array, new_size_bytes);
+  if (new_array == NULL)
+    return false;
+  list->array = new_array;
+  list->allocated = size;
+  list->used = size;
+  return true;
+}
+libc_hidden_def (__libc_dynarray_resize)
diff --git a/xcompile/malloc/dynarray_resize_clear.c 
b/xcompile/malloc/dynarray_resize_clear.c
new file mode 100644
index 0000000000..bb23c522a1
--- /dev/null
+++ b/xcompile/malloc/dynarray_resize_clear.c
@@ -0,0 +1,39 @@
+/* Increase the size of a dynamic array and clear the new part.
+   Copyright (C) 2017-2023 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library 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.
+
+   The GNU C Library 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 the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#ifndef _LIBC
+# include <libc-config.h>
+#endif
+
+#include <dynarray.h>
+#include <string.h>
+
+bool
+__libc_dynarray_resize_clear (struct dynarray_header *list, size_t size,
+                              void *scratch, size_t element_size)
+{
+  size_t old_size = list->used;
+  if (!__libc_dynarray_resize (list, size, scratch, element_size))
+    return false;
+  /* __libc_dynarray_resize already checked for overflow.  */
+  char *array = list->array;
+  memset (array + (old_size * element_size), 0,
+          (size - old_size) * element_size);
+  return true;
+}
+libc_hidden_def (__libc_dynarray_resize_clear)
diff --git a/xcompile/malloc/scratch_buffer.h b/xcompile/malloc/scratch_buffer.h
new file mode 100644
index 0000000000..33fd2b29cd
--- /dev/null
+++ b/xcompile/malloc/scratch_buffer.h
@@ -0,0 +1,135 @@
+/* Variable-sized buffer with on-stack default allocation.
+   Copyright (C) 2015-2023 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library 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.
+
+   The GNU C Library 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 the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#ifndef _SCRATCH_BUFFER_H
+#define _SCRATCH_BUFFER_H
+
+/* Scratch buffers with a default stack allocation and fallback to
+   heap allocation.  It is expected that this function is used in this
+   way:
+
+     struct scratch_buffer tmpbuf;
+     scratch_buffer_init (&tmpbuf);
+
+     while (!function_that_uses_buffer (tmpbuf.data, tmpbuf.length))
+       if (!scratch_buffer_grow (&tmpbuf))
+        return -1;
+
+     scratch_buffer_free (&tmpbuf);
+     return 0;
+
+   The allocation functions (scratch_buffer_grow,
+   scratch_buffer_grow_preserve, scratch_buffer_set_array_size) make
+   sure that the heap allocation, if any, is freed, so that the code
+   above does not have a memory leak.  The buffer still remains in a
+   state that can be deallocated using scratch_buffer_free, so a loop
+   like this is valid as well:
+
+     struct scratch_buffer tmpbuf;
+     scratch_buffer_init (&tmpbuf);
+
+     while (!function_that_uses_buffer (tmpbuf.data, tmpbuf.length))
+       if (!scratch_buffer_grow (&tmpbuf))
+        break;
+
+     scratch_buffer_free (&tmpbuf);
+
+   scratch_buffer_grow and scratch_buffer_grow_preserve are guaranteed
+   to grow the buffer by at least 512 bytes.  This means that when
+   using the scratch buffer as a backing store for a non-character
+   array whose element size, in bytes, is 512 or smaller, the scratch
+   buffer only has to grow once to make room for at least one more
+   element.
+*/
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdlib.h>
+
+/* Scratch buffer.  Must be initialized with scratch_buffer_init
+   before its use.  */
+struct scratch_buffer {
+  void *data;    /* Pointer to the beginning of the scratch area.  */
+  size_t length; /* Allocated space at the data pointer, in bytes.  */
+  union { max_align_t __align; char __c[1024]; } __space;
+};
+
+/* Initializes *BUFFER so that BUFFER->data points to BUFFER->__space
+   and BUFFER->length reflects the available space.  */
+static inline void
+scratch_buffer_init (struct scratch_buffer *buffer)
+{
+  buffer->data = buffer->__space.__c;
+  buffer->length = sizeof (buffer->__space);
+}
+
+/* Deallocates *BUFFER (if it was heap-allocated).  */
+static inline void
+scratch_buffer_free (struct scratch_buffer *buffer)
+{
+  if (buffer->data != buffer->__space.__c)
+    free (buffer->data);
+}
+
+/* Grow *BUFFER by some arbitrary amount.  The buffer contents is NOT
+   preserved.  Return true on success, false on allocation failure (in
+   which case the old buffer is freed).  On success, the new buffer is
+   larger than the previous size.  On failure, *BUFFER is deallocated,
+   but remains in a free-able state, and errno is set.  */
+bool __libc_scratch_buffer_grow (struct scratch_buffer *buffer);
+libc_hidden_proto (__libc_scratch_buffer_grow)
+
+/* Alias for __libc_scratch_buffer_grow.  */
+static __always_inline bool
+scratch_buffer_grow (struct scratch_buffer *buffer)
+{
+  return __glibc_likely (__libc_scratch_buffer_grow (buffer));
+}
+
+/* Like __libc_scratch_buffer_grow, but preserve the old buffer
+   contents on success, as a prefix of the new buffer.  */
+bool __libc_scratch_buffer_grow_preserve (struct scratch_buffer *buffer);
+libc_hidden_proto (__libc_scratch_buffer_grow_preserve)
+
+/* Alias for __libc_scratch_buffer_grow_preserve.  */
+static __always_inline bool
+scratch_buffer_grow_preserve (struct scratch_buffer *buffer)
+{
+  return __glibc_likely (__libc_scratch_buffer_grow_preserve (buffer));
+}
+
+/* Grow *BUFFER so that it can store at least NELEM elements of SIZE
+   bytes.  The buffer contents are NOT preserved.  Both NELEM and SIZE
+   can be zero.  Return true on success, false on allocation failure
+   (in which case the old buffer is freed, but *BUFFER remains in a
+   free-able state, and errno is set).  It is unspecified whether this
+   function can reduce the array size.  */
+bool __libc_scratch_buffer_set_array_size (struct scratch_buffer *buffer,
+                                          size_t nelem, size_t size);
+libc_hidden_proto (__libc_scratch_buffer_set_array_size)
+
+/* Alias for __libc_scratch_set_array_size.  */
+static __always_inline bool
+scratch_buffer_set_array_size (struct scratch_buffer *buffer,
+                              size_t nelem, size_t size)
+{
+  return __glibc_likely (__libc_scratch_buffer_set_array_size
+                        (buffer, nelem, size));
+}
+
+#endif /* _SCRATCH_BUFFER_H */
diff --git a/xcompile/malloc/scratch_buffer_dupfree.c 
b/xcompile/malloc/scratch_buffer_dupfree.c
new file mode 100644
index 0000000000..2f60fbb54e
--- /dev/null
+++ b/xcompile/malloc/scratch_buffer_dupfree.c
@@ -0,0 +1,41 @@
+/* Variable-sized buffer with on-stack default allocation.
+   Copyright (C) 2020-2023 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library 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.
+
+   The GNU C Library 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 the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#ifndef _LIBC
+# include <libc-config.h>
+#endif
+
+#include <scratch_buffer.h>
+#include <string.h>
+
+void *
+__libc_scratch_buffer_dupfree (struct scratch_buffer *buffer, size_t size)
+{
+  void *data = buffer->data;
+  if (data == buffer->__space.__c)
+    {
+      void *copy = malloc (size);
+      return copy != NULL ? memcpy (copy, data, size) : NULL;
+    }
+  else
+    {
+      void *copy = realloc (data, size);
+      return copy != NULL ? copy : data;
+    }
+}
+libc_hidden_def (__libc_scratch_buffer_dupfree)
diff --git a/xcompile/malloc/scratch_buffer_grow.c 
b/xcompile/malloc/scratch_buffer_grow.c
new file mode 100644
index 0000000000..a5e8f2f723
--- /dev/null
+++ b/xcompile/malloc/scratch_buffer_grow.c
@@ -0,0 +1,56 @@
+/* Variable-sized buffer with on-stack default allocation.
+   Copyright (C) 2015-2023 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library 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.
+
+   The GNU C Library 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 the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#ifndef _LIBC
+# include <libc-config.h>
+#endif
+
+#include <scratch_buffer.h>
+#include <errno.h>
+
+bool
+__libc_scratch_buffer_grow (struct scratch_buffer *buffer)
+{
+  void *new_ptr;
+  size_t new_length = buffer->length * 2;
+
+  /* Discard old buffer.  */
+  scratch_buffer_free (buffer);
+
+  /* Check for overflow.  */
+  if (__glibc_likely (new_length >= buffer->length))
+    new_ptr = malloc (new_length);
+  else
+    {
+      __set_errno (ENOMEM);
+      new_ptr = NULL;
+    }
+
+  if (__glibc_unlikely (new_ptr == NULL))
+    {
+      /* Buffer must remain valid to free.  */
+      scratch_buffer_init (buffer);
+      return false;
+    }
+
+  /* Install new heap-based buffer.  */
+  buffer->data = new_ptr;
+  buffer->length = new_length;
+  return true;
+}
+libc_hidden_def (__libc_scratch_buffer_grow)
diff --git a/xcompile/malloc/scratch_buffer_grow_preserve.c 
b/xcompile/malloc/scratch_buffer_grow_preserve.c
new file mode 100644
index 0000000000..c0b5d87b7e
--- /dev/null
+++ b/xcompile/malloc/scratch_buffer_grow_preserve.c
@@ -0,0 +1,67 @@
+/* Variable-sized buffer with on-stack default allocation.
+   Copyright (C) 2015-2023 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library 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.
+
+   The GNU C Library 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 the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#ifndef _LIBC
+# include <libc-config.h>
+#endif
+
+#include <scratch_buffer.h>
+#include <errno.h>
+#include <string.h>
+
+bool
+__libc_scratch_buffer_grow_preserve (struct scratch_buffer *buffer)
+{
+  size_t new_length = 2 * buffer->length;
+  void *new_ptr;
+
+  if (buffer->data == buffer->__space.__c)
+    {
+      /* Move buffer to the heap.  No overflow is possible because
+        buffer->length describes a small buffer on the stack.  */
+      new_ptr = malloc (new_length);
+      if (new_ptr == NULL)
+       return false;
+      memcpy (new_ptr, buffer->__space.__c, buffer->length);
+    }
+  else
+    {
+      /* Buffer was already on the heap.  Check for overflow.  */
+      if (__glibc_likely (new_length >= buffer->length))
+       new_ptr = realloc (buffer->data, new_length);
+      else
+       {
+         __set_errno (ENOMEM);
+         new_ptr = NULL;
+       }
+
+      if (__glibc_unlikely (new_ptr == NULL))
+       {
+         /* Deallocate, but buffer must remain valid to free.  */
+         free (buffer->data);
+         scratch_buffer_init (buffer);
+         return false;
+       }
+    }
+
+  /* Install new heap-based buffer.  */
+  buffer->data = new_ptr;
+  buffer->length = new_length;
+  return true;
+}
+libc_hidden_def (__libc_scratch_buffer_grow_preserve)
diff --git a/xcompile/malloc/scratch_buffer_set_array_size.c 
b/xcompile/malloc/scratch_buffer_set_array_size.c
new file mode 100644
index 0000000000..24c39350ad
--- /dev/null
+++ b/xcompile/malloc/scratch_buffer_set_array_size.c
@@ -0,0 +1,64 @@
+/* Variable-sized buffer with on-stack default allocation.
+   Copyright (C) 2015-2023 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library 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.
+
+   The GNU C Library 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 the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#ifndef _LIBC
+# include <libc-config.h>
+#endif
+
+#include <scratch_buffer.h>
+#include <errno.h>
+#include <limits.h>
+
+bool
+__libc_scratch_buffer_set_array_size (struct scratch_buffer *buffer,
+                                     size_t nelem, size_t size)
+{
+  size_t new_length = nelem * size;
+
+  /* Avoid overflow check if both values are small. */
+  if ((nelem | size) >> (sizeof (size_t) * CHAR_BIT / 2) != 0
+      && nelem != 0 && size != new_length / nelem)
+    {
+      /* Overflow.  Discard the old buffer, but it must remain valid
+        to free.  */
+      scratch_buffer_free (buffer);
+      scratch_buffer_init (buffer);
+      __set_errno (ENOMEM);
+      return false;
+    }
+
+  if (new_length <= buffer->length)
+    return true;
+
+  /* Discard old buffer.  */
+  scratch_buffer_free (buffer);
+
+  char *new_ptr = malloc (new_length);
+  if (new_ptr == NULL)
+    {
+      /* Buffer must remain valid to free.  */
+      scratch_buffer_init (buffer);
+      return false;
+    }
+
+  /* Install new heap-based buffer.  */
+  buffer->data = new_ptr;
+  buffer->length = new_length;
+  return true;
+}
+libc_hidden_def (__libc_scratch_buffer_set_array_size)



reply via email to

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